├── .gitignore ├── LICENSE.md ├── README.md ├── compile-release-all-netf.sh ├── ship ├── VRCRouter 1.0.2 (2023-07-12).zip ├── VRCRouter 1.0.2 (2023-07-12) │ ├── Newtonsoft.Json.dll │ ├── USAGE.txt │ ├── VRCRouter Configuration.exe │ ├── VRCRouter.exe │ ├── manifest.vrmanifest │ ├── openvr_api.dll │ ├── route presets │ │ ├── README.txt │ │ └── VRC Face Tracking 5.0.json │ ├── vrcrouter_common.dll │ └── vrcrouter_native.dll ├── VRCRouter 1.0.3 (2024-12-12).zip ├── VRCRouter 1.0.3 (2024-12-12) │ ├── CHANGELOG.txt │ ├── Newtonsoft.Json.dll │ ├── USAGE.txt │ ├── VRCRouter Configuration.exe │ ├── VRCRouter.exe │ ├── manifest.vrmanifest │ ├── openvr_api.dll │ ├── route presets │ │ ├── README.txt │ │ ├── VRC Face Tracking 5.1.1.0.json │ │ └── VRC Face Tracking 5.2.3.0.json │ ├── vrcrouter.json │ ├── vrcrouter_common.dll │ └── vrcrouter_native.dll └── staging │ ├── CHANGELOG.txt │ ├── Newtonsoft.Json.dll │ ├── USAGE.txt │ ├── VRCRouter Configuration.exe │ ├── VRCRouter.exe │ ├── manifest.vrmanifest │ ├── openvr_api.dll │ ├── route presets │ ├── README.txt │ ├── VRC Face Tracking 5.1.1.0.json │ └── VRC Face Tracking 5.2.3.0.json │ ├── vrcrouter.json │ ├── vrcrouter_common.dll │ └── vrcrouter_native.dll ├── vrcrouter-native ├── README.txt ├── compile_native_release.sh └── native.cpp └── vrcrouter-netf ├── README.md ├── vrcrouter-common ├── Build.cs ├── Properties │ └── AssemblyInfo.cs ├── Stuff.cs ├── app.config ├── native.cs ├── openvr_api.cs ├── packages.config └── vrcrouter-common.csproj ├── vrcrouter-config ├── About.Designer.cs ├── About.cs ├── About.resx ├── App.config ├── Main.Designer.cs ├── Main.cs ├── Main.resx ├── NewRouteForm.Designer.cs ├── NewRouteForm.cs ├── NewRouteForm.resx ├── Program.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── RenameRouteForm.Designer.cs ├── RenameRouteForm.cs ├── RenameRouteForm.resx ├── icon.ico ├── packages.config └── vrcrouter-config.csproj ├── vrcrouter-ui ├── App.config ├── Form1.Designer.cs ├── Form1.cs ├── Form1.resx ├── Program.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── RouteControl.Designer.cs ├── RouteControl.cs ├── RouteControl.resx └── vrcrouter-ui.csproj ├── vrcrouter.sln └── vrcrouter ├── App.config ├── Program.cs ├── Properties ├── AssemblyInfo.cs └── app.manifest ├── icon.ico └── vrcrouter.csproj /.gitignore: -------------------------------------------------------------------------------- 1 | vrcrouter-native/*.dll 2 | vrcrouter-native/*.exp 3 | vrcrouter-native/*.lib 4 | 5 | ## Ignore Visual Studio temporary files, build results, and 6 | ## files generated by popular Visual Studio add-ons. 7 | ## 8 | ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore 9 | 10 | # User-specific files 11 | *.rsuser 12 | *.suo 13 | *.user 14 | *.userosscache 15 | *.sln.docstates 16 | 17 | # User-specific files (MonoDevelop/Xamarin Studio) 18 | *.userprefs 19 | 20 | # Mono auto generated files 21 | mono_crash.* 22 | 23 | # Build results 24 | [Dd]ebug/ 25 | [Dd]ebugPublic/ 26 | [Rr]elease/ 27 | [Rr]eleases/ 28 | x64/ 29 | x86/ 30 | [Ww][Ii][Nn]32/ 31 | [Aa][Rr][Mm]/ 32 | [Aa][Rr][Mm]64/ 33 | bld/ 34 | [Bb]in/ 35 | [Oo]bj/ 36 | [Ll]og/ 37 | [Ll]ogs/ 38 | 39 | # Visual Studio 2015/2017 cache/options directory 40 | .vs/ 41 | # Uncomment if you have tasks that create the project's static files in wwwroot 42 | #wwwroot/ 43 | 44 | # Visual Studio 2017 auto generated files 45 | Generated\ Files/ 46 | 47 | # MSTest test Results 48 | [Tt]est[Rr]esult*/ 49 | [Bb]uild[Ll]og.* 50 | 51 | # NUnit 52 | *.VisualState.xml 53 | TestResult.xml 54 | nunit-*.xml 55 | 56 | # Build Results of an ATL Project 57 | [Dd]ebugPS/ 58 | [Rr]eleasePS/ 59 | dlldata.c 60 | 61 | # Benchmark Results 62 | BenchmarkDotNet.Artifacts/ 63 | 64 | # .NET Core 65 | project.lock.json 66 | project.fragment.lock.json 67 | artifacts/ 68 | 69 | # ASP.NET Scaffolding 70 | ScaffoldingReadMe.txt 71 | 72 | # StyleCop 73 | StyleCopReport.xml 74 | 75 | # Files built by Visual Studio 76 | *_i.c 77 | *_p.c 78 | *_h.h 79 | *.ilk 80 | *.meta 81 | *.obj 82 | *.iobj 83 | *.pch 84 | *.pdb 85 | *.ipdb 86 | *.pgc 87 | *.pgd 88 | *.rsp 89 | *.sbr 90 | *.tlb 91 | *.tli 92 | *.tlh 93 | *.tmp 94 | *.tmp_proj 95 | *_wpftmp.csproj 96 | *.log 97 | *.tlog 98 | *.vspscc 99 | *.vssscc 100 | .builds 101 | *.pidb 102 | *.svclog 103 | *.scc 104 | 105 | # Chutzpah Test files 106 | _Chutzpah* 107 | 108 | # Visual C++ cache files 109 | ipch/ 110 | *.aps 111 | *.ncb 112 | *.opendb 113 | *.opensdf 114 | *.sdf 115 | *.cachefile 116 | *.VC.db 117 | *.VC.VC.opendb 118 | 119 | # Visual Studio profiler 120 | *.psess 121 | *.vsp 122 | *.vspx 123 | *.sap 124 | 125 | # Visual Studio Trace Files 126 | *.e2e 127 | 128 | # TFS 2012 Local Workspace 129 | $tf/ 130 | 131 | # Guidance Automation Toolkit 132 | *.gpState 133 | 134 | # ReSharper is a .NET coding add-in 135 | _ReSharper*/ 136 | *.[Rr]e[Ss]harper 137 | *.DotSettings.user 138 | 139 | # TeamCity is a build add-in 140 | _TeamCity* 141 | 142 | # DotCover is a Code Coverage Tool 143 | *.dotCover 144 | 145 | # AxoCover is a Code Coverage Tool 146 | .axoCover/* 147 | !.axoCover/settings.json 148 | 149 | # Coverlet is a free, cross platform Code Coverage Tool 150 | coverage*.json 151 | coverage*.xml 152 | coverage*.info 153 | 154 | # Visual Studio code coverage results 155 | *.coverage 156 | *.coveragexml 157 | 158 | # NCrunch 159 | _NCrunch_* 160 | .*crunch*.local.xml 161 | nCrunchTemp_* 162 | 163 | # MightyMoose 164 | *.mm.* 165 | AutoTest.Net/ 166 | 167 | # Web workbench (sass) 168 | .sass-cache/ 169 | 170 | # Installshield output folder 171 | [Ee]xpress/ 172 | 173 | # DocProject is a documentation generator add-in 174 | DocProject/buildhelp/ 175 | DocProject/Help/*.HxT 176 | DocProject/Help/*.HxC 177 | DocProject/Help/*.hhc 178 | DocProject/Help/*.hhk 179 | DocProject/Help/*.hhp 180 | DocProject/Help/Html2 181 | DocProject/Help/html 182 | 183 | # Click-Once directory 184 | publish/ 185 | 186 | # Publish Web Output 187 | *.[Pp]ublish.xml 188 | *.azurePubxml 189 | # Note: Comment the next line if you want to checkin your web deploy settings, 190 | # but database connection strings (with potential passwords) will be unencrypted 191 | *.pubxml 192 | *.publishproj 193 | 194 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 195 | # checkin your Azure Web App publish settings, but sensitive information contained 196 | # in these scripts will be unencrypted 197 | PublishScripts/ 198 | 199 | # NuGet Packages 200 | *.nupkg 201 | # NuGet Symbol Packages 202 | *.snupkg 203 | # The packages folder can be ignored because of Package Restore 204 | **/[Pp]ackages/* 205 | # except build/, which is used as an MSBuild target. 206 | !**/[Pp]ackages/build/ 207 | # Uncomment if necessary however generally it will be regenerated when needed 208 | #!**/[Pp]ackages/repositories.config 209 | # NuGet v3's project.json files produces more ignorable files 210 | *.nuget.props 211 | *.nuget.targets 212 | 213 | # Microsoft Azure Build Output 214 | csx/ 215 | *.build.csdef 216 | 217 | # Microsoft Azure Emulator 218 | ecf/ 219 | rcf/ 220 | 221 | # Windows Store app package directories and files 222 | AppPackages/ 223 | BundleArtifacts/ 224 | Package.StoreAssociation.xml 225 | _pkginfo.txt 226 | *.appx 227 | *.appxbundle 228 | *.appxupload 229 | 230 | # Visual Studio cache files 231 | # files ending in .cache can be ignored 232 | *.[Cc]ache 233 | # but keep track of directories ending in .cache 234 | !?*.[Cc]ache/ 235 | 236 | # Others 237 | ClientBin/ 238 | ~$* 239 | *~ 240 | *.dbmdl 241 | *.dbproj.schemaview 242 | *.jfm 243 | *.pfx 244 | *.publishsettings 245 | orleans.codegen.cs 246 | 247 | # Including strong name files can present a security risk 248 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 249 | #*.snk 250 | 251 | # Since there are multiple workflows, uncomment next line to ignore bower_components 252 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 253 | #bower_components/ 254 | 255 | # RIA/Silverlight projects 256 | Generated_Code/ 257 | 258 | # Backup & report files from converting an old project file 259 | # to a newer Visual Studio version. Backup files are not needed, 260 | # because we have git ;-) 261 | _UpgradeReport_Files/ 262 | Backup*/ 263 | UpgradeLog*.XML 264 | UpgradeLog*.htm 265 | ServiceFabricBackup/ 266 | *.rptproj.bak 267 | 268 | # SQL Server files 269 | *.mdf 270 | *.ldf 271 | *.ndf 272 | 273 | # Business Intelligence projects 274 | *.rdl.data 275 | *.bim.layout 276 | *.bim_*.settings 277 | *.rptproj.rsuser 278 | *- [Bb]ackup.rdl 279 | *- [Bb]ackup ([0-9]).rdl 280 | *- [Bb]ackup ([0-9][0-9]).rdl 281 | 282 | # Microsoft Fakes 283 | FakesAssemblies/ 284 | 285 | # GhostDoc plugin setting file 286 | *.GhostDoc.xml 287 | 288 | # Node.js Tools for Visual Studio 289 | .ntvs_analysis.dat 290 | node_modules/ 291 | 292 | # Visual Studio 6 build log 293 | *.plg 294 | 295 | # Visual Studio 6 workspace options file 296 | *.opt 297 | 298 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 299 | *.vbw 300 | 301 | # Visual Studio 6 auto-generated project file (contains which files were open etc.) 302 | *.vbp 303 | 304 | # Visual Studio 6 workspace and project file (working project files containing files to include in project) 305 | *.dsw 306 | *.dsp 307 | 308 | # Visual Studio 6 technical files 309 | *.ncb 310 | *.aps 311 | 312 | # Visual Studio LightSwitch build output 313 | **/*.HTMLClient/GeneratedArtifacts 314 | **/*.DesktopClient/GeneratedArtifacts 315 | **/*.DesktopClient/ModelManifest.xml 316 | **/*.Server/GeneratedArtifacts 317 | **/*.Server/ModelManifest.xml 318 | _Pvt_Extensions 319 | 320 | # Paket dependency manager 321 | .paket/paket.exe 322 | paket-files/ 323 | 324 | # FAKE - F# Make 325 | .fake/ 326 | 327 | # CodeRush personal settings 328 | .cr/personal 329 | 330 | # Python Tools for Visual Studio (PTVS) 331 | __pycache__/ 332 | *.pyc 333 | 334 | # Cake - Uncomment if you are using it 335 | # tools/** 336 | # !tools/packages.config 337 | 338 | # Tabs Studio 339 | *.tss 340 | 341 | # Telerik's JustMock configuration file 342 | *.jmconfig 343 | 344 | # BizTalk build output 345 | *.btp.cs 346 | *.btm.cs 347 | *.odx.cs 348 | *.xsd.cs 349 | 350 | # OpenCover UI analysis results 351 | OpenCover/ 352 | 353 | # Azure Stream Analytics local run output 354 | ASALocalRun/ 355 | 356 | # MSBuild Binary and Structured Log 357 | *.binlog 358 | 359 | # NVidia Nsight GPU debugger configuration file 360 | *.nvuser 361 | 362 | # MFractors (Xamarin productivity tool) working folder 363 | .mfractor/ 364 | 365 | # Local History for Visual Studio 366 | .localhistory/ 367 | 368 | # Visual Studio History (VSHistory) files 369 | .vshistory/ 370 | 371 | # BeatPulse healthcheck temp database 372 | healthchecksdb 373 | 374 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 375 | MigrationBackup/ 376 | 377 | # Ionide (cross platform F# VS Code tools) working folder 378 | .ionide/ 379 | 380 | # Fody - auto-generated XML schema 381 | FodyWeavers.xsd 382 | 383 | # VS Code files for those working on multiple tools 384 | .vscode/* 385 | !.vscode/settings.json 386 | !.vscode/tasks.json 387 | !.vscode/launch.json 388 | !.vscode/extensions.json 389 | *.code-workspace 390 | 391 | # Local History for Visual Studio Code 392 | .history/ 393 | 394 | # Windows Installer files from build outputs 395 | *.cab 396 | *.msi 397 | *.msix 398 | *.msm 399 | *.msp 400 | 401 | # JetBrains Rider 402 | *.sln.iml 403 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2023 ValueFactory https://value.gay 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 4 | associated documentation files (the “Software”), to deal in the Software without restriction, 5 | including without limitation the rights to use, copy, modify, merge, publish, distribute, 6 | sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 7 | furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial 10 | portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 13 | NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 14 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES 15 | OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 16 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VRCRouter 2 | Lightweight and configurable OSC router for VRChat PCVR. 3 | 4 | ## Features 5 | * Configuration interface. 6 | * Low CPU & RAM footprint. 7 | * Start with SteamVR. 8 | * OSC Message routing from VRChat to specified address:port. 9 | * Application autostarting and autoclosing. 10 | 11 | ## Download 12 | [Download from the Releases page](https://github.com/valuef/VRCRouter/releases/) 13 | 14 | ## Usage Tutorial 15 | https://www.youtube.com/watch?v=8abMvrzguo4 16 | 17 | ## Issues/Merges 18 | I don't check GitHub so poke me on [other socials](https://shader.gay) in case you've opened a ticket. 19 | 20 | ## Building release 21 | Run the compile-release-all-netf.sh script on cygwin. 22 | Msbuild path is hard-coded into the script. 23 | Clang must be accessible on the PATH. 24 | 25 | Final binaries will be placed in staging/ship/ 26 | 27 | For a proper release, the final package should contain the following files/folders: 28 | * manifest.vrmanifest 29 | * openvr_api.dll 30 | * USAGE.txt 31 | * route presets 32 | -------------------------------------------------------------------------------- /compile-release-all-netf.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | VERSION="1.0.3" 4 | DATE=$(date "+%Y-%m-%d %H:%M:%S") 5 | 6 | echo $VERSION 7 | echo $DATE 8 | 9 | cat > "vrcrouter-netf/vrcrouter-common/Build.cs" <<- EOM 10 | // AUTOGENERATED FILE DO NOT EDIT 11 | namespace ValueFactoryVRCRouterCommon { 12 | public static class Build { 13 | #if DEBUG 14 | public static string build_version = "DEBUG"; 15 | public static string build_date = "DEBUG"; 16 | #else 17 | public static string build_version = "${VERSION}"; 18 | public static string build_date = "${DATE}"; 19 | #endif 20 | } 21 | } 22 | EOM 23 | 24 | 25 | echo == Compiling VRCRouter 26 | /cygdrive/c/Program\ Files/Microsoft\ Visual\ Studio/2022/Community/Msbuild/Current/Bin/MSBuild.exe -p:Configuration=Release vrcrouter-netf/vrcrouter/vrcrouter.csproj 27 | 28 | echo == Compiling VRCRouter Configuration 29 | /cygdrive/c/Program\ Files/Microsoft\ Visual\ Studio/2022/Community/Msbuild/Current/Bin/MSBuild.exe -p:Configuration=Release vrcrouter-netf/vrcrouter-config/vrcrouter-config.csproj 30 | 31 | echo == Compiling VRCRouter Common 32 | /cygdrive/c/Program\ Files/Microsoft\ Visual\ Studio/2022/Community/Msbuild/Current/Bin/MSBuild.exe -p:Configuration=Release vrcrouter-netf/vrcrouter-common/vrcrouter-common.csproj 33 | 34 | echo == Compiling VRCRouter Native 35 | cd "vrcrouter-native" 36 | ./compile_native_release.sh 37 | cd ../ 38 | 39 | echo == Copying Stuff 40 | 41 | mkdir -p "ship/staging/" 42 | 43 | cp "vrcrouter-netf/vrcrouter/bin/Release/VRCRouter.exe" "ship/staging/" 44 | cp "vrcrouter-netf/vrcrouter/bin/Release/Newtonsoft.Json.dll" "ship/staging/" 45 | 46 | cp "vrcrouter-netf/vrcrouter-config/bin/Release/VRCRouter Configuration.exe" "ship/staging/" 47 | 48 | cp "vrcrouter-netf/vrcrouter-common/bin/Release/vrcrouter_common.dll" "ship/staging/" 49 | cp "vrcrouter-native/vrcrouter_native.dll" "ship/staging/" 50 | 51 | echo DONE 52 | -------------------------------------------------------------------------------- /ship/VRCRouter 1.0.2 (2023-07-12).zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valuef/VRCRouter/4aab0bd8c374e8f883d0a46b844b9c4f6d11519e/ship/VRCRouter 1.0.2 (2023-07-12).zip -------------------------------------------------------------------------------- /ship/VRCRouter 1.0.2 (2023-07-12)/Newtonsoft.Json.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valuef/VRCRouter/4aab0bd8c374e8f883d0a46b844b9c4f6d11519e/ship/VRCRouter 1.0.2 (2023-07-12)/Newtonsoft.Json.dll -------------------------------------------------------------------------------- /ship/VRCRouter 1.0.2 (2023-07-12)/USAGE.txt: -------------------------------------------------------------------------------- 1 | All of the configuration of the VRCRouter is done through the configuration app. 2 | 3 | ----------------------------------------------------------------------------------------------- 4 | --- Requirements (look here if launching the executables does nothing or spews out errors!) --- 5 | ----------------------------------------------------------------------------------------------- 6 | * MSVC 2022 Redistributables 7 | https://aka.ms/vs/17/release/vc_redist.x64.exe 8 | 9 | * .NET Framework 4.8.1 Runtime 10 | https://dotnet.microsoft.com/en-us/download/dotnet-framework/net481 11 | 12 | * OSC Enabled in VRChat: 13 | Radial Menu -> Optons -> OSC -> Enabled 14 | 15 | I also recommend running each of the executables, making sure that your antivirus isn't stopping 16 | them from running and disabling the popup that asks if you're sure you want to launch this 17 | application as all of these may intefere with the VRCRouter autostart functionality. 18 | 19 | -------------------------- 20 | --- SteamVR Autolaunch --- 21 | -------------------------- 22 | VRCRouter is able to automatically launch when SteamVR is launched. Once you launch the VRCRouter 23 | configuration for the first time, you'll be asked if you want to set this up. 24 | 25 | If you want to enable/disable SteamVR autolaunch, you can do it at any time by choosing one of the 26 | Enable/Disable options in the SteamVR section. 27 | 28 | ---------------- 29 | -- OSC Input --- 30 | ---------------- 31 | The OSC input receive address and port determine where the OSC data will be coming from. For VRChat, 32 | this is going to be 127.0.0.1:9001. You usually do not want to change this unless you have an odd 33 | set up and know what you're doing! 34 | 35 | --------------------- 36 | -- Wait for VRChat -- 37 | --------------------- 38 | This option will wait for the 'VRChat.exe' process to start before launching any of the autostart 39 | apps and doing routing. 40 | 41 | -------------------------- 42 | --- Setting up a Route --- 43 | -------------------------- 44 | To create a new route: 45 | * Click the 'New Route' button at the bottom of the configuration app. 46 | * Select a name that hasn't already been taken by another route and click create. 47 | * Configure the output port to match the port that your routed app will be using. 48 | If that address:port is already in use by another app, a warning will appear. 49 | 50 | * You may optionally set up an app to automatically start in the 'Autostart App' section. 51 | I recommend placing the app in a subdirectory of the VRCRouter folder, preferrably in the routes 52 | folder, clicking 'Browse' and finding the executable app. This way, the path to the executable 53 | will be relative and you'll be able to move the VRCRouter folder around your file system easily 54 | without breaking things. 55 | 56 | You may also specify launch arguments to launch the app with. We have some special formatting 57 | that you can use: 58 | Any occurance of {output-address} will be replaced with the output address of the route. 59 | Any occurange of {output-port} will be replaced wit hthe output address of the route. 60 | 61 | Example: 62 | A route with output address "127.0.0.1", an output port of "9020" and autostart arguments of 63 | "--osc=9000:{output-address}:{output-port}" will have the arguments formatted into: 64 | "--osc=9000:127.0.0.1:9020" 65 | 66 | We will try to automatically close the autostarted app and any children processes once the 67 | router closes. This may not always succeed however. If it doesn't please try to set up a manual 68 | autoclose using the 'Autoclose App' section. 69 | 70 | * You may optionally specify an executable name to close when the router closes in the 'Autoclose 71 | App' section. 72 | You only need to specify the filename of the executable. For example: "VRCFaceTracking.exe" 73 | 74 | Any apps that have that executable name, and their children processes, will be closed when the 75 | router is closed. The app doesn't have to have been started using the autostart. 76 | 77 | Beware that you do not necessarily need to use this to close autostarted apps. This is only 78 | intended to close apps that work a bit oddly, such as the VRC Face Tracking 5.0 app. 79 | 80 | A route may be enabled or disabled. Disabled routes do not do routing, do not launch apps and do not 81 | close apps. They are completely dormant and do nothing. 82 | 83 | Disabling OSC routing will disable sending OSC data to the selected address and port. 84 | 85 | You can leave the exectuable path of the autostart app empty and no app will be started. 86 | 87 | You can leave the exectuable path of the autoclose app empty and no app will be closed. 88 | 89 | --------------------- 90 | --- Example Route --- 91 | --------------------- 92 | Here are the steps I take to set up a route for the VF boop counter (https://boop.shader.gay) 93 | 1. Create a new route called 'Boop Counter' 94 | 2. Set the output port to 9002 95 | 3. Move the 'Boop Counter' folder into the 'VRCRouter/routes' folder 96 | 4. Click 'Browse' on the Executable Path field of the route. 97 | 5. Select the 'Boop Counter.exe' executable. The 'Executable Path' field now looks like this: 98 | routes\Boop Counter\Boop Counter.exe 99 | 6. Put the following into the Launch Arguments field: 100 | --osc_receive={output-address}:{output-port} 101 | 102 | The boop counter is designed to understand these arguments, they may not work for other OSC apps! 103 | 104 | That's it! You may test the autolaunch by clicking the 'Launch' button. If everything is working 105 | fine, the boop counter will start. 106 | 107 | ------------------------ 108 | --- Using the router --- 109 | ------------------------ 110 | 111 | You may manually launch the VRCRouter executable, or if you have SteamVR autostart enabled, it will 112 | launch by itself once SteamVR starts. The router will automatically close once SteamVR is exiting 113 | and will close any autostarted and autoclose apps with it. 114 | 115 | --------------- 116 | -- In VRChat -- 117 | --------------- 118 | 119 | Once in VRChat, you'll want to enable OSC. Open your radial menu and find: 120 | Optons -> OSC -> Enabled 121 | 122 | You may need to restart VRChat for data to properly be sent from OSC apps to VRChat. 123 | 124 | ----------------- 125 | --- Tutorials --- 126 | ----------------- 127 | 128 | https://www.youtube.com/watch?v=1v8vxQEpF8w - Router Setup 129 | https://www.youtube.com/watch?v=kyXLpmeJouQ - Full Setup (Boop Counter) 130 | 131 | ------------------------------------------ 132 | by ValueFactory https://value.gay 133 | Initially for https://boop.shader.gay 134 | -------------------------------------------------------------------------------- /ship/VRCRouter 1.0.2 (2023-07-12)/VRCRouter Configuration.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valuef/VRCRouter/4aab0bd8c374e8f883d0a46b844b9c4f6d11519e/ship/VRCRouter 1.0.2 (2023-07-12)/VRCRouter Configuration.exe -------------------------------------------------------------------------------- /ship/VRCRouter 1.0.2 (2023-07-12)/VRCRouter.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valuef/VRCRouter/4aab0bd8c374e8f883d0a46b844b9c4f6d11519e/ship/VRCRouter 1.0.2 (2023-07-12)/VRCRouter.exe -------------------------------------------------------------------------------- /ship/VRCRouter 1.0.2 (2023-07-12)/manifest.vrmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "source" : "builtin", 3 | 4 | "applications": [{ 5 | "app_key": "valuefactory.lvrcoscr", 6 | "launch_type": "binary", 7 | "binary_path_windows": "VRCRouter.exe", 8 | "is_dashboard_overlay": true, 9 | 10 | "strings": { 11 | "en_us": { 12 | "name": "VRCRouter", 13 | "description": "A Light VRC OSC Router." 14 | } 15 | } 16 | }] 17 | } 18 | -------------------------------------------------------------------------------- /ship/VRCRouter 1.0.2 (2023-07-12)/openvr_api.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valuef/VRCRouter/4aab0bd8c374e8f883d0a46b844b9c4f6d11519e/ship/VRCRouter 1.0.2 (2023-07-12)/openvr_api.dll -------------------------------------------------------------------------------- /ship/VRCRouter 1.0.2 (2023-07-12)/route presets/README.txt: -------------------------------------------------------------------------------- 1 | These routes are preconfigured to use with some apps! 2 | 3 | Before using them, you'll need to make sure that the output address and ports match the ones on the 4 | app. 5 | 6 | For VRCFacetracking 5.0 you will need to configure the output address and port yourself. You can do 7 | that by launching VRCFT, looking for the cogwheel button at the bottom left and setting the receive 8 | port and receive ip address to match the output address and output port in the router configuration. 9 | The send port should be set to 9000. 10 | -------------------------------------------------------------------------------- /ship/VRCRouter 1.0.2 (2023-07-12)/route presets/VRC Face Tracking 5.0.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "enabled": true, 4 | "routing_enabled": true, 5 | "output-address": "127.0.0.1", 6 | "output-port": 9023, 7 | "autostart-path": "C:\\windows\\explorer.exe", 8 | "autostart-args": "shell:appsFolder\\96ba052f-0948-44d8-86c4-a0212e4ae047_d7rcq4vxghz0r!App", 9 | "autoclose-executable-name": "VRCFaceTracking.exe" 10 | } -------------------------------------------------------------------------------- /ship/VRCRouter 1.0.2 (2023-07-12)/vrcrouter_common.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valuef/VRCRouter/4aab0bd8c374e8f883d0a46b844b9c4f6d11519e/ship/VRCRouter 1.0.2 (2023-07-12)/vrcrouter_common.dll -------------------------------------------------------------------------------- /ship/VRCRouter 1.0.2 (2023-07-12)/vrcrouter_native.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valuef/VRCRouter/4aab0bd8c374e8f883d0a46b844b9c4f6d11519e/ship/VRCRouter 1.0.2 (2023-07-12)/vrcrouter_native.dll -------------------------------------------------------------------------------- /ship/VRCRouter 1.0.3 (2024-12-12).zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valuef/VRCRouter/4aab0bd8c374e8f883d0a46b844b9c4f6d11519e/ship/VRCRouter 1.0.3 (2024-12-12).zip -------------------------------------------------------------------------------- /ship/VRCRouter 1.0.3 (2024-12-12)/CHANGELOG.txt: -------------------------------------------------------------------------------- 1 | ## Version 1.0.3 (2024-04-12) 2 | * Added a route preset for VRCFaceTracking 5.2.3.0 3 | * Updated the notes for VRCFaceTracking preset usage. 4 | 5 | -------------------------------------------------------------------------------- /ship/VRCRouter 1.0.3 (2024-12-12)/Newtonsoft.Json.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valuef/VRCRouter/4aab0bd8c374e8f883d0a46b844b9c4f6d11519e/ship/VRCRouter 1.0.3 (2024-12-12)/Newtonsoft.Json.dll -------------------------------------------------------------------------------- /ship/VRCRouter 1.0.3 (2024-12-12)/USAGE.txt: -------------------------------------------------------------------------------- 1 | All of the configuration of the VRCRouter is done through the configuration app. 2 | 3 | ----------------------------------------------------------------------------------------------- 4 | --- Requirements (look here if launching the executables does nothing or spews out errors!) --- 5 | ----------------------------------------------------------------------------------------------- 6 | * MSVC 2022 Redistributables 7 | https://aka.ms/vs/17/release/vc_redist.x64.exe 8 | 9 | * .NET Framework 4.8.1 Runtime 10 | https://dotnet.microsoft.com/en-us/download/dotnet-framework/net481 11 | 12 | * OSC Enabled in VRChat: 13 | Radial Menu -> Optons -> OSC -> Enabled 14 | 15 | I also recommend running each of the executables, making sure that your antivirus isn't stopping 16 | them from running and disabling the popup that asks if you're sure you want to launch this 17 | application as all of these may intefere with the VRCRouter autostart functionality. 18 | 19 | -------------------------- 20 | --- SteamVR Autolaunch --- 21 | -------------------------- 22 | VRCRouter is able to automatically launch when SteamVR is launched. Once you launch the VRCRouter 23 | configuration for the first time, you'll be asked if you want to set this up. 24 | 25 | If you want to enable/disable SteamVR autolaunch, you can do it at any time by choosing one of the 26 | Enable/Disable options in the SteamVR section. 27 | 28 | ---------------- 29 | -- OSC Input --- 30 | ---------------- 31 | The OSC input receive address and port determine where the OSC data will be coming from. For VRChat, 32 | this is going to be 127.0.0.1:9001. You usually do not want to change this unless you have an odd 33 | set up and know what you're doing! 34 | 35 | --------------------- 36 | -- Wait for VRChat -- 37 | --------------------- 38 | This option will wait for the 'VRChat.exe' process to start before launching any of the autostart 39 | apps and doing routing. 40 | 41 | -------------------------- 42 | --- Setting up a Route --- 43 | -------------------------- 44 | To create a new route: 45 | * Click the 'New Route' button at the bottom of the configuration app. 46 | * Select a name that hasn't already been taken by another route and click create. 47 | * Configure the output port to match the port that your routed app will be using. 48 | If that address:port is already in use by another app, a warning will appear. 49 | 50 | * You may optionally set up an app to automatically start in the 'Autostart App' section. 51 | I recommend placing the app in a subdirectory of the VRCRouter folder, preferrably in the routes 52 | folder, clicking 'Browse' and finding the executable app. This way, the path to the executable 53 | will be relative and you'll be able to move the VRCRouter folder around your file system easily 54 | without breaking things. 55 | 56 | You may also specify launch arguments to launch the app with. We have some special formatting 57 | that you can use: 58 | Any occurance of {output-address} will be replaced with the output address of the route. 59 | Any occurange of {output-port} will be replaced wit hthe output address of the route. 60 | 61 | Example: 62 | A route with output address "127.0.0.1", an output port of "9020" and autostart arguments of 63 | "--osc=9000:{output-address}:{output-port}" will have the arguments formatted into: 64 | "--osc=9000:127.0.0.1:9020" 65 | 66 | We will try to automatically close the autostarted app and any children processes once the 67 | router closes. This may not always succeed however. If it doesn't please try to set up a manual 68 | autoclose using the 'Autoclose App' section. 69 | 70 | * You may optionally specify an executable name to close when the router closes in the 'Autoclose 71 | App' section. 72 | You only need to specify the filename of the executable. For example: "VRCFaceTracking.exe" 73 | 74 | Any apps that have that executable name, and their children processes, will be closed when the 75 | router is closed. The app doesn't have to have been started using the autostart. 76 | 77 | Beware that you do not necessarily need to use this to close autostarted apps. This is only 78 | intended to close apps that work a bit oddly, such as the VRC Face Tracking 5.0 app. 79 | 80 | A route may be enabled or disabled. Disabled routes do not do routing, do not launch apps and do not 81 | close apps. They are completely dormant and do nothing. 82 | 83 | Disabling OSC routing will disable sending OSC data to the selected address and port. 84 | 85 | You can leave the exectuable path of the autostart app empty and no app will be started. 86 | 87 | You can leave the exectuable path of the autoclose app empty and no app will be closed. 88 | 89 | --------------------- 90 | --- Example Route --- 91 | --------------------- 92 | Here are the steps I take to set up a route for the VF boop counter (https://boop.shader.gay) 93 | 1. Create a new route called 'Boop Counter' 94 | 2. Set the output port to 9002 95 | 3. Move the 'Boop Counter' folder into the 'VRCRouter/routes' folder 96 | 4. Click 'Browse' on the Executable Path field of the route. 97 | 5. Select the 'Boop Counter.exe' executable. The 'Executable Path' field now looks like this: 98 | routes\Boop Counter\Boop Counter.exe 99 | 6. Put the following into the Launch Arguments field: 100 | --osc_receive={output-address}:{output-port} 101 | 102 | The boop counter is designed to understand these arguments, they may not work for other OSC apps! 103 | 104 | That's it! You may test the autolaunch by clicking the 'Launch' button. If everything is working 105 | fine, the boop counter will start. 106 | 107 | ------------------------ 108 | --- Using the router --- 109 | ------------------------ 110 | 111 | You may manually launch the VRCRouter executable, or if you have SteamVR autostart enabled, it will 112 | launch by itself once SteamVR starts. The router will automatically close once SteamVR is exiting 113 | and will close any autostarted and autoclose apps with it. 114 | 115 | --------------- 116 | -- In VRChat -- 117 | --------------- 118 | 119 | Once in VRChat, you'll want to enable OSC. Open your radial menu and find: 120 | Optons -> OSC -> Enabled 121 | 122 | You may need to restart VRChat for data to properly be sent from OSC apps to VRChat. 123 | 124 | ----------------- 125 | --- Tutorials --- 126 | ----------------- 127 | 128 | https://www.youtube.com/watch?v=1v8vxQEpF8w - Router Setup 129 | https://www.youtube.com/watch?v=kyXLpmeJouQ - Full Setup (Boop Counter) 130 | 131 | ------------------------------------------ 132 | by ValueFactory https://value.gay 133 | Initially for https://boop.shader.gay 134 | -------------------------------------------------------------------------------- /ship/VRCRouter 1.0.3 (2024-12-12)/VRCRouter Configuration.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valuef/VRCRouter/4aab0bd8c374e8f883d0a46b844b9c4f6d11519e/ship/VRCRouter 1.0.3 (2024-12-12)/VRCRouter Configuration.exe -------------------------------------------------------------------------------- /ship/VRCRouter 1.0.3 (2024-12-12)/VRCRouter.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valuef/VRCRouter/4aab0bd8c374e8f883d0a46b844b9c4f6d11519e/ship/VRCRouter 1.0.3 (2024-12-12)/VRCRouter.exe -------------------------------------------------------------------------------- /ship/VRCRouter 1.0.3 (2024-12-12)/manifest.vrmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "source" : "builtin", 3 | 4 | "applications": [{ 5 | "app_key": "valuefactory.lvrcoscr", 6 | "launch_type": "binary", 7 | "binary_path_windows": "VRCRouter.exe", 8 | "is_dashboard_overlay": true, 9 | 10 | "strings": { 11 | "en_us": { 12 | "name": "VRCRouter", 13 | "description": "A Light VRC OSC Router." 14 | } 15 | } 16 | }] 17 | } 18 | -------------------------------------------------------------------------------- /ship/VRCRouter 1.0.3 (2024-12-12)/openvr_api.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valuef/VRCRouter/4aab0bd8c374e8f883d0a46b844b9c4f6d11519e/ship/VRCRouter 1.0.3 (2024-12-12)/openvr_api.dll -------------------------------------------------------------------------------- /ship/VRCRouter 1.0.3 (2024-12-12)/route presets/README.txt: -------------------------------------------------------------------------------- 1 | These routes are preconfigured to use with some apps! 2 | 3 | Before using them, you'll need to make sure that the output address and ports match the ones on the 4 | app. 5 | 6 | For VRCFaceTracking 5.0 you will need to configure the output address and port yourself. You can do 7 | that by launching VRCFT, looking for the cogwheel button at the bottom left and setting the receive 8 | port and receive ip address to match the output address and output port in the router configuration. 9 | The send port should be set to 9000. 10 | 11 | You'll need to use a different route depending on your VRCFaceTracking version. Make sure to use the 12 | one that matches your version (you can find your VRCFT version in the settings page of the VRCFT 13 | app) or else the auto-start will not work! 14 | 15 | Only use one of these routes at a time! 16 | 17 | For VRCFT 5.2.3.0 and above, you can configure VRCFT to auto start with SteamVR in the settings tab 18 | of the VRCFT app. Keep in mind you have to have SteamVR running and your HMD connected to have the 19 | option be available. 20 | -------------------------------------------------------------------------------- /ship/VRCRouter 1.0.3 (2024-12-12)/route presets/VRC Face Tracking 5.1.1.0.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "enabled": true, 4 | "routing_enabled": true, 5 | "output-address": "127.0.0.1", 6 | "output-port": 9023, 7 | "autostart-path": "C:\\windows\\explorer.exe", 8 | "autostart-args": "shell:appsFolder\\96ba052f-0948-44d8-86c4-a0212e4ae047_d7rcq4vxghz0r!App", 9 | "autoclose-executable-name": "VRCFaceTracking.exe" 10 | } 11 | -------------------------------------------------------------------------------- /ship/VRCRouter 1.0.3 (2024-12-12)/route presets/VRC Face Tracking 5.2.3.0.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "enabled": true, 4 | "routing_enabled": true, 5 | "output-address": "127.0.0.1", 6 | "output-port": 9023, 7 | "autostart-path": "C:\\windows\\explorer.exe", 8 | "autostart-args": "shell:appsFolder\\96ba052f-0948-44d8-86c4-a0212e4ae047_4s4k90pjvq32p!App", 9 | 10 | "autoclose-executable-name": "VRCFaceTracking.exe" 11 | } 12 | -------------------------------------------------------------------------------- /ship/VRCRouter 1.0.3 (2024-12-12)/vrcrouter.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "receive_address": "127.0.0.1", 4 | "receive_port": 9001, 5 | "wait_for_vrchat": true, 6 | "config_first_launch": false 7 | } -------------------------------------------------------------------------------- /ship/VRCRouter 1.0.3 (2024-12-12)/vrcrouter_common.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valuef/VRCRouter/4aab0bd8c374e8f883d0a46b844b9c4f6d11519e/ship/VRCRouter 1.0.3 (2024-12-12)/vrcrouter_common.dll -------------------------------------------------------------------------------- /ship/VRCRouter 1.0.3 (2024-12-12)/vrcrouter_native.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valuef/VRCRouter/4aab0bd8c374e8f883d0a46b844b9c4f6d11519e/ship/VRCRouter 1.0.3 (2024-12-12)/vrcrouter_native.dll -------------------------------------------------------------------------------- /ship/staging/CHANGELOG.txt: -------------------------------------------------------------------------------- 1 | ## Version 1.0.3 (2024-04-12) 2 | * Added a route preset for VRCFaceTracking 5.2.3.0 3 | * Updated the notes for VRCFaceTracking preset usage. 4 | 5 | -------------------------------------------------------------------------------- /ship/staging/Newtonsoft.Json.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valuef/VRCRouter/4aab0bd8c374e8f883d0a46b844b9c4f6d11519e/ship/staging/Newtonsoft.Json.dll -------------------------------------------------------------------------------- /ship/staging/USAGE.txt: -------------------------------------------------------------------------------- 1 | All of the configuration of the VRCRouter is done through the configuration app. 2 | 3 | ----------------------------------------------------------------------------------------------- 4 | --- Requirements (look here if launching the executables does nothing or spews out errors!) --- 5 | ----------------------------------------------------------------------------------------------- 6 | * MSVC 2022 Redistributables 7 | https://aka.ms/vs/17/release/vc_redist.x64.exe 8 | 9 | * .NET Framework 4.8.1 Runtime 10 | https://dotnet.microsoft.com/en-us/download/dotnet-framework/net481 11 | 12 | * OSC Enabled in VRChat: 13 | Radial Menu -> Optons -> OSC -> Enabled 14 | 15 | I also recommend running each of the executables, making sure that your antivirus isn't stopping 16 | them from running and disabling the popup that asks if you're sure you want to launch this 17 | application as all of these may intefere with the VRCRouter autostart functionality. 18 | 19 | -------------------------- 20 | --- SteamVR Autolaunch --- 21 | -------------------------- 22 | VRCRouter is able to automatically launch when SteamVR is launched. Once you launch the VRCRouter 23 | configuration for the first time, you'll be asked if you want to set this up. 24 | 25 | If you want to enable/disable SteamVR autolaunch, you can do it at any time by choosing one of the 26 | Enable/Disable options in the SteamVR section. 27 | 28 | ---------------- 29 | -- OSC Input --- 30 | ---------------- 31 | The OSC input receive address and port determine where the OSC data will be coming from. For VRChat, 32 | this is going to be 127.0.0.1:9001. You usually do not want to change this unless you have an odd 33 | set up and know what you're doing! 34 | 35 | --------------------- 36 | -- Wait for VRChat -- 37 | --------------------- 38 | This option will wait for the 'VRChat.exe' process to start before launching any of the autostart 39 | apps and doing routing. 40 | 41 | -------------------------- 42 | --- Setting up a Route --- 43 | -------------------------- 44 | To create a new route: 45 | * Click the 'New Route' button at the bottom of the configuration app. 46 | * Select a name that hasn't already been taken by another route and click create. 47 | * Configure the output port to match the port that your routed app will be using. 48 | If that address:port is already in use by another app, a warning will appear. 49 | 50 | * You may optionally set up an app to automatically start in the 'Autostart App' section. 51 | I recommend placing the app in a subdirectory of the VRCRouter folder, preferrably in the routes 52 | folder, clicking 'Browse' and finding the executable app. This way, the path to the executable 53 | will be relative and you'll be able to move the VRCRouter folder around your file system easily 54 | without breaking things. 55 | 56 | You may also specify launch arguments to launch the app with. We have some special formatting 57 | that you can use: 58 | Any occurance of {output-address} will be replaced with the output address of the route. 59 | Any occurange of {output-port} will be replaced wit hthe output address of the route. 60 | 61 | Example: 62 | A route with output address "127.0.0.1", an output port of "9020" and autostart arguments of 63 | "--osc=9000:{output-address}:{output-port}" will have the arguments formatted into: 64 | "--osc=9000:127.0.0.1:9020" 65 | 66 | We will try to automatically close the autostarted app and any children processes once the 67 | router closes. This may not always succeed however. If it doesn't please try to set up a manual 68 | autoclose using the 'Autoclose App' section. 69 | 70 | * You may optionally specify an executable name to close when the router closes in the 'Autoclose 71 | App' section. 72 | You only need to specify the filename of the executable. For example: "VRCFaceTracking.exe" 73 | 74 | Any apps that have that executable name, and their children processes, will be closed when the 75 | router is closed. The app doesn't have to have been started using the autostart. 76 | 77 | Beware that you do not necessarily need to use this to close autostarted apps. This is only 78 | intended to close apps that work a bit oddly, such as the VRC Face Tracking 5.0 app. 79 | 80 | A route may be enabled or disabled. Disabled routes do not do routing, do not launch apps and do not 81 | close apps. They are completely dormant and do nothing. 82 | 83 | Disabling OSC routing will disable sending OSC data to the selected address and port. 84 | 85 | You can leave the exectuable path of the autostart app empty and no app will be started. 86 | 87 | You can leave the exectuable path of the autoclose app empty and no app will be closed. 88 | 89 | --------------------- 90 | --- Example Route --- 91 | --------------------- 92 | Here are the steps I take to set up a route for the VF boop counter (https://boop.shader.gay) 93 | 1. Create a new route called 'Boop Counter' 94 | 2. Set the output port to 9002 95 | 3. Move the 'Boop Counter' folder into the 'VRCRouter/routes' folder 96 | 4. Click 'Browse' on the Executable Path field of the route. 97 | 5. Select the 'Boop Counter.exe' executable. The 'Executable Path' field now looks like this: 98 | routes\Boop Counter\Boop Counter.exe 99 | 6. Put the following into the Launch Arguments field: 100 | --osc_receive={output-address}:{output-port} 101 | 102 | The boop counter is designed to understand these arguments, they may not work for other OSC apps! 103 | 104 | That's it! You may test the autolaunch by clicking the 'Launch' button. If everything is working 105 | fine, the boop counter will start. 106 | 107 | ------------------------ 108 | --- Using the router --- 109 | ------------------------ 110 | 111 | You may manually launch the VRCRouter executable, or if you have SteamVR autostart enabled, it will 112 | launch by itself once SteamVR starts. The router will automatically close once SteamVR is exiting 113 | and will close any autostarted and autoclose apps with it. 114 | 115 | --------------- 116 | -- In VRChat -- 117 | --------------- 118 | 119 | Once in VRChat, you'll want to enable OSC. Open your radial menu and find: 120 | Optons -> OSC -> Enabled 121 | 122 | You may need to restart VRChat for data to properly be sent from OSC apps to VRChat. 123 | 124 | ----------------- 125 | --- Tutorials --- 126 | ----------------- 127 | 128 | https://www.youtube.com/watch?v=1v8vxQEpF8w - Router Setup 129 | https://www.youtube.com/watch?v=kyXLpmeJouQ - Full Setup (Boop Counter) 130 | 131 | ------------------------------------------ 132 | by ValueFactory https://value.gay 133 | Initially for https://boop.shader.gay 134 | -------------------------------------------------------------------------------- /ship/staging/VRCRouter Configuration.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valuef/VRCRouter/4aab0bd8c374e8f883d0a46b844b9c4f6d11519e/ship/staging/VRCRouter Configuration.exe -------------------------------------------------------------------------------- /ship/staging/VRCRouter.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valuef/VRCRouter/4aab0bd8c374e8f883d0a46b844b9c4f6d11519e/ship/staging/VRCRouter.exe -------------------------------------------------------------------------------- /ship/staging/manifest.vrmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "source" : "builtin", 3 | 4 | "applications": [{ 5 | "app_key": "valuefactory.lvrcoscr", 6 | "launch_type": "binary", 7 | "binary_path_windows": "VRCRouter.exe", 8 | "is_dashboard_overlay": true, 9 | 10 | "strings": { 11 | "en_us": { 12 | "name": "VRCRouter", 13 | "description": "A Light VRC OSC Router." 14 | } 15 | } 16 | }] 17 | } 18 | -------------------------------------------------------------------------------- /ship/staging/openvr_api.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valuef/VRCRouter/4aab0bd8c374e8f883d0a46b844b9c4f6d11519e/ship/staging/openvr_api.dll -------------------------------------------------------------------------------- /ship/staging/route presets/README.txt: -------------------------------------------------------------------------------- 1 | These routes are preconfigured to use with some apps! 2 | 3 | Before using them, you'll need to make sure that the output address and ports match the ones on the 4 | app. 5 | 6 | For VRCFaceTracking 5.0 you will need to configure the output address and port yourself. You can do 7 | that by launching VRCFT, looking for the cogwheel button at the bottom left and setting the receive 8 | port and receive ip address to match the output address and output port in the router configuration. 9 | The send port should be set to 9000. 10 | 11 | You'll need to use a different route depending on your VRCFaceTracking version. Make sure to use the 12 | one that matches your version (you can find your VRCFT version in the settings page of the VRCFT 13 | app) or else the auto-start will not work! 14 | 15 | Only use one of these routes at a time! 16 | 17 | For VRCFT 5.2.3.0 and above, you can configure VRCFT to auto start with SteamVR in the settings tab 18 | of the VRCFT app. Keep in mind you have to have SteamVR running and your HMD connected to have the 19 | option be available. 20 | -------------------------------------------------------------------------------- /ship/staging/route presets/VRC Face Tracking 5.1.1.0.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "enabled": true, 4 | "routing_enabled": true, 5 | "output-address": "127.0.0.1", 6 | "output-port": 9023, 7 | "autostart-path": "C:\\windows\\explorer.exe", 8 | "autostart-args": "shell:appsFolder\\96ba052f-0948-44d8-86c4-a0212e4ae047_d7rcq4vxghz0r!App", 9 | "autoclose-executable-name": "VRCFaceTracking.exe" 10 | } 11 | -------------------------------------------------------------------------------- /ship/staging/route presets/VRC Face Tracking 5.2.3.0.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "enabled": true, 4 | "routing_enabled": true, 5 | "output-address": "127.0.0.1", 6 | "output-port": 9023, 7 | "autostart-path": "C:\\windows\\explorer.exe", 8 | "autostart-args": "shell:appsFolder\\96ba052f-0948-44d8-86c4-a0212e4ae047_4s4k90pjvq32p!App", 9 | 10 | "autoclose-executable-name": "VRCFaceTracking.exe" 11 | } 12 | -------------------------------------------------------------------------------- /ship/staging/vrcrouter.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "receive_address": "127.0.0.1", 4 | "receive_port": 9001, 5 | "wait_for_vrchat": true, 6 | "config_first_launch": false 7 | } -------------------------------------------------------------------------------- /ship/staging/vrcrouter_common.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valuef/VRCRouter/4aab0bd8c374e8f883d0a46b844b9c4f6d11519e/ship/staging/vrcrouter_common.dll -------------------------------------------------------------------------------- /ship/staging/vrcrouter_native.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valuef/VRCRouter/4aab0bd8c374e8f883d0a46b844b9c4f6d11519e/ship/staging/vrcrouter_native.dll -------------------------------------------------------------------------------- /vrcrouter-native/README.txt: -------------------------------------------------------------------------------- 1 | Contains common native code that the router/configuration app will use. 2 | -------------------------------------------------------------------------------- /vrcrouter-native/compile_native_release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | clang native.cpp --shared -o vrcrouter_native.dll -O3 -Wno-undefined-internal --target=x86_64-pc-windows-msvc 4 | # cp vrcrouter_native.dll "../VRCRouter/bin/Debug/net6.0/vrcrouter_native.dll" 5 | # cp vrcrouter_native.dll "../VRCRouter/bin/Release/net6.0/vrcrouter_native.dll" 6 | # cp vrcrouter_native.dll "../vrcrouter-netf/vrcrouter-config/bin/Debug/" 7 | -------------------------------------------------------------------------------- /vrcrouter-native/native.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define COMBINE1(X,Y) X##Y 6 | #define CMB(X,Y) COMBINE1(X,Y) 7 | 8 | template 9 | struct DeferExitScope { 10 | T lambda; 11 | 12 | DeferExitScope(T l) : lambda(l) {} 13 | 14 | ~DeferExitScope() { lambda(); } 15 | DeferExitScope(const DeferExitScope &); 16 | 17 | operator bool() const { return true; } 18 | 19 | private: 20 | DeferExitScope& operator = (const DeferExitScope &); 21 | }; 22 | 23 | class DeferExitScopeHelp { 24 | public: 25 | template 26 | DeferExitScope operator+(T t){ return t;} 27 | }; 28 | 29 | #define defer const auto & CMB(defer__, __COUNTER__) = DeferExitScopeHelp() + [&]() 30 | 31 | extern "C" __declspec(dllexport) 32 | bool 33 | __cdecl 34 | terminate_app(DWORD process_id) { 35 | auto handle = OpenProcess(PROCESS_TERMINATE, FALSE, process_id); 36 | 37 | do { 38 | HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 39 | if(snapshot == INVALID_HANDLE_VALUE) { 40 | printf("\tFailed to get process snapshot. Error code is: %ld\n", GetLastError()); 41 | return false; 42 | } 43 | defer { CloseHandle(snapshot); }; 44 | 45 | PROCESSENTRY32 process_entry = {}; 46 | process_entry.dwSize = sizeof(process_entry); 47 | 48 | if(!Process32First(snapshot, &process_entry)) { 49 | printf("\tCall to Process32First failed. Error code is: %ld\n", GetLastError()); 50 | return false; 51 | } 52 | 53 | do { 54 | if(process_entry.th32ParentProcessID == process_id) { 55 | auto child_handle = OpenProcess(PROCESS_TERMINATE, FALSE, process_entry.th32ProcessID); 56 | if(child_handle == NULL) { 57 | printf("\tFailed to OpenProcess to terminate process '%s'. Error code is: %ld\n", process_entry.szExeFile, GetLastError()); 58 | } 59 | else { 60 | printf("\tClosing child process %s (pid %lu handle %p)\n", process_entry.szExeFile, process_entry.th32ProcessID, child_handle); 61 | TerminateProcess(child_handle, 0); 62 | CloseHandle(child_handle); 63 | } 64 | } 65 | } while(Process32Next(snapshot, &process_entry)); 66 | 67 | } while(false); 68 | 69 | TerminateProcess(handle, 0); 70 | CloseHandle(handle); 71 | 72 | return true; 73 | } 74 | 75 | struct Autostart_App_Result { 76 | DWORD id; 77 | DWORD error; 78 | bool success; 79 | }; 80 | 81 | extern "C" __declspec(dllexport) 82 | void 83 | __cdecl 84 | launch_autostart_app( 85 | wchar_t *path, 86 | wchar_t *args, 87 | wchar_t *working_dir, 88 | Autostart_App_Result *ret 89 | ) { 90 | STARTUPINFOW si = {}; 91 | si.cb = sizeof(si); 92 | PROCESS_INFORMATION pi = {}; 93 | 94 | auto result = CreateProcessW( 95 | path, args, 96 | 0, 97 | 0, 98 | false, 99 | CREATE_NEW_CONSOLE, 100 | 0, 101 | working_dir, 102 | &si, 103 | &pi 104 | ); 105 | 106 | *ret = {}; 107 | 108 | if(result) { 109 | ret->success = true; 110 | ret->id = pi.dwProcessId; 111 | 112 | CloseHandle(pi.hProcess); 113 | CloseHandle(pi.hThread); 114 | 115 | return; 116 | } 117 | 118 | ret->error = GetLastError(); 119 | ret->success = false; 120 | } 121 | 122 | 123 | extern "C" __declspec(dllexport) 124 | bool 125 | __cdecl 126 | wait_for_vrchat_to_start() { 127 | while(true) { 128 | HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 129 | if(snapshot == INVALID_HANDLE_VALUE) { 130 | //logt_error("vrchat", "Failed to get a list of all running processes. Not going to wait for VRChat...\n"); 131 | return false; 132 | } 133 | 134 | defer { CloseHandle(snapshot); }; 135 | 136 | PROCESSENTRY32 process_entry = {}; 137 | process_entry.dwSize = sizeof(process_entry); 138 | if(!Process32First(snapshot, &process_entry)) { 139 | //logt_error("vrchat", "Failed to retrieve information about the first process. Not going to wait for VRChat...\n"); 140 | return false; 141 | break; 142 | } 143 | 144 | do { 145 | auto *name = process_entry.szExeFile; 146 | auto *vrc_name = "VRChat.exe"; 147 | 148 | if(strcmp(name, vrc_name) == 0) { 149 | return true; 150 | } 151 | 152 | } while(Process32Next(snapshot, &process_entry)); 153 | 154 | Sleep(1000); 155 | } 156 | } 157 | 158 | 159 | extern "C" __declspec(dllexport) 160 | bool 161 | __cdecl 162 | terminate_app_by_name( 163 | wchar_t *search_for_name 164 | ) { 165 | HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 166 | if(snapshot == INVALID_HANDLE_VALUE) { 167 | //logt_error("vrchat", "Failed to get a list of all running processes. Not going to wait for VRChat...\n"); 168 | return false; 169 | } 170 | 171 | defer { CloseHandle(snapshot); }; 172 | 173 | PROCESSENTRY32W process_entry = {}; 174 | process_entry.dwSize = sizeof(process_entry); 175 | if(!Process32FirstW(snapshot, &process_entry)) { 176 | //logt_error("vrchat", "Failed to retrieve information about the first process. Not going to wait for VRChat...\n"); 177 | return false; 178 | } 179 | 180 | do { 181 | auto *got_name = process_entry.szExeFile; 182 | 183 | if(wcscmp(got_name, search_for_name) == 0) { 184 | return terminate_app(process_entry.th32ProcessID); 185 | } 186 | 187 | } while(Process32NextW(snapshot, &process_entry)); 188 | 189 | return false; 190 | } 191 | 192 | -------------------------------------------------------------------------------- /vrcrouter-netf/README.md: -------------------------------------------------------------------------------- 1 | Main .NET framework solution. 2 | 3 | ## vrcrouter 4 | The router. 5 | 6 | ## vrcrouter-config 7 | The winforms configuration app. Pretty much a front-end to the route json files with some QOL stuff 8 | to test functionality. 9 | 10 | ## vrcrouter-common 11 | Common shared code between router and configuration 12 | 13 | ## Building 14 | 15 | Build from Visual Studio. 16 | 17 | Both the router and the config will require the following files in the same folder for proper 18 | functionality: 19 | * manifest.vrmanifest 20 | * openvr_api.dll 21 | * vrcrouter_native.dll 22 | 23 | These files are not automatically copied on build. Please do so manually. 24 | -------------------------------------------------------------------------------- /vrcrouter-netf/vrcrouter-common/Build.cs: -------------------------------------------------------------------------------- 1 | // AUTOGENERATED FILE DO NOT EDIT 2 | namespace ValueFactoryVRCRouterCommon { 3 | public static class Build { 4 | #if DEBUG 5 | public static string build_version = "DEBUG"; 6 | public static string build_date = "DEBUG"; 7 | #else 8 | public static string build_version = "1.0.3"; 9 | public static string build_date = "2024-04-12 17:14:42"; 10 | #endif 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /vrcrouter-netf/vrcrouter-common/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("vrcrouter-common")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("vrcrouter-common")] 13 | [assembly: AssemblyCopyright("Copyright © 2023")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("627a06c1-0cd0-40a5-b3ae-1e86eb8ec9f6")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /vrcrouter-netf/vrcrouter-common/Stuff.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using System; 3 | using System.IO; 4 | using Newtonsoft.Json; 5 | using Newtonsoft.Json.Linq; 6 | 7 | namespace ValueFactoryVRCRouterCommon { 8 | 9 | 10 | public class Common { 11 | public struct Autostart_Data { 12 | public string full_path; 13 | public string args; 14 | public string working_dir; 15 | }; 16 | 17 | public static Autostart_Data prep_autostart_data(Route.V1 route, out string error) { 18 | var ret = new Autostart_Data(); 19 | ret.full_path = Path.GetFullPath(route.autostart_path); 20 | ret.working_dir = Path.GetDirectoryName(ret.full_path); 21 | 22 | ret.args = Route.format_args(route, out error); 23 | var module_name = Path.GetFileName(route.autostart_path); 24 | ret.args = $"{module_name} {ret.args}"; 25 | 26 | return ret; 27 | } 28 | } 29 | 30 | public class Config { 31 | public const int LATEST_VERSION = 1; 32 | public const string FILE = "vrcrouter.json"; 33 | 34 | public class V1 { 35 | public int version { get; set; } = 1; 36 | public string receive_address { get; set; } = "127.0.0.1"; 37 | public int receive_port { get; set; } = 9001; 38 | public bool wait_for_vrchat { get; set; } = true; 39 | public bool config_first_launch { get; set; } = true; 40 | } 41 | 42 | public static Config.V1 try_load_config(string path, out Exception out_ex, out string out_error) { 43 | string contents = null; 44 | try { 45 | contents = File.ReadAllText(path); 46 | } 47 | catch(Exception ex) { 48 | out_ex = ex; 49 | out_error = $"Failed to read the contents of the config at path '{path}'. Does the file exist and do we have permission to open it?"; 50 | return null; 51 | } 52 | 53 | JObject root = null; 54 | try { 55 | root = JObject.Parse(contents); 56 | } 57 | catch(Exception ex) { 58 | out_ex = ex; 59 | out_error = $"Failed to parse config file contents at path '{path}'."; 60 | return null; 61 | } 62 | 63 | if(root == null) { 64 | out_error = $"Parsed the config file at '{path}' but it ended up being empty."; 65 | out_ex = null; 66 | return null; 67 | } 68 | 69 | var version = root["version"]; 70 | if(version == null) { 71 | out_error = $"The config file at '{path}' does not have a 'version' property on it. It may be corrupted!"; 72 | out_ex = null; 73 | return null; 74 | } 75 | 76 | bool deserialize(out T config, out Exception out_out_ex, out string out_out_error) where T : class { 77 | config = null; 78 | try { 79 | config = root.ToObject(); 80 | } 81 | catch(Exception ex) { 82 | out_out_ex = ex; 83 | out_out_error = $"Failed to deserialize the config file at '{path}'."; 84 | return false; 85 | } 86 | 87 | if(config == null) { 88 | out_out_error = $"The deserialized config file at '{path}' turned out to be empty!"; 89 | out_out_ex = null; 90 | return false; 91 | } 92 | 93 | out_out_error = null; 94 | out_out_ex = null; 95 | return true; 96 | } 97 | 98 | if((int?)version == 1) { 99 | if(deserialize(out var cfg, out out_ex, out out_error)) { 100 | return cfg; 101 | } 102 | } 103 | 104 | out_error = $"The config file at '{path}' is an unsupprted version {version}. The maximum supported version right now is 1."; 105 | out_ex = null; 106 | return null; 107 | } 108 | } 109 | 110 | public class Route { 111 | public const int LATEST_VERSION = 1; 112 | public const string FOLDER = "routes"; 113 | 114 | 115 | public static bool ensure_route_directory_exists(out Exception out_ex, out string error) { 116 | try { 117 | Directory.CreateDirectory(Route.FOLDER); 118 | out_ex = null; 119 | error = null; 120 | return true; 121 | } 122 | catch(Exception ex) { 123 | out_ex = ex; 124 | error = $"An error occured while trying to create the routes folder at '{Route.FOLDER}'"; 125 | return false; 126 | } 127 | } 128 | 129 | public static string[] get_routes_folder_contents(out string err) { 130 | string[] files; 131 | try { 132 | if(!ensure_route_directory_exists(out var ex, out err)) { 133 | if(ex != null) { 134 | err = $"{err}\r\n\r\nException info: {ex}"; 135 | } 136 | return new string[] { }; 137 | } 138 | files = Directory.GetFiles(Route.FOLDER, "*.json"); 139 | } 140 | catch(Exception ex) { 141 | err = $"Failed to get the contents of the {Route.FOLDER} folder. No autostarting or routing will be done! Exception: {ex}"; 142 | return new string[] { }; 143 | } 144 | 145 | err = null; 146 | return files; 147 | } 148 | 149 | public static string make_path_to_route(string name) { 150 | return Path.Combine(Route.FOLDER, name + ".json"); 151 | } 152 | 153 | public class V1 { 154 | public int version { get; set; } = 1; 155 | 156 | [JsonIgnore] 157 | public string name { get; set; } = ""; 158 | public bool enabled { get; set; } = true; 159 | 160 | public bool routing_enabled { get; set; } = true; 161 | 162 | [JsonProperty("output-address")] 163 | public string output_address { get; set; } = "127.0.0.1"; 164 | 165 | [JsonProperty("output-port")] 166 | public int output_port { get; set; } = 9002; 167 | 168 | [JsonProperty("autostart-path")] 169 | public string autostart_path { get; set; } = ""; 170 | 171 | [JsonProperty("autostart-args")] 172 | public string autostart_args { get; set; } = ""; 173 | 174 | [JsonProperty("autoclose-executable-name")] 175 | public string autoclose_executable_name { get; set; } = ""; 176 | } 177 | 178 | public static bool try_save(Route.V1 route, out Exception out_ex, out string error) { 179 | route.version = Route.LATEST_VERSION; 180 | 181 | var path = Route.make_path_to_route(route.name); 182 | 183 | try { 184 | var text = JsonConvert.SerializeObject(route, Formatting.Indented); 185 | 186 | if(!ensure_route_directory_exists(out out_ex, out error)) { 187 | return false; 188 | } 189 | 190 | File.WriteAllText(path, text); 191 | out_ex = null; 192 | error = null; 193 | return true; 194 | } 195 | catch(Exception e) { 196 | out_ex = e; 197 | error = $"Failed to save the route to '{path}'."; 198 | return false; 199 | } 200 | } 201 | 202 | public static string format_args(Route.V1 route, out string error) { 203 | // "--osc_receive={output-address}:{output-port}" 204 | error = null; 205 | 206 | var parsing_format = false; 207 | 208 | var parse_start = 0; 209 | var parse_length = 0; 210 | 211 | var sb = new StringBuilder(); 212 | var has_parsed_formats = false; 213 | 214 | for(var char_index = 0; char_index < route.autostart_args.Length; char_index++) { 215 | var c = route.autostart_args[char_index]; 216 | 217 | if(parsing_format) { 218 | if(c == '}') { 219 | parsing_format = false; 220 | 221 | var format_parsed_string = route.autostart_args.Substring(parse_start, parse_length); 222 | 223 | if(format_parsed_string.Equals("output-address")) { 224 | sb.Append(route.output_address); 225 | has_parsed_formats = true; 226 | } 227 | else if(format_parsed_string.Equals("output-port")) { 228 | sb.Append(route.output_port); 229 | has_parsed_formats = true; 230 | } 231 | else { 232 | error = $"Unknown format string {format_parsed_string}. Available format strings: {{output-address}} and {{output-port}}"; 233 | return route.autostart_args; 234 | } 235 | } 236 | else { 237 | parse_length += 1; 238 | } 239 | } 240 | else { 241 | if(c == '{') { 242 | parse_start = char_index + 1; 243 | parse_length = 0; 244 | parsing_format = true; 245 | } 246 | else { 247 | sb.Append(c); 248 | } 249 | } 250 | } 251 | 252 | if(parsing_format) { 253 | error = "There is a format string that is missing a '}'!"; 254 | return route.autostart_args; 255 | } 256 | 257 | if(has_parsed_formats) { 258 | return sb.ToString(); 259 | } 260 | return route.autostart_args; 261 | } 262 | 263 | 264 | 265 | public static Route.V1 try_load_route(string path, out Exception out_ex, out string out_error) { 266 | string contents = null; 267 | try { 268 | contents = File.ReadAllText(path); 269 | } 270 | catch(Exception ex) { 271 | out_ex = ex; 272 | out_error = $"Failed to read the contents of the route at path '{path}'. Does the file exist and do we have permission to open it?"; 273 | return null; 274 | } 275 | 276 | JObject root = null; 277 | try { 278 | root = JObject.Parse(contents); 279 | } 280 | catch(Exception ex) { 281 | out_ex = ex; 282 | out_error = $"Failed to load route! Failed to parse the route file's contents at path '{path}'."; 283 | return null; 284 | } 285 | 286 | if(root == null) { 287 | out_error = $"Failed to load route! Parsed the route file at '{path}' but it ended up being empty."; 288 | out_ex = null; 289 | return null; 290 | } 291 | 292 | var version = root["version"]; 293 | if(version == null) { 294 | out_error = $"Failed to load route! The route file at '{path}' does not have a 'version' property on it. It may be corrupted!"; 295 | out_ex = null; 296 | return null; 297 | } 298 | 299 | bool deserialize(out T route, out Exception out_out_ex, out string out_out_error) where T : class { 300 | route = null; 301 | try { 302 | route = root.ToObject(); 303 | } 304 | catch(Exception ex) { 305 | out_out_ex = ex; 306 | out_out_error = $"Failed to load route! Failed to deserialize the route file at '{path}'."; 307 | return false; 308 | } 309 | 310 | if(route == null) { 311 | out_out_error = $"Failed to load route! The deserialized route file at '{path}' turned out to be empty!"; 312 | out_out_ex = null; 313 | return false; 314 | } 315 | 316 | out_out_ex = null; 317 | out_out_error = null; 318 | return true; 319 | } 320 | 321 | var route_name = Path.GetFileNameWithoutExtension(path); 322 | 323 | if((int?)version == 1) { 324 | if(deserialize(out var route, out out_ex, out out_error)) { 325 | route.name = route_name; 326 | return route; 327 | } 328 | } 329 | 330 | out_error = $"The route file at '{path}' is an unsupprted version {version}. The maximum supported version right now is 1."; 331 | out_ex = null; 332 | return null; 333 | } 334 | } 335 | } -------------------------------------------------------------------------------- /vrcrouter-netf/vrcrouter-common/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /vrcrouter-netf/vrcrouter-common/native.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using System.Text; 3 | 4 | namespace ValueFactoryVRCRouterCommon { 5 | public static class Native { 6 | 7 | [StructLayout(LayoutKind.Sequential)] 8 | public struct Autostart_App_Result { 9 | public uint id; 10 | public uint error; 11 | public bool success; 12 | }; 13 | 14 | [DllImport("vrcrouter_native.dll", EntryPoint = "terminate_app", CallingConvention = CallingConvention.Cdecl)] 15 | public static extern bool terminate_app(uint process_id); 16 | 17 | [DllImport("vrcrouter_native.dll", EntryPoint = "wait_for_vrchat_to_start", CallingConvention = CallingConvention.Cdecl)] 18 | public static extern bool wait_for_vrchat_to_start(); 19 | 20 | [DllImport("vrcrouter_native.dll", EntryPoint = "launch_autostart_app", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] 21 | // NOTE(valuef): The underlying win32 apis that launch_autostart_app uses may modify the strings, so we pass in mutable buffers. 22 | // Also .NET framework seems to have an issue with returning native structs, so we pass an out argument. 23 | // 2023-06-26 24 | public static extern void launch_autostart_app(StringBuilder path, StringBuilder args, StringBuilder working_dir, ref Autostart_App_Result ret); 25 | 26 | [DllImport("vrcrouter_native.dll", EntryPoint = "terminate_app_by_name", CallingConvention = CallingConvention.Cdecl)] 27 | public static extern bool terminate_app_by_name([MarshalAs(UnmanagedType.LPWStr)] string name); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /vrcrouter-netf/vrcrouter-common/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /vrcrouter-netf/vrcrouter-common/vrcrouter-common.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {627A06C1-0CD0-40A5-B3AE-1E86EB8EC9F6} 8 | Library 9 | Properties 10 | ValueFactoryVRCRouterCommon 11 | vrcrouter_common 12 | v4.8.1 13 | 512 14 | true 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | 35 | ..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /vrcrouter-netf/vrcrouter-config/About.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace ValueFactoryVRCRouterConfig { 2 | partial class About { 3 | /// 4 | /// Required designer variable. 5 | /// 6 | private System.ComponentModel.IContainer components = null; 7 | 8 | /// 9 | /// Clean up any resources being used. 10 | /// 11 | /// true if managed resources should be disposed; otherwise, false. 12 | protected override void Dispose(bool disposing) { 13 | if (disposing && (components != null)) { 14 | components.Dispose(); 15 | } 16 | base.Dispose(disposing); 17 | } 18 | 19 | #region Windows Form Designer generated code 20 | 21 | /// 22 | /// Required method for Designer support - do not modify 23 | /// the contents of this method with the code editor. 24 | /// 25 | private void InitializeComponent() { 26 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(About)); 27 | this.label6 = new System.Windows.Forms.Label(); 28 | this.version = new System.Windows.Forms.Label(); 29 | this.built = new System.Windows.Forms.Label(); 30 | this.label2 = new System.Windows.Forms.Label(); 31 | this.label4 = new System.Windows.Forms.Label(); 32 | this.linkLabel1 = new System.Windows.Forms.LinkLabel(); 33 | this.label3 = new System.Windows.Forms.Label(); 34 | this.label1 = new System.Windows.Forms.Label(); 35 | this.textBox1 = new System.Windows.Forms.TextBox(); 36 | this.linkLabel2 = new System.Windows.Forms.LinkLabel(); 37 | this.SuspendLayout(); 38 | // 39 | // label6 40 | // 41 | this.label6.AutoSize = true; 42 | this.label6.Location = new System.Drawing.Point(195, 9); 43 | this.label6.Name = "label6"; 44 | this.label6.Size = new System.Drawing.Size(267, 15); 45 | this.label6.TabIndex = 18; 46 | this.label6.Text = "Copyright (c) 2023 ValueFactory https://value.gay"; 47 | // 48 | // version 49 | // 50 | this.version.AutoSize = true; 51 | this.version.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 52 | this.version.Location = new System.Drawing.Point(10, 59); 53 | this.version.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); 54 | this.version.Name = "version"; 55 | this.version.Size = new System.Drawing.Size(45, 15); 56 | this.version.TabIndex = 15; 57 | this.version.Text = "Version"; 58 | // 59 | // built 60 | // 61 | this.built.AutoSize = true; 62 | this.built.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 63 | this.built.Location = new System.Drawing.Point(10, 74); 64 | this.built.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); 65 | this.built.Name = "built"; 66 | this.built.Size = new System.Drawing.Size(31, 15); 67 | this.built.TabIndex = 16; 68 | this.built.Text = "Built"; 69 | // 70 | // label2 71 | // 72 | this.label2.AutoSize = true; 73 | this.label2.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 74 | this.label2.Location = new System.Drawing.Point(10, 103); 75 | this.label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); 76 | this.label2.Name = "label2"; 77 | this.label2.Size = new System.Drawing.Size(90, 15); 78 | this.label2.TabIndex = 14; 79 | this.label2.Text = "With help from:"; 80 | // 81 | // label4 82 | // 83 | this.label4.AutoSize = true; 84 | this.label4.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 85 | this.label4.Location = new System.Drawing.Point(10, 29); 86 | this.label4.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); 87 | this.label4.Name = "label4"; 88 | this.label4.Size = new System.Drawing.Size(90, 15); 89 | this.label4.TabIndex = 13; 90 | this.label4.Text = "by ValueFactory"; 91 | // 92 | // linkLabel1 93 | // 94 | this.linkLabel1.AutoSize = true; 95 | this.linkLabel1.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 96 | this.linkLabel1.Location = new System.Drawing.Point(10, 44); 97 | this.linkLabel1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); 98 | this.linkLabel1.Name = "linkLabel1"; 99 | this.linkLabel1.Size = new System.Drawing.Size(109, 15); 100 | this.linkLabel1.TabIndex = 12; 101 | this.linkLabel1.TabStop = true; 102 | this.linkLabel1.Text = "https://shader.gay/"; 103 | this.linkLabel1.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel1_LinkClicked); 104 | // 105 | // label3 106 | // 107 | this.label3.AutoSize = true; 108 | this.label3.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 109 | this.label3.Location = new System.Drawing.Point(10, 118); 110 | this.label3.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); 111 | this.label3.Name = "label3"; 112 | this.label3.Size = new System.Drawing.Size(171, 180); 113 | this.label3.TabIndex = 11; 114 | this.label3.Text = resources.GetString("label3.Text"); 115 | // 116 | // label1 117 | // 118 | this.label1.AutoSize = true; 119 | this.label1.Font = new System.Drawing.Font("Segoe UI", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 120 | this.label1.Location = new System.Drawing.Point(9, 9); 121 | this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); 122 | this.label1.Name = "label1"; 123 | this.label1.Size = new System.Drawing.Size(80, 20); 124 | this.label1.TabIndex = 10; 125 | this.label1.Text = "VRCRouter"; 126 | // 127 | // textBox1 128 | // 129 | this.textBox1.Location = new System.Drawing.Point(198, 56); 130 | this.textBox1.Multiline = true; 131 | this.textBox1.Name = "textBox1"; 132 | this.textBox1.ReadOnly = true; 133 | this.textBox1.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; 134 | this.textBox1.Size = new System.Drawing.Size(290, 274); 135 | this.textBox1.TabIndex = 19; 136 | this.textBox1.Text = resources.GetString("textBox1.Text"); 137 | // 138 | // linkLabel2 139 | // 140 | this.linkLabel2.AutoSize = true; 141 | this.linkLabel2.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 142 | this.linkLabel2.Location = new System.Drawing.Point(195, 29); 143 | this.linkLabel2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); 144 | this.linkLabel2.Name = "linkLabel2"; 145 | this.linkLabel2.Size = new System.Drawing.Size(208, 15); 146 | this.linkLabel2.TabIndex = 20; 147 | this.linkLabel2.TabStop = true; 148 | this.linkLabel2.Text = "https://github.com/valuef/VRCRouter"; 149 | this.linkLabel2.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel2_LinkClicked); 150 | // 151 | // About 152 | // 153 | this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); 154 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 155 | this.ClientSize = new System.Drawing.Size(506, 344); 156 | this.Controls.Add(this.linkLabel2); 157 | this.Controls.Add(this.textBox1); 158 | this.Controls.Add(this.label6); 159 | this.Controls.Add(this.version); 160 | this.Controls.Add(this.built); 161 | this.Controls.Add(this.label2); 162 | this.Controls.Add(this.label4); 163 | this.Controls.Add(this.linkLabel1); 164 | this.Controls.Add(this.label3); 165 | this.Controls.Add(this.label1); 166 | this.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 167 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; 168 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); 169 | this.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); 170 | this.MaximizeBox = false; 171 | this.Name = "About"; 172 | this.Text = "About | VRCRouter"; 173 | this.Load += new System.EventHandler(this.About_Load); 174 | this.ResumeLayout(false); 175 | this.PerformLayout(); 176 | 177 | } 178 | 179 | #endregion 180 | 181 | private System.Windows.Forms.Label label6; 182 | private System.Windows.Forms.Label version; 183 | private System.Windows.Forms.Label built; 184 | private System.Windows.Forms.Label label2; 185 | private System.Windows.Forms.Label label4; 186 | private System.Windows.Forms.LinkLabel linkLabel1; 187 | private System.Windows.Forms.Label label3; 188 | private System.Windows.Forms.Label label1; 189 | private System.Windows.Forms.TextBox textBox1; 190 | private System.Windows.Forms.LinkLabel linkLabel2; 191 | } 192 | } -------------------------------------------------------------------------------- /vrcrouter-netf/vrcrouter-config/About.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | using System.Diagnostics; 6 | using System.Drawing; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using System.Windows.Forms; 11 | using ValueFactoryVRCRouterCommon; 12 | 13 | namespace ValueFactoryVRCRouterConfig { 14 | public partial class About : Form { 15 | public About() { 16 | InitializeComponent(); 17 | } 18 | 19 | private void About_Load(object sender, EventArgs e) { 20 | version.Text = $"Version {Build.build_version}"; 21 | built.Text = $"Built {Build.build_date}"; 22 | } 23 | 24 | private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { 25 | Process.Start(new ProcessStartInfo() { FileName = "https://shader.gay", UseShellExecute = true }); 26 | } 27 | 28 | private void linkLabel2_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { 29 | Process.Start(new ProcessStartInfo() { FileName = "https://github.com/valuef/VRCRouter", UseShellExecute = true }); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /vrcrouter-netf/vrcrouter-config/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /vrcrouter-netf/vrcrouter-config/Main.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Net; 4 | using System.Windows.Forms; 5 | using ValueFactoryVRCRouterCommon; 6 | using ValueFactoryVRCRouterConfig; 7 | 8 | namespace ValueFactoryVRCRouterConfig { 9 | public partial class Main : Form { 10 | public Main() { 11 | InitializeComponent(); 12 | } 13 | 14 | private void groupBox5_Enter(object sender, EventArgs e) { 15 | 16 | } 17 | 18 | private void Form1_Load(object sender, EventArgs e) { 19 | 20 | } 21 | 22 | 23 | private void enable_start_with_steamvr_Click(object sender, EventArgs e) { 24 | if (Program.steamvr_enable_autostart()) { 25 | MessageBox.Show("VRCRouter will now start with SteamVR!", Program.TITLE, MessageBoxButtons.OK, MessageBoxIcon.Information); 26 | } 27 | } 28 | 29 | private void disable_start_with_steamvr_Click(object sender, EventArgs e) { 30 | if (Program.steamvr_disable_autostart()) { 31 | MessageBox.Show("VRCRouter will no longer start with SteamVR.", Program.TITLE, MessageBoxButtons.OK, MessageBoxIcon.Information); 32 | } 33 | } 34 | 35 | private void wait_for_vrc_to_start_CheckedChanged(object sender, EventArgs e) { 36 | if (Program.is_loading) return; 37 | 38 | Program.config.wait_for_vrchat = wait_for_vrc_to_start.Checked; 39 | Program.save_config(); 40 | } 41 | 42 | private void osc_receive_port_input_ValueChanged(object sender, EventArgs e) { 43 | if (Program.is_loading) return; 44 | 45 | Program.config.receive_port = (int)osc_receive_port_input.Value; 46 | Program.save_config(); 47 | } 48 | 49 | private void Main_FormClosed(object sender, FormClosedEventArgs e) { 50 | Program.save_config(); 51 | } 52 | 53 | public static void validate_address(TextBox textbox, CancelEventArgs e) { 54 | var is_valid = IPAddress.TryParse(textbox.Text, out var _); 55 | if (is_valid) { 56 | return; 57 | } 58 | 59 | e.Cancel = true; 60 | textbox.SelectAll(); 61 | 62 | MessageBox.Show($"'{textbox.Text}' is not a valid IP Address!", "Boop Counter", MessageBoxButtons.OK, MessageBoxIcon.Warning); 63 | } 64 | private void osc_receive_address_input_Validating(object sender, CancelEventArgs e) { 65 | if (Program.is_loading) return; 66 | 67 | validate_address((TextBox)sender, e); 68 | } 69 | 70 | private void Main_FormClosing(object sender, FormClosingEventArgs e) { 71 | ValidateChildren(); 72 | } 73 | 74 | private void osc_receive_address_input_TextChanged(object sender, EventArgs e) { 75 | if (Program.is_loading) return; 76 | 77 | Program.config.receive_address = osc_receive_address_input.Text.Trim(); 78 | Program.save_config(); 79 | 80 | } 81 | 82 | private void route_list_SelectedIndexChanged(object sender, EventArgs e) { 83 | if (Program.is_loading) return; 84 | if (route_list.SelectedIndex < 0) return; 85 | 86 | Program.selected_route_index = route_list.SelectedIndex; 87 | configure_group_box.Show(); 88 | Program.populate_route_configuration(); 89 | 90 | check_for_duplicate_port_addr(); 91 | } 92 | 93 | private void route_enabled_CheckedChanged(object sender, EventArgs e) { 94 | if (Program.is_loading) return; 95 | 96 | Program.change_route_enabled_status(route_enabled.Checked); 97 | 98 | } 99 | 100 | private void route_osc_receive_address_Validating(object sender, CancelEventArgs e) { 101 | if (Program.is_loading) return; 102 | if (Program.selected_route_index < 0) return; 103 | 104 | var textbox = (TextBox)sender; 105 | validate_address(textbox, e); 106 | } 107 | public void check_for_duplicate_port_addr() { 108 | var addr = route_osc_receive_address.Text.Trim(); 109 | var port = (int)route_osc_receive_port.Value; 110 | var current_route = Program.get_edited_route(); 111 | 112 | if(current_route.output_port == 9000) { 113 | route_port_addr_in_use.Show(); 114 | route_port_addr_in_use.Text = $"Port 9000 is the default port that VRChat uses to receive data. Please don't use this port unless you've overriden the VRChat default port!"; 115 | return; 116 | } 117 | else if(current_route.output_port == 9001) { 118 | route_port_addr_in_use.Show(); 119 | route_port_addr_in_use.Text = $"Port 9001 is the default port that VRChat uses to send data. Please don't use this port unless you've overriden the VRChat default port!"; 120 | return; 121 | } 122 | 123 | foreach (var route in Program.routes) { 124 | if (route == current_route) continue; 125 | 126 | var addr_match = route.output_address.Equals(addr, StringComparison.InvariantCultureIgnoreCase); 127 | var port_match = route.output_port == port; 128 | 129 | if (addr_match && port_match && route.enabled) { 130 | route_port_addr_in_use.Show(); 131 | route_port_addr_in_use.Text = $"This address and port is already in use by route \"{route.name}\" Please change the port or address or else these routes may not work properly!"; 132 | return; 133 | } 134 | } 135 | 136 | route_port_addr_in_use.Hide(); 137 | } 138 | 139 | private void route_osc_receive_address_TextChanged(object sender, EventArgs e) { 140 | if (Program.is_loading) return; 141 | 142 | var route = Program.get_edited_route(); 143 | route.output_address = route_osc_receive_address.Text; 144 | Program.save_route(route); 145 | 146 | check_for_duplicate_port_addr(); 147 | } 148 | 149 | private void route_osc_receive_port_ValueChanged(object sender, EventArgs e) { 150 | if (Program.is_loading) return; 151 | 152 | var route = Program.get_edited_route(); 153 | route.output_port = (int)route_osc_receive_port.Value; 154 | Program.save_route(route); 155 | 156 | check_for_duplicate_port_addr(); 157 | } 158 | 159 | private void route_autostart_path_TextChanged(object sender, EventArgs e) { 160 | if (Program.is_loading) return; 161 | 162 | var route = Program.get_edited_route(); 163 | route.autostart_path = route_autostart_path.Text; 164 | Program.save_route(route); 165 | } 166 | 167 | private void route_autostart_args_TextChanged(object sender, EventArgs e) { 168 | if (Program.is_loading) return; 169 | 170 | var route = Program.get_edited_route(); 171 | route.autostart_args = route_autostart_args.Text; 172 | Program.save_route(route); 173 | } 174 | 175 | OpenFileDialog ofd = new OpenFileDialog(); 176 | 177 | private void route_browse_autostart_path_Click(object sender, EventArgs e) { 178 | var result = ofd.ShowDialog(); 179 | if (result == DialogResult.OK) { 180 | var file = ofd.FileName; 181 | var base_path = AppDomain.CurrentDomain.BaseDirectory; 182 | 183 | if (file.StartsWith(base_path, StringComparison.InvariantCultureIgnoreCase)) { 184 | file = file.Substring(base_path.Length); 185 | } 186 | 187 | var route = Program.get_edited_route(); 188 | route.autostart_path = file; 189 | route_autostart_path.Text = file; 190 | 191 | Program.save_route(route); 192 | } 193 | } 194 | 195 | private void route_autostart_launch_Click(object sender, EventArgs e) { 196 | Program.launch_autostart_app(Program.get_edited_route()); 197 | } 198 | 199 | private void remove_route_button_Click(object sender, EventArgs e) { 200 | Program.try_remove_route(Program.get_edited_route()); 201 | } 202 | 203 | public NewRouteForm new_route_form = new NewRouteForm(); 204 | 205 | private void create_new_route_button_Click(object sender, EventArgs e) { 206 | new_route_form.ShowDialog(); 207 | } 208 | 209 | RenameRouteForm rename_route_form = new RenameRouteForm(); 210 | 211 | private void rename_route_Click(object sender, EventArgs e) { 212 | var route = Program.get_edited_route(); 213 | 214 | rename_route_form.old_route_name.Text = route.name; 215 | rename_route_form.new_name.Text = route.name; 216 | 217 | rename_route_form.ShowDialog(); 218 | } 219 | 220 | private void route_autoclose_app_executable_name_TextChanged(object sender, EventArgs e) { 221 | if (Program.is_loading) return; 222 | 223 | var route = Program.get_edited_route(); 224 | route.autoclose_executable_name = route_autoclose_app_executable_name.Text.Trim(); 225 | 226 | Program.save_route(route); 227 | } 228 | 229 | private void button1_Click(object sender, EventArgs e) { 230 | 231 | var route = Program.get_edited_route(); 232 | 233 | var exe_name = route.autoclose_executable_name; 234 | var result = Native.terminate_app_by_name(exe_name); 235 | if (result) { 236 | MessageBox.Show($"Closed '{exe_name}'!", Program.TITLE, MessageBoxButtons.OK, MessageBoxIcon.Information); 237 | } 238 | else { 239 | MessageBox.Show($"Failed to close '{exe_name}'. Is the program running?", Program.TITLE, MessageBoxButtons.OK, MessageBoxIcon.Warning); 240 | } 241 | } 242 | 243 | private void button2_Click(object sender, EventArgs e) { 244 | MessageBox.Show("This should just be the executable name (including the .exe) of the app you want to close.\r\n\r\nFor example, if I want to close VRCFaceTracking, I'd put VRCFaceTracking.exe into this field.", Program.TITLE, MessageBoxButtons.OK, MessageBoxIcon.Information); 245 | } 246 | 247 | private void route_osc_enabled_CheckedChanged(object sender, EventArgs e) { 248 | if (Program.is_loading) return; 249 | 250 | var route = Program.get_edited_route(); 251 | route.routing_enabled = route_osc_enabled.Checked; 252 | 253 | Program.save_route(route); 254 | Program.populate_route_configuration(); 255 | } 256 | 257 | About about; 258 | 259 | private void aboutToolStripMenuItem_Click(object sender, EventArgs e) { 260 | if(about == null) { 261 | about = new About(); 262 | } 263 | about.ShowDialog(); 264 | } 265 | } 266 | } 267 | -------------------------------------------------------------------------------- /vrcrouter-netf/vrcrouter-config/NewRouteForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace ValueFactoryVRCRouterConfig { 2 | partial class NewRouteForm { 3 | /// 4 | /// Required designer variable. 5 | /// 6 | private System.ComponentModel.IContainer components = null; 7 | 8 | /// 9 | /// Clean up any resources being used. 10 | /// 11 | /// true if managed resources should be disposed; otherwise, false. 12 | protected override void Dispose(bool disposing) { 13 | if (disposing && (components != null)) { 14 | components.Dispose(); 15 | } 16 | base.Dispose(disposing); 17 | } 18 | 19 | #region Windows Form Designer generated code 20 | 21 | /// 22 | /// Required method for Designer support - do not modify 23 | /// the contents of this method with the code editor. 24 | /// 25 | private void InitializeComponent() { 26 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(NewRouteForm)); 27 | this.label1 = new System.Windows.Forms.Label(); 28 | this.new_route_name = new System.Windows.Forms.TextBox(); 29 | this.create_route = new System.Windows.Forms.Button(); 30 | this.SuspendLayout(); 31 | // 32 | // label1 33 | // 34 | this.label1.AutoSize = true; 35 | this.label1.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 36 | this.label1.Location = new System.Drawing.Point(12, 15); 37 | this.label1.Name = "label1"; 38 | this.label1.Size = new System.Drawing.Size(39, 15); 39 | this.label1.TabIndex = 0; 40 | this.label1.Text = "Name"; 41 | // 42 | // new_route_name 43 | // 44 | this.new_route_name.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 45 | this.new_route_name.Location = new System.Drawing.Point(57, 12); 46 | this.new_route_name.Name = "new_route_name"; 47 | this.new_route_name.Size = new System.Drawing.Size(279, 23); 48 | this.new_route_name.TabIndex = 1; 49 | this.new_route_name.TextChanged += new System.EventHandler(this.new_route_name_TextChanged); 50 | // 51 | // create_route 52 | // 53 | this.create_route.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 54 | this.create_route.Location = new System.Drawing.Point(12, 41); 55 | this.create_route.Name = "create_route"; 56 | this.create_route.Size = new System.Drawing.Size(324, 23); 57 | this.create_route.TabIndex = 2; 58 | this.create_route.Text = "Create"; 59 | this.create_route.UseVisualStyleBackColor = true; 60 | this.create_route.Click += new System.EventHandler(this.create_route_Click); 61 | // 62 | // NewRouteForm 63 | // 64 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 65 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 66 | this.ClientSize = new System.Drawing.Size(348, 75); 67 | this.Controls.Add(this.create_route); 68 | this.Controls.Add(this.new_route_name); 69 | this.Controls.Add(this.label1); 70 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; 71 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); 72 | this.MaximizeBox = false; 73 | this.Name = "NewRouteForm"; 74 | this.Text = "Create New Route"; 75 | this.ResumeLayout(false); 76 | this.PerformLayout(); 77 | 78 | } 79 | 80 | #endregion 81 | 82 | private System.Windows.Forms.Label label1; 83 | private System.Windows.Forms.TextBox new_route_name; 84 | private System.Windows.Forms.Button create_route; 85 | } 86 | } -------------------------------------------------------------------------------- /vrcrouter-netf/vrcrouter-config/NewRouteForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | using System.Drawing; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Windows.Forms; 10 | using ValueFactoryVRCRouterConfig; 11 | 12 | namespace ValueFactoryVRCRouterConfig { 13 | public partial class NewRouteForm : Form { 14 | public NewRouteForm() { 15 | InitializeComponent(); 16 | } 17 | 18 | private void new_route_name_TextChanged(object sender, EventArgs e) { 19 | if (string.IsNullOrWhiteSpace(new_route_name.Text)) { 20 | create_route.Enabled = false; 21 | } 22 | else { 23 | create_route.Enabled = true; 24 | } 25 | } 26 | 27 | private void create_route_Click(object sender, EventArgs e) { 28 | var name = new_route_name.Text; 29 | 30 | if (Program.try_create_new_route(name, out var ex, out var error)) { 31 | Program.show_confirm_notif($"Created route '{name}'"); 32 | Close(); 33 | } 34 | else { 35 | if(ex == null) { 36 | MessageBox.Show(error, Program.TITLE, MessageBoxButtons.OK, MessageBoxIcon.Warning); 37 | } 38 | else { 39 | MessageBox.Show($"{error}\r\n\r\nException info: {ex}", Program.TITLE, MessageBoxButtons.OK, MessageBoxIcon.Error); 40 | } 41 | } 42 | } 43 | 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /vrcrouter-netf/vrcrouter-config/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("vrcrouter-config")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("vrcrouter-config")] 13 | [assembly: AssemblyCopyright("Copyright © 2023")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("4021d161-f4a3-462b-b6b3-0a6cd87728eb")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /vrcrouter-netf/vrcrouter-config/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace ValueFactoryVRCRouterConfig.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ValueFactoryVRCRouterConfig.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /vrcrouter-netf/vrcrouter-config/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /vrcrouter-netf/vrcrouter-config/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace ValueFactoryVRCRouterConfig.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.5.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /vrcrouter-netf/vrcrouter-config/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /vrcrouter-netf/vrcrouter-config/RenameRouteForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace ValueFactoryVRCRouterConfig { 2 | partial class RenameRouteForm { 3 | /// 4 | /// Required designer variable. 5 | /// 6 | private System.ComponentModel.IContainer components = null; 7 | 8 | /// 9 | /// Clean up any resources being used. 10 | /// 11 | /// true if managed resources should be disposed; otherwise, false. 12 | protected override void Dispose(bool disposing) { 13 | if (disposing && (components != null)) { 14 | components.Dispose(); 15 | } 16 | base.Dispose(disposing); 17 | } 18 | 19 | #region Windows Form Designer generated code 20 | 21 | /// 22 | /// Required method for Designer support - do not modify 23 | /// the contents of this method with the code editor. 24 | /// 25 | private void InitializeComponent() { 26 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(RenameRouteForm)); 27 | this.label1 = new System.Windows.Forms.Label(); 28 | this.old_route_name = new System.Windows.Forms.Label(); 29 | this.new_name = new System.Windows.Forms.TextBox(); 30 | this.label3 = new System.Windows.Forms.Label(); 31 | this.rename_button = new System.Windows.Forms.Button(); 32 | this.SuspendLayout(); 33 | // 34 | // label1 35 | // 36 | this.label1.AutoSize = true; 37 | this.label1.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 38 | this.label1.Location = new System.Drawing.Point(12, 9); 39 | this.label1.Name = "label1"; 40 | this.label1.Size = new System.Drawing.Size(64, 15); 41 | this.label1.TabIndex = 0; 42 | this.label1.Text = "Old Name:"; 43 | this.label1.Click += new System.EventHandler(this.label1_Click); 44 | // 45 | // old_route_name 46 | // 47 | this.old_route_name.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 48 | this.old_route_name.Location = new System.Drawing.Point(84, 9); 49 | this.old_route_name.Name = "old_route_name"; 50 | this.old_route_name.Size = new System.Drawing.Size(235, 15); 51 | this.old_route_name.TabIndex = 1; 52 | this.old_route_name.Text = "OLD ROUTE NAME"; 53 | this.old_route_name.Click += new System.EventHandler(this.old_route_name_Click); 54 | // 55 | // new_name 56 | // 57 | this.new_name.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 58 | this.new_name.Location = new System.Drawing.Point(87, 27); 59 | this.new_name.Name = "new_name"; 60 | this.new_name.Size = new System.Drawing.Size(232, 23); 61 | this.new_name.TabIndex = 2; 62 | this.new_name.TextChanged += new System.EventHandler(this.new_name_TextChanged); 63 | // 64 | // label3 65 | // 66 | this.label3.AutoSize = true; 67 | this.label3.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 68 | this.label3.Location = new System.Drawing.Point(12, 30); 69 | this.label3.Name = "label3"; 70 | this.label3.Size = new System.Drawing.Size(69, 15); 71 | this.label3.TabIndex = 3; 72 | this.label3.Text = "New Name:"; 73 | this.label3.Click += new System.EventHandler(this.label3_Click); 74 | // 75 | // rename_button 76 | // 77 | this.rename_button.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 78 | this.rename_button.Location = new System.Drawing.Point(15, 56); 79 | this.rename_button.Name = "rename_button"; 80 | this.rename_button.Size = new System.Drawing.Size(304, 23); 81 | this.rename_button.TabIndex = 4; 82 | this.rename_button.Text = "Rename"; 83 | this.rename_button.UseVisualStyleBackColor = true; 84 | this.rename_button.Click += new System.EventHandler(this.rename_button_Click); 85 | // 86 | // RenameRouteForm 87 | // 88 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 89 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 90 | this.ClientSize = new System.Drawing.Size(331, 92); 91 | this.Controls.Add(this.rename_button); 92 | this.Controls.Add(this.label3); 93 | this.Controls.Add(this.new_name); 94 | this.Controls.Add(this.old_route_name); 95 | this.Controls.Add(this.label1); 96 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; 97 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); 98 | this.MaximizeBox = false; 99 | this.Name = "RenameRouteForm"; 100 | this.Text = "Rename Route"; 101 | this.ResumeLayout(false); 102 | this.PerformLayout(); 103 | 104 | } 105 | 106 | #endregion 107 | 108 | public System.Windows.Forms.Label label1; 109 | public System.Windows.Forms.Label old_route_name; 110 | public System.Windows.Forms.TextBox new_name; 111 | public System.Windows.Forms.Label label3; 112 | public System.Windows.Forms.Button rename_button; 113 | } 114 | } -------------------------------------------------------------------------------- /vrcrouter-netf/vrcrouter-config/RenameRouteForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | using System.Drawing; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Windows.Forms; 10 | 11 | namespace ValueFactoryVRCRouterConfig { 12 | public partial class RenameRouteForm : Form { 13 | public RenameRouteForm() { 14 | InitializeComponent(); 15 | } 16 | 17 | private void new_name_TextChanged(object sender, EventArgs e) { 18 | var old_name = old_route_name.Text.Trim(); 19 | var new_name_text = new_name.Text.Trim(); 20 | 21 | var is_empty = string.IsNullOrWhiteSpace(new_name_text); 22 | var is_the_same = new_name_text.Equals(old_name, StringComparison.InvariantCultureIgnoreCase); 23 | if (is_empty || is_the_same) { 24 | rename_button.Enabled = false; 25 | } 26 | else { 27 | rename_button.Enabled = true; 28 | } 29 | } 30 | 31 | private void rename_button_Click(object sender, EventArgs e) { 32 | var new_name_text = new_name.Text.Trim(); 33 | 34 | if (Program.try_rename_route(new_name_text, out var ex, out var error)) { 35 | Program.show_confirm_notif($"Renamed '{old_route_name.Text}' to '{new_name_text}'"); 36 | Close(); 37 | } 38 | else { 39 | MessageBox.Show(error, Program.TITLE, MessageBoxButtons.OK, MessageBoxIcon.Warning); 40 | } 41 | } 42 | 43 | private void label3_Click(object sender, EventArgs e) { 44 | 45 | } 46 | 47 | private void old_route_name_Click(object sender, EventArgs e) { 48 | 49 | } 50 | 51 | private void label1_Click(object sender, EventArgs e) { 52 | 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /vrcrouter-netf/vrcrouter-config/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valuef/VRCRouter/4aab0bd8c374e8f883d0a46b844b9c4f6d11519e/vrcrouter-netf/vrcrouter-config/icon.ico -------------------------------------------------------------------------------- /vrcrouter-netf/vrcrouter-config/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /vrcrouter-netf/vrcrouter-config/vrcrouter-config.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {4021D161-F4A3-462B-B6B3-0A6CD87728EB} 8 | WinExe 9 | ValueFactoryVRCRouterConfig 10 | VRCRouter Configuration 11 | v4.8.1 12 | 512 13 | true 14 | true 15 | publish\ 16 | true 17 | Disk 18 | false 19 | Foreground 20 | 7 21 | Days 22 | false 23 | false 24 | true 25 | 0 26 | 1.0.0.%2a 27 | false 28 | false 29 | true 30 | 31 | 32 | x64 33 | true 34 | full 35 | false 36 | bin\Debug\ 37 | DEBUG;TRACE 38 | prompt 39 | 4 40 | 41 | 42 | x64 43 | pdbonly 44 | true 45 | bin\Release\ 46 | TRACE 47 | prompt 48 | 4 49 | 50 | 51 | icon.ico 52 | 53 | 54 | 55 | 56 | 57 | 58 | ..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | Form 69 | 70 | 71 | About.cs 72 | 73 | 74 | Form 75 | 76 | 77 | Main.cs 78 | 79 | 80 | Form 81 | 82 | 83 | NewRouteForm.cs 84 | 85 | 86 | 87 | 88 | Form 89 | 90 | 91 | RenameRouteForm.cs 92 | 93 | 94 | About.cs 95 | 96 | 97 | Main.cs 98 | 99 | 100 | NewRouteForm.cs 101 | 102 | 103 | ResXFileCodeGenerator 104 | Resources.Designer.cs 105 | Designer 106 | 107 | 108 | True 109 | Resources.resx 110 | True 111 | 112 | 113 | RenameRouteForm.cs 114 | 115 | 116 | 117 | SettingsSingleFileGenerator 118 | Settings.Designer.cs 119 | 120 | 121 | True 122 | Settings.settings 123 | True 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | False 135 | Microsoft .NET Framework 4.8.1 %28x86 and x64%29 136 | true 137 | 138 | 139 | False 140 | .NET Framework 3.5 SP1 141 | false 142 | 143 | 144 | 145 | 146 | {627a06c1-0cd0-40a5-b3ae-1e86eb8ec9f6} 147 | vrcrouter-common 148 | 149 | 150 | 151 | -------------------------------------------------------------------------------- /vrcrouter-netf/vrcrouter-ui/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /vrcrouter-netf/vrcrouter-ui/Form1.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace ValueFactoryVRCRouter { 2 | partial class Form1 { 3 | /// 4 | /// Required designer variable. 5 | /// 6 | private System.ComponentModel.IContainer components = null; 7 | 8 | /// 9 | /// Clean up any resources being used. 10 | /// 11 | /// true if managed resources should be disposed; otherwise, false. 12 | protected override void Dispose(bool disposing) { 13 | if (disposing && (components != null)) { 14 | components.Dispose(); 15 | } 16 | base.Dispose(disposing); 17 | } 18 | 19 | #region Windows Form Designer generated code 20 | 21 | /// 22 | /// Required method for Designer support - do not modify 23 | /// the contents of this method with the code editor. 24 | /// 25 | private void InitializeComponent() { 26 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1)); 27 | this.groupBox1 = new System.Windows.Forms.GroupBox(); 28 | this.label3 = new System.Windows.Forms.Label(); 29 | this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); 30 | this.groupBox2 = new System.Windows.Forms.GroupBox(); 31 | this.label5 = new System.Windows.Forms.Label(); 32 | this.label4 = new System.Windows.Forms.Label(); 33 | this.vrcrouter_version = new System.Windows.Forms.Label(); 34 | this.linkLabel1 = new System.Windows.Forms.LinkLabel(); 35 | this.routeControl1 = new ValueFactoryVRCRouter.RouteControl(); 36 | this.routeControl2 = new ValueFactoryVRCRouter.RouteControl(); 37 | this.routeControl3 = new ValueFactoryVRCRouter.RouteControl(); 38 | this.groupBox1.SuspendLayout(); 39 | this.flowLayoutPanel1.SuspendLayout(); 40 | this.groupBox2.SuspendLayout(); 41 | this.SuspendLayout(); 42 | // 43 | // groupBox1 44 | // 45 | this.groupBox1.Controls.Add(this.label3); 46 | this.groupBox1.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 47 | this.groupBox1.Location = new System.Drawing.Point(12, 12); 48 | this.groupBox1.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); 49 | this.groupBox1.Name = "groupBox1"; 50 | this.groupBox1.Padding = new System.Windows.Forms.Padding(4, 3, 4, 3); 51 | this.groupBox1.Size = new System.Drawing.Size(258, 47); 52 | this.groupBox1.TabIndex = 0; 53 | this.groupBox1.TabStop = false; 54 | this.groupBox1.Text = "Status"; 55 | // 56 | // label3 57 | // 58 | this.label3.AutoSize = true; 59 | this.label3.Location = new System.Drawing.Point(7, 19); 60 | this.label3.Name = "label3"; 61 | this.label3.Size = new System.Drawing.Size(157, 15); 62 | this.label3.TabIndex = 6; 63 | this.label3.Text = "Waiting for VRChat to start..."; 64 | // 65 | // flowLayoutPanel1 66 | // 67 | this.flowLayoutPanel1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; 68 | this.flowLayoutPanel1.Controls.Add(this.routeControl1); 69 | this.flowLayoutPanel1.Controls.Add(this.routeControl2); 70 | this.flowLayoutPanel1.Controls.Add(this.routeControl3); 71 | this.flowLayoutPanel1.Location = new System.Drawing.Point(9, 55); 72 | this.flowLayoutPanel1.MaximumSize = new System.Drawing.Size(540, 0); 73 | this.flowLayoutPanel1.Name = "flowLayoutPanel1"; 74 | this.flowLayoutPanel1.Size = new System.Drawing.Size(540, 236); 75 | this.flowLayoutPanel1.TabIndex = 3; 76 | // 77 | // groupBox2 78 | // 79 | this.groupBox2.Controls.Add(this.label5); 80 | this.groupBox2.Controls.Add(this.label4); 81 | this.groupBox2.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 82 | this.groupBox2.Location = new System.Drawing.Point(278, 12); 83 | this.groupBox2.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); 84 | this.groupBox2.Name = "groupBox2"; 85 | this.groupBox2.Padding = new System.Windows.Forms.Padding(4, 3, 4, 3); 86 | this.groupBox2.Size = new System.Drawing.Size(258, 47); 87 | this.groupBox2.TabIndex = 7; 88 | this.groupBox2.TabStop = false; 89 | this.groupBox2.Text = "Info"; 90 | // 91 | // label5 92 | // 93 | this.label5.AutoSize = true; 94 | this.label5.Location = new System.Drawing.Point(128, 19); 95 | this.label5.Name = "label5"; 96 | this.label5.Size = new System.Drawing.Size(79, 15); 97 | this.label5.TabIndex = 7; 98 | this.label5.Text = "127.0.0.1:9000"; 99 | // 100 | // label4 101 | // 102 | this.label4.AutoSize = true; 103 | this.label4.Location = new System.Drawing.Point(7, 19); 104 | this.label4.Name = "label4"; 105 | this.label4.Size = new System.Drawing.Size(115, 15); 106 | this.label4.TabIndex = 6; 107 | this.label4.Text = "Receiving OSC From"; 108 | // 109 | // vrcrouter_version 110 | // 111 | this.vrcrouter_version.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); 112 | this.vrcrouter_version.AutoSize = true; 113 | this.vrcrouter_version.Location = new System.Drawing.Point(9, 294); 114 | this.vrcrouter_version.Name = "vrcrouter_version"; 115 | this.vrcrouter_version.Size = new System.Drawing.Size(105, 15); 116 | this.vrcrouter_version.TabIndex = 8; 117 | this.vrcrouter_version.Text = "VRCRouter Version"; 118 | // 119 | // linkLabel1 120 | // 121 | this.linkLabel1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); 122 | this.linkLabel1.AutoSize = true; 123 | this.linkLabel1.Location = new System.Drawing.Point(467, 294); 124 | this.linkLabel1.Name = "linkLabel1"; 125 | this.linkLabel1.Size = new System.Drawing.Size(64, 15); 126 | this.linkLabel1.TabIndex = 9; 127 | this.linkLabel1.TabStop = true; 128 | this.linkLabel1.Text = "shader.gay"; 129 | this.linkLabel1.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel1_LinkClicked); 130 | // 131 | // routeControl1 132 | // 133 | this.routeControl1.Location = new System.Drawing.Point(4, 3); 134 | this.routeControl1.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); 135 | this.routeControl1.Name = "routeControl1"; 136 | this.routeControl1.Size = new System.Drawing.Size(262, 78); 137 | this.routeControl1.TabIndex = 10; 138 | // 139 | // routeControl2 140 | // 141 | this.routeControl2.Location = new System.Drawing.Point(275, 3); 142 | this.routeControl2.Margin = new System.Windows.Forms.Padding(5, 3, 5, 3); 143 | this.routeControl2.Name = "routeControl2"; 144 | this.routeControl2.Size = new System.Drawing.Size(255, 90); 145 | this.routeControl2.TabIndex = 11; 146 | // 147 | // routeControl3 148 | // 149 | this.routeControl3.Location = new System.Drawing.Point(6, 99); 150 | this.routeControl3.Margin = new System.Windows.Forms.Padding(6, 3, 6, 3); 151 | this.routeControl3.Name = "routeControl3"; 152 | this.routeControl3.Size = new System.Drawing.Size(260, 104); 153 | this.routeControl3.TabIndex = 12; 154 | // 155 | // Form1 156 | // 157 | this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); 158 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 159 | this.AutoSize = true; 160 | this.ClientSize = new System.Drawing.Size(543, 318); 161 | this.Controls.Add(this.linkLabel1); 162 | this.Controls.Add(this.vrcrouter_version); 163 | this.Controls.Add(this.groupBox2); 164 | this.Controls.Add(this.groupBox1); 165 | this.Controls.Add(this.flowLayoutPanel1); 166 | this.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 167 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; 168 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); 169 | this.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); 170 | this.MaximizeBox = false; 171 | this.Name = "Form1"; 172 | this.Text = "VRCRouter"; 173 | this.Load += new System.EventHandler(this.Form1_Load); 174 | this.groupBox1.ResumeLayout(false); 175 | this.groupBox1.PerformLayout(); 176 | this.flowLayoutPanel1.ResumeLayout(false); 177 | this.groupBox2.ResumeLayout(false); 178 | this.groupBox2.PerformLayout(); 179 | this.ResumeLayout(false); 180 | this.PerformLayout(); 181 | 182 | } 183 | 184 | #endregion 185 | 186 | private System.Windows.Forms.GroupBox groupBox1; 187 | private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1; 188 | private System.Windows.Forms.Label label3; 189 | private System.Windows.Forms.GroupBox groupBox2; 190 | private System.Windows.Forms.Label label5; 191 | private System.Windows.Forms.Label label4; 192 | private System.Windows.Forms.Label vrcrouter_version; 193 | private System.Windows.Forms.LinkLabel linkLabel1; 194 | private RouteControl routeControl1; 195 | private RouteControl routeControl2; 196 | private RouteControl routeControl3; 197 | } 198 | } 199 | 200 | -------------------------------------------------------------------------------- /vrcrouter-netf/vrcrouter-ui/Form1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Drawing; 4 | using System.Windows.Forms; 5 | using ValueFactoryVRCRouterCommon; 6 | 7 | namespace ValueFactoryVRCRouter { 8 | public partial class Form1 : Form { 9 | public Form1() { 10 | InitializeComponent(); 11 | } 12 | 13 | private void Form1_Load(object sender, EventArgs e) { 14 | vrcrouter_version.Text = $"VRCRouter Version {Build.build_version} Date {Build.build_date}"; 15 | flowLayoutPanel1.AutoSize = true; 16 | //pictureBox1.Image = Icon.ExtractAssociatedIcon("C:\\stuff\\VRCFaceTracking.exe").ToBitmap(); 17 | } 18 | 19 | private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { 20 | Process.Start(new ProcessStartInfo() { FileName = "https://shader.gay", UseShellExecute = true }); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /vrcrouter-netf/vrcrouter-ui/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Net.Sockets; 5 | using System.Net; 6 | using System.Runtime.InteropServices; 7 | using System.Text; 8 | using System.Threading; 9 | using System.Windows.Forms; 10 | using Valve.VR; 11 | using ValueFactoryVRCRouterCommon; 12 | using System.IO; 13 | 14 | namespace ValueFactoryVRCRouter { 15 | internal static class Program { 16 | 17 | public static Form1 main; 18 | 19 | /// 20 | /// The main entry point for the application. 21 | /// 22 | [STAThread] 23 | static void Main() { 24 | 25 | Application.EnableVisualStyles(); 26 | Application.SetCompatibleTextRenderingDefault(false); 27 | 28 | main = new Form1(); 29 | 30 | // TODO exception 31 | var thread = new Thread(background_main); 32 | thread.Start(); 33 | 34 | Application.Run(main); 35 | } 36 | 37 | enum Log_Type { 38 | normal, 39 | warning, 40 | error 41 | } 42 | 43 | static void logt(string type, string message, Log_Type log_type = Log_Type.normal) { 44 | if(log_type == Log_Type.warning) { 45 | var col = Console.ForegroundColor; 46 | Console.ForegroundColor = ConsoleColor.Yellow; 47 | Console.Write($"[{type}] "); 48 | Console.ForegroundColor = col; 49 | } 50 | else if(log_type == Log_Type.error) { 51 | var col = Console.ForegroundColor; 52 | Console.ForegroundColor = ConsoleColor.Red; 53 | Console.Write($"[{type}] "); 54 | Console.ForegroundColor = col; 55 | } 56 | else { 57 | Console.Write($"[{type}] "); 58 | } 59 | Console.WriteLine(message); 60 | } 61 | 62 | static CVRSystem vr_system; 63 | 64 | public static List autoclose_exes = new List(); 65 | 66 | public static void terminate_autostart_apps() { 67 | logt("autostart", "Closing autostarted apps..."); 68 | 69 | foreach(var exe in autoclose_exes) { 70 | Native.terminate_app_by_name(exe); 71 | } 72 | 73 | foreach(var process_id in child_processes) { 74 | Native.terminate_app(process_id); 75 | } 76 | } 77 | 78 | public static void on_exit() { 79 | terminate_autostart_apps(); 80 | } 81 | 82 | readonly static uint VREvent_T_Size = (uint)Marshal.SizeOf(typeof(VREvent_t)); 83 | 84 | public static void vr_thread() { 85 | logt("vr thread", "Started!"); 86 | 87 | VREvent_t ev = new VREvent_t(); 88 | while(true) { 89 | if(vr_system.PollNextEvent(ref ev, VREvent_T_Size)) { 90 | if(ev.eventType == (uint)EVREventType.VREvent_Quit) { 91 | logt("vr thread", "Got VREvent_Quit event, stopping."); 92 | 93 | // NOTE(valuef): terminate_autostart_apps uses resources created on the main thread, but 94 | // we're not going to be writing to them in any way that would cause threading issues, so 95 | // this is okay! 96 | // 2023-05-08 97 | terminate_autostart_apps(); 98 | logt("vr thread", "Exiting VR Thread\n"); 99 | return; 100 | } 101 | } 102 | else { 103 | Thread.Sleep(1000); 104 | } 105 | } 106 | } 107 | 108 | 109 | public static Config.V1 config = new Config.V1(); 110 | public static List child_processes = new List(); 111 | 112 | public struct Route_Endpoint { 113 | public EndPoint endpoint; 114 | public string address; 115 | public int port; 116 | } 117 | 118 | public static List route_endpoints= new List(); 119 | 120 | static void loud_crash(string text) { 121 | MessageBox.Show(text, "VRCRouter", MessageBoxButtons.OK, MessageBoxIcon.Error); 122 | Environment.Exit(1); 123 | } 124 | 125 | [DllImport("Kernel32")] 126 | static extern bool SetConsoleCtrlHandler(EventHandler handler, bool add); 127 | 128 | delegate bool EventHandler(CtrlType sig); 129 | static EventHandler exit_handler; 130 | 131 | enum CtrlType { 132 | CTRL_C_EVENT = 0, 133 | CTRL_BREAK_EVENT = 1, 134 | CTRL_CLOSE_EVENT = 2, 135 | CTRL_LOGOFF_EVENT = 5, 136 | CTRL_SHUTDOWN_EVENT = 6 137 | } 138 | 139 | 140 | static void background_main() { 141 | try { 142 | wrapped_main(); 143 | } 144 | catch(Exception ex) { 145 | MessageBox.Show($"Uh oh! We've encountered an unhandled error in the background thread. Please let us know about this at discord.shader.gay\r\n\r\n\r\n{ex}\r\n\r\nThe router will now exit.", "VRCRouter", MessageBoxButtons.OK, MessageBoxIcon.Error); 146 | Process.Start(new ProcessStartInfo() { FileName = "https://discord.shader.gay", UseShellExecute = true }); 147 | } 148 | } 149 | 150 | static void wrapped_main() { 151 | // NOTE(valuef): Idle until the form has been created 152 | // 2023-06-10 153 | while(!main.Created) { 154 | Thread.Sleep(100); 155 | } 156 | 157 | exit_handler += (s) => { on_exit(); return true; }; 158 | SetConsoleCtrlHandler(exit_handler, true); 159 | Console.CancelKeyPress += (s, e) => { on_exit(); }; 160 | AppDomain.CurrentDomain.ProcessExit += (s, e) => { on_exit(); }; 161 | 162 | Console.WriteLine(""); 163 | Console.WriteLine($"--> Starting VRCRouter build {Build.build_version}, date {Build.build_date}"); 164 | Console.WriteLine($"--> ValueFactory https://shader.gay"); 165 | Console.WriteLine("------------------------------------"); 166 | Console.WriteLine(""); 167 | 168 | do { 169 | 170 | var vr_err = EVRInitError.None; 171 | try { 172 | vr_system = OpenVR.Init(ref vr_err, EVRApplicationType.VRApplication_Background); 173 | } 174 | catch(Exception ex) { 175 | logt("vr", $"VR_Init failed due to an exception: {ex}. SteamVR initialization stopped. The router will still function.", Log_Type.error); 176 | break; 177 | } 178 | 179 | if(vr_system == null) { 180 | var err_desc = OpenVR.GetStringForHmdError(vr_err); 181 | logt("vr", $"VR_Init failed: {err_desc}. SteamVR initialization stopped. The router will still function.", Log_Type.warning); 182 | break; 183 | } 184 | 185 | logt("vr", "SteamVR setup done!"); 186 | } while(false); 187 | 188 | if(File.Exists(Config.FILE)) { 189 | var new_config = Config.try_load_config(Config.FILE, out var load_ex, out var load_error); 190 | if(new_config == null) { 191 | logt("config", $"Failed to load config. Using defaults. Error message: {load_error}", Log_Type.warning); 192 | if(load_ex != null) { 193 | logt("config", $"Exception: {load_ex}", Log_Type.warning); 194 | } 195 | } 196 | else { 197 | config = new_config; 198 | } 199 | } 200 | else { 201 | logt("config", $"Config file not found ({Config.FILE}). Using defaults.", Log_Type.warning); 202 | } 203 | 204 | if(config.wait_for_vrchat) { 205 | logt("vrchat", "Wait for VRChat is enabled. Waiting for VRChat to start before launching autostart apps and routing...", Log_Type.warning); 206 | var result = Native.wait_for_vrchat_to_start(); 207 | if(result) { 208 | logt("vrchat", "VRChat executable found! Continuing."); 209 | } 210 | else { 211 | logt("vrchat", "An error occurred while waiting for VRChat to start. Stopping waiting and continuing.", Log_Type.error); 212 | } 213 | } 214 | 215 | do { 216 | 217 | var files = Route.get_routes_folder_contents(out var routes_folder_err); 218 | if(routes_folder_err != null) { 219 | logt("route", routes_folder_err, Log_Type.error); 220 | break; 221 | } 222 | 223 | foreach(var file in files) { 224 | logt("route", $"Loading route {file}"); 225 | 226 | var route = Route.try_load_route(file, out var load_ex, out var load_error); 227 | 228 | if(route == null) { 229 | logt("route", load_error, Log_Type.error); 230 | if(load_ex != null) { 231 | logt("route", $"Exception: {load_ex}", Log_Type.error); 232 | } 233 | continue; 234 | } 235 | 236 | if(!route.enabled) { 237 | logt("route", "Route disabled, skipping.", Log_Type.warning); 238 | continue; 239 | } 240 | 241 | if(string.IsNullOrWhiteSpace(route.autostart_path)) { 242 | logt("route", "Doesn't specify autostart-path, no app will be started.", Log_Type.warning); 243 | } 244 | else { 245 | var data = Common.prep_autostart_data(route, out var error); 246 | if(error != null) { 247 | logt("route", error, Log_Type.warning); 248 | // NOTE(valuef): We still continue even if we errored as that function should give us valid autostart data all the time 249 | // 2023-06-21 250 | } 251 | 252 | var result = new Native.Autostart_App_Result(); 253 | Native.launch_autostart_app(new StringBuilder(data.full_path), new StringBuilder(data.args), new StringBuilder(data.working_dir), ref result); 254 | 255 | if(result.success) { 256 | child_processes.Add(result.id); 257 | logt("route", $"Started app {route.autostart_path} {data.args}"); 258 | } 259 | else { 260 | logt("route", $"Failed to start the autostart app at path {data.full_path} with args {data.args} and working directory {data.working_dir}. Error code: {result.error}"); 261 | } 262 | } 263 | 264 | if(!string.IsNullOrWhiteSpace(route.autoclose_executable_name)) { 265 | autoclose_exes.Add(route.autoclose_executable_name); 266 | logt("route", $"Registered an executable to autoclose: {route.autoclose_executable_name}"); 267 | } 268 | 269 | if(!route.routing_enabled) { 270 | logt("route", "OSC Routing has been explicitly disabled on this route.", Log_Type.warning); 271 | } 272 | else if(string.IsNullOrWhiteSpace(route.output_address)) { 273 | logt("route", "Doesn't specify output-address, no routing will be done.", Log_Type.warning); 274 | } 275 | else if(route.output_port <= 0) { 276 | logt("route", "Doesn't specify a valid output-port, no routing will be done.", Log_Type.warning); 277 | } 278 | else { 279 | IPAddress ip; 280 | if (!IPAddress.TryParse(route.output_address, out ip)) { 281 | logt("route", $"The address '{route.output_address}' is invalid! No routing will be done for this route.", Log_Type.error); 282 | } 283 | else { 284 | EndPoint endpoint = null; 285 | try { 286 | endpoint = new IPEndPoint(ip, route.output_port); 287 | } 288 | catch(ArgumentOutOfRangeException ex) { 289 | logt("route", $"Looks like the port '{route.output_port}' is not a valid port! No routing will be done for this route.", Log_Type.error); 290 | } 291 | 292 | if(endpoint != null) { 293 | var end = new Route_Endpoint(); 294 | end.endpoint = endpoint; 295 | end.address = route.output_address; 296 | end.port = route.output_port; 297 | route_endpoints.Add(end); 298 | logt("route", $"Registered route output to {route.output_address}:{route.output_port}"); 299 | } 300 | } 301 | } 302 | } 303 | 304 | } while(false); 305 | 306 | var do_routing = true; 307 | 308 | var receive_socket = new Socket(SocketType.Dgram, ProtocolType.Udp); 309 | receive_socket.ReceiveTimeout = 0; 310 | var send_socket = new Socket(SocketType.Dgram, ProtocolType.Udp); 311 | 312 | EndPoint receive_endpoint = null; 313 | do { 314 | IPAddress receive_ip = null; 315 | if(!IPAddress.TryParse(config.receive_address, out receive_ip)) { 316 | logt("router", $"Looks like the receive IP Address '{config.receive_address}' is not a valid IP address! No routing will be done.", Log_Type.error); 317 | do_routing = false; 318 | break; 319 | } 320 | 321 | try { 322 | receive_endpoint = new IPEndPoint(receive_ip, config.receive_port); 323 | } 324 | catch(ArgumentOutOfRangeException ex) { 325 | logt("router", $"Looks like the receive port '{config.receive_port}' is not a valid port! No routing will be done.", Log_Type.error); 326 | do_routing = false; 327 | break; 328 | } 329 | 330 | while(true) { 331 | try { 332 | receive_socket.Bind(receive_endpoint); 333 | break; 334 | } 335 | catch(Exception e) { 336 | logt("router", $"Failed to bind the receive socket to {config.receive_address}:{config.receive_port}.\r\nThat port may already be in use by another OSC program, please double-check.\r\n\r\nException info:{e}\r\n\r\nPress any key to retry binding...", Log_Type.error); 337 | Console.Read(); 338 | } 339 | } 340 | } while(false); 341 | 342 | if(vr_system != null) { 343 | logt("vr", "Startig SteamVR background thread."); 344 | var thread = new Thread(vr_thread); 345 | thread.Start(); 346 | } 347 | 348 | if(do_routing) { 349 | logt("router", "Startig OSC routing."); 350 | var buffer_size = 1024 * 4; 351 | var buffer = new byte[buffer_size]; 352 | 353 | while(true) { 354 | 355 | int num_bytes_received = 0; 356 | try { 357 | num_bytes_received = receive_socket.ReceiveFrom(buffer, 0, buffer_size, SocketFlags.None, ref receive_endpoint); 358 | } 359 | catch(Exception ex) { 360 | loud_crash($"Router malfunction! Failed to receive data from {config.receive_address}:{config.receive_port}.\r\n\r\nException info: {ex}\r\n\r\n\r\nThe router will now exit."); 361 | } 362 | 363 | foreach(var end in route_endpoints) { 364 | try { 365 | send_socket.SendTo(buffer, 0, num_bytes_received, SocketFlags.None, end.endpoint); 366 | } 367 | catch(Exception ex) { 368 | loud_crash($"Router malfunction! Failed to send data to {end.address}:{end.port}.\r\n\r\nException info: {ex}\r\n\r\n\r\nThe router will now exit."); 369 | } 370 | } 371 | } 372 | } 373 | else { 374 | MessageBox.Show("Failed to start OSC Routing. Please check the console for more information.", "VRCRouter", MessageBoxButtons.OK, MessageBoxIcon.Warning); 375 | } 376 | } 377 | } 378 | } 379 | -------------------------------------------------------------------------------- /vrcrouter-netf/vrcrouter-ui/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("vrcrouter-ui")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("vrcrouter-ui")] 13 | [assembly: AssemblyCopyright("Copyright © 2023")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("e96e237b-01ef-484a-9da5-f9a6a463cbcd")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /vrcrouter-netf/vrcrouter-ui/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace ValueFactoryVRCRouter.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ValueFactoryVRCRouter.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /vrcrouter-netf/vrcrouter-ui/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /vrcrouter-netf/vrcrouter-ui/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace ValueFactoryVRCRouter.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.5.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /vrcrouter-netf/vrcrouter-ui/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /vrcrouter-netf/vrcrouter-ui/RouteControl.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace ValueFactoryVRCRouter { 2 | public partial class RouteControl { 3 | /// 4 | /// Required designer variable. 5 | /// 6 | private System.ComponentModel.IContainer components = null; 7 | 8 | /// 9 | /// Clean up any resources being used. 10 | /// 11 | /// true if managed resources should be disposed; otherwise, false. 12 | protected override void Dispose(bool disposing) { 13 | if (disposing && (components != null)) { 14 | components.Dispose(); 15 | } 16 | base.Dispose(disposing); 17 | } 18 | 19 | #region Component Designer generated code 20 | 21 | /// 22 | /// Required method for Designer support - do not modify 23 | /// the contents of this method with the code editor. 24 | /// 25 | private void InitializeComponent() { 26 | this.groupBox6 = new System.Windows.Forms.GroupBox(); 27 | this.label2 = new System.Windows.Forms.Label(); 28 | this.label1 = new System.Windows.Forms.Label(); 29 | this.pictureBox1 = new System.Windows.Forms.PictureBox(); 30 | this.label9 = new System.Windows.Forms.Label(); 31 | this.button5 = new System.Windows.Forms.Button(); 32 | this.groupBox6.SuspendLayout(); 33 | ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); 34 | this.SuspendLayout(); 35 | // 36 | // groupBox6 37 | // 38 | this.groupBox6.Controls.Add(this.label2); 39 | this.groupBox6.Controls.Add(this.label1); 40 | this.groupBox6.Controls.Add(this.pictureBox1); 41 | this.groupBox6.Controls.Add(this.label9); 42 | this.groupBox6.Controls.Add(this.button5); 43 | this.groupBox6.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 44 | this.groupBox6.Location = new System.Drawing.Point(0, 0); 45 | this.groupBox6.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); 46 | this.groupBox6.Name = "groupBox6"; 47 | this.groupBox6.Padding = new System.Windows.Forms.Padding(4, 3, 4, 3); 48 | this.groupBox6.Size = new System.Drawing.Size(262, 78); 49 | this.groupBox6.TabIndex = 5; 50 | this.groupBox6.TabStop = false; 51 | this.groupBox6.Text = "VRC Face Tracking"; 52 | this.groupBox6.Enter += new System.EventHandler(this.groupBox6_Enter); 53 | // 54 | // label2 55 | // 56 | this.label2.AutoSize = true; 57 | this.label2.Location = new System.Drawing.Point(113, 52); 58 | this.label2.Name = "label2"; 59 | this.label2.Size = new System.Drawing.Size(79, 15); 60 | this.label2.TabIndex = 5; 61 | this.label2.Text = "127.0.0.1:9023"; 62 | // 63 | // label1 64 | // 65 | this.label1.AutoSize = true; 66 | this.label1.Location = new System.Drawing.Point(7, 52); 67 | this.label1.Name = "label1"; 68 | this.label1.Size = new System.Drawing.Size(100, 15); 69 | this.label1.TabIndex = 4; 70 | this.label1.Text = "Address and Port:"; 71 | // 72 | // pictureBox1 73 | // 74 | this.pictureBox1.Location = new System.Drawing.Point(10, 21); 75 | this.pictureBox1.Name = "pictureBox1"; 76 | this.pictureBox1.Size = new System.Drawing.Size(28, 28); 77 | this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; 78 | this.pictureBox1.TabIndex = 1; 79 | this.pictureBox1.TabStop = false; 80 | // 81 | // label9 82 | // 83 | this.label9.AutoSize = true; 84 | this.label9.Location = new System.Drawing.Point(113, 28); 85 | this.label9.Name = "label9"; 86 | this.label9.Size = new System.Drawing.Size(35, 15); 87 | this.label9.TabIndex = 3; 88 | this.label9.Text = "Idle..."; 89 | // 90 | // button5 91 | // 92 | this.button5.Location = new System.Drawing.Point(44, 21); 93 | this.button5.Name = "button5"; 94 | this.button5.Size = new System.Drawing.Size(63, 28); 95 | this.button5.TabIndex = 0; 96 | this.button5.Text = "Launch"; 97 | this.button5.UseVisualStyleBackColor = true; 98 | // 99 | // RouteControl 100 | // 101 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 102 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 103 | this.Controls.Add(this.groupBox6); 104 | this.Name = "RouteControl"; 105 | this.Size = new System.Drawing.Size(262, 78); 106 | this.groupBox6.ResumeLayout(false); 107 | this.groupBox6.PerformLayout(); 108 | ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); 109 | this.ResumeLayout(false); 110 | 111 | } 112 | 113 | #endregion 114 | 115 | private System.Windows.Forms.GroupBox groupBox6; 116 | private System.Windows.Forms.Label label2; 117 | private System.Windows.Forms.Label label1; 118 | private System.Windows.Forms.PictureBox pictureBox1; 119 | private System.Windows.Forms.Label label9; 120 | private System.Windows.Forms.Button button5; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /vrcrouter-netf/vrcrouter-ui/RouteControl.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | using System.Drawing; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Windows.Forms; 10 | 11 | namespace ValueFactoryVRCRouter { 12 | 13 | [ToolboxItem(true)] 14 | public partial class RouteControl : UserControl { 15 | public RouteControl() { 16 | InitializeComponent(); 17 | } 18 | 19 | private void groupBox6_Enter(object sender, EventArgs e) { 20 | 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /vrcrouter-netf/vrcrouter-ui/RouteControl.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /vrcrouter-netf/vrcrouter-ui/vrcrouter-ui.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {E96E237B-01EF-484A-9DA5-F9A6A463CBCD} 8 | WinExe 9 | ValueFactoryVRCRouter 10 | VRCRouter 11 | v4.8.1 12 | 512 13 | true 14 | true 15 | 16 | 17 | x64 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | x64 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | Form 45 | 46 | 47 | Form1.cs 48 | 49 | 50 | 51 | 52 | UserControl 53 | 54 | 55 | RouteControl.cs 56 | 57 | 58 | Form1.cs 59 | 60 | 61 | ResXFileCodeGenerator 62 | Resources.Designer.cs 63 | Designer 64 | 65 | 66 | True 67 | Resources.resx 68 | True 69 | 70 | 71 | RouteControl.cs 72 | 73 | 74 | SettingsSingleFileGenerator 75 | Settings.Designer.cs 76 | 77 | 78 | True 79 | Settings.settings 80 | True 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | {627a06c1-0cd0-40a5-b3ae-1e86eb8ec9f6} 89 | vrcrouter-common 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /vrcrouter-netf/vrcrouter.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.5.33516.290 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "vrcrouter", "vrcrouter\vrcrouter.csproj", "{A2D5B458-7E52-40B5-BBA5-555B7B37A4F7}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "vrcrouter-common", "vrcrouter-common\vrcrouter-common.csproj", "{627A06C1-0CD0-40A5-B3AE-1E86EB8EC9F6}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "vrcrouter-config", "vrcrouter-config\vrcrouter-config.csproj", "{4021D161-F4A3-462B-B6B3-0A6CD87728EB}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "vrcrouter-ui", "vrcrouter-ui\vrcrouter-ui.csproj", "{E96E237B-01EF-484A-9DA5-F9A6A463CBCD}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Release|Any CPU = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {A2D5B458-7E52-40B5-BBA5-555B7B37A4F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {A2D5B458-7E52-40B5-BBA5-555B7B37A4F7}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {A2D5B458-7E52-40B5-BBA5-555B7B37A4F7}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {A2D5B458-7E52-40B5-BBA5-555B7B37A4F7}.Release|Any CPU.Build.0 = Release|Any CPU 24 | {627A06C1-0CD0-40A5-B3AE-1E86EB8EC9F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {627A06C1-0CD0-40A5-B3AE-1E86EB8EC9F6}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {627A06C1-0CD0-40A5-B3AE-1E86EB8EC9F6}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {627A06C1-0CD0-40A5-B3AE-1E86EB8EC9F6}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {4021D161-F4A3-462B-B6B3-0A6CD87728EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {4021D161-F4A3-462B-B6B3-0A6CD87728EB}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {4021D161-F4A3-462B-B6B3-0A6CD87728EB}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {4021D161-F4A3-462B-B6B3-0A6CD87728EB}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {E96E237B-01EF-484A-9DA5-F9A6A463CBCD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {E96E237B-01EF-484A-9DA5-F9A6A463CBCD}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {E96E237B-01EF-484A-9DA5-F9A6A463CBCD}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {E96E237B-01EF-484A-9DA5-F9A6A463CBCD}.Release|Any CPU.Build.0 = Release|Any CPU 36 | EndGlobalSection 37 | GlobalSection(SolutionProperties) = preSolution 38 | HideSolutionNode = FALSE 39 | EndGlobalSection 40 | GlobalSection(ExtensibilityGlobals) = postSolution 41 | SolutionGuid = {2F1BCE59-4B55-44F3-9A46-F124C61BF79C} 42 | EndGlobalSection 43 | EndGlobal 44 | -------------------------------------------------------------------------------- /vrcrouter-netf/vrcrouter/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /vrcrouter-netf/vrcrouter/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Collections.Generic; 4 | using System.Net; 5 | using System.Net.Sockets; 6 | using Valve.VR; 7 | using System.Runtime.InteropServices; 8 | using ValueFactoryVRCRouterCommon; 9 | using System.Threading; 10 | using System.Text; 11 | using System.IO; 12 | 13 | namespace ValueFactoryVRCRouterCommon { 14 | internal static class Program { 15 | 16 | enum Log_Type { 17 | normal, 18 | warning, 19 | error 20 | } 21 | 22 | static void logt(string type, string message, Log_Type log_type = Log_Type.normal) { 23 | if(log_type == Log_Type.warning) { 24 | var col = Console.ForegroundColor; 25 | Console.ForegroundColor = ConsoleColor.Yellow; 26 | Console.Write($"[{type}] "); 27 | Console.ForegroundColor = col; 28 | } 29 | else if(log_type == Log_Type.error) { 30 | var col = Console.ForegroundColor; 31 | Console.ForegroundColor = ConsoleColor.Red; 32 | Console.Write($"[{type}] "); 33 | Console.ForegroundColor = col; 34 | } 35 | else { 36 | Console.Write($"[{type}] "); 37 | } 38 | Console.WriteLine(message); 39 | } 40 | 41 | static CVRSystem vr_system; 42 | 43 | public static List autoclose_exes = new List(); 44 | 45 | public static void terminate_autostart_apps() { 46 | logt("autostart", "Closing autostarted apps..."); 47 | 48 | foreach(var exe in autoclose_exes) { 49 | Native.terminate_app_by_name(exe); 50 | } 51 | 52 | foreach(var process_id in child_processes) { 53 | Native.terminate_app(process_id); 54 | } 55 | } 56 | 57 | public static void on_exit() { 58 | terminate_autostart_apps(); 59 | } 60 | 61 | readonly static uint VREvent_T_Size = (uint)Marshal.SizeOf(typeof(VREvent_t)); 62 | 63 | public static void vr_thread() { 64 | logt("vr thread", "Started!"); 65 | 66 | VREvent_t ev = new VREvent_t(); 67 | while(true) { 68 | if(vr_system.PollNextEvent(ref ev, VREvent_T_Size)) { 69 | if(ev.eventType == (uint)EVREventType.VREvent_Quit) { 70 | logt("vr thread", "Got VREvent_Quit event, stopping."); 71 | 72 | // NOTE(valuef): terminate_autostart_apps uses resources created on the main thread, but 73 | // we're not going to be writing to them in any way that would cause threading issues, so 74 | // this is okay! 75 | // 2023-05-08 76 | terminate_autostart_apps(); 77 | logt("vr thread", "Exiting VR Thread\n"); 78 | return; 79 | } 80 | } 81 | else { 82 | Thread.Sleep(1000); 83 | } 84 | } 85 | } 86 | 87 | 88 | public static Config.V1 config = new Config.V1(); 89 | public static List child_processes = new List(); 90 | 91 | public struct Route_Endpoint { 92 | public EndPoint endpoint; 93 | public string address; 94 | public int port; 95 | } 96 | 97 | public static List route_endpoints= new List(); 98 | 99 | [DllImport("user32.dll", CharSet = CharSet.Unicode)] 100 | static extern int MessageBox(IntPtr hWnd, String text, String caption, uint type); 101 | const uint MB_ABORTRETRYIGNORE = 0x00000002; 102 | const uint MB_CANCELTRYCONTINUE = 0x00000006; 103 | const uint MB_HELP = 0x00004000; 104 | const uint MB_OK = 0x00000000; 105 | const uint MB_OKCANCEL = 0x00000001; 106 | const uint MB_RETRYCANCEL = 0x00000005; 107 | const uint MB_YESNO = 0x00000004; 108 | const uint MB_YESNOCANCEL = 0x00000003; 109 | 110 | const uint MB_ICONEXCLAMATION = 0x00000030; 111 | const uint MB_ICONWARNING = 0x00000030; 112 | const uint MB_ICONINFORMATION = 0x00000040; 113 | const uint MB_ICONASTERISK = 0x00000040; 114 | const uint MB_ICONQUESTION = 0x00000020; 115 | const uint MB_ICONSTOP = 0x00000010; 116 | const uint MB_ICONERROR = 0x00000010; 117 | const uint MB_ICONHAND = 0x00000010; 118 | 119 | static void msg_box(string text, uint type = MB_OK | MB_ICONWARNING) { 120 | MessageBox(new IntPtr(0), text, "VRCRouter", type); 121 | } 122 | 123 | static void loud_crash(string text) { 124 | msg_box(text, MB_OK | MB_ICONERROR); 125 | Environment.Exit(1); 126 | } 127 | 128 | [DllImport("Kernel32")] 129 | static extern bool SetConsoleCtrlHandler(EventHandler handler, bool add); 130 | 131 | delegate bool EventHandler(CtrlType sig); 132 | static EventHandler exit_handler; 133 | 134 | enum CtrlType { 135 | CTRL_C_EVENT = 0, 136 | CTRL_BREAK_EVENT = 1, 137 | CTRL_CLOSE_EVENT = 2, 138 | CTRL_LOGOFF_EVENT = 5, 139 | CTRL_SHUTDOWN_EVENT = 6 140 | } 141 | 142 | 143 | [STAThread] 144 | static void Main(string[] args) { 145 | try { 146 | wrapped_main(args); 147 | } 148 | catch(Exception ex) { 149 | msg_box($"Uh oh! We've encountered an unhandled error. Please let us know about this at discord.shader.gay\r\n\r\n\r\n{ex}\r\n\r\nThe router will now exit.", MB_OK | MB_ICONERROR); 150 | Process.Start(new ProcessStartInfo() { FileName = "https://discord.shader.gay", UseShellExecute = true }); 151 | } 152 | } 153 | 154 | static void wrapped_main(string[] args) { 155 | exit_handler += (s) => { on_exit(); return true; }; 156 | SetConsoleCtrlHandler(exit_handler, true); 157 | Console.CancelKeyPress += (s, e) => { on_exit(); }; 158 | AppDomain.CurrentDomain.ProcessExit += (s, e) => { on_exit(); }; 159 | 160 | Console.WriteLine(""); 161 | Console.WriteLine($"--> Starting VRCRouter build {Build.build_version}, date {Build.build_date}"); 162 | Console.WriteLine($"--> ValueFactory https://shader.gay"); 163 | Console.WriteLine("------------------------------------"); 164 | Console.WriteLine(""); 165 | 166 | do { 167 | 168 | var vr_err = EVRInitError.None; 169 | try { 170 | vr_system = OpenVR.Init(ref vr_err, EVRApplicationType.VRApplication_Background); 171 | } 172 | catch(Exception ex) { 173 | logt("vr", $"VR_Init failed due to an exception: {ex}. SteamVR initialization stopped. The router will still function.", Log_Type.error); 174 | break; 175 | } 176 | 177 | if(vr_system == null) { 178 | var err_desc = OpenVR.GetStringForHmdError(vr_err); 179 | logt("vr", $"VR_Init failed: {err_desc}. SteamVR initialization stopped. The router will still function.", Log_Type.warning); 180 | break; 181 | } 182 | 183 | logt("vr", "SteamVR setup done!"); 184 | } while(false); 185 | 186 | if(File.Exists(Config.FILE)) { 187 | var new_config = Config.try_load_config(Config.FILE, out var load_ex, out var load_error); 188 | if(new_config == null) { 189 | logt("config", $"Failed to load config. Using defaults. Error message: {load_error}", Log_Type.warning); 190 | if(load_ex != null) { 191 | logt("config", $"Exception: {load_ex}", Log_Type.warning); 192 | } 193 | } 194 | else { 195 | config = new_config; 196 | } 197 | } 198 | else { 199 | logt("config", $"Config file not found ({Config.FILE}). Using defaults.", Log_Type.warning); 200 | } 201 | 202 | if(config.wait_for_vrchat) { 203 | logt("vrchat", "Wait for VRChat is enabled. Waiting for VRChat to start before launching autostart apps and routing...", Log_Type.warning); 204 | var result = Native.wait_for_vrchat_to_start(); 205 | if(result) { 206 | logt("vrchat", "VRChat executable found! Continuing."); 207 | } 208 | else { 209 | logt("vrchat", "An error occurred while waiting for VRChat to start. Stopping waiting and continuing.", Log_Type.error); 210 | } 211 | } 212 | 213 | do { 214 | 215 | var files = Route.get_routes_folder_contents(out var routes_folder_err); 216 | if(routes_folder_err != null) { 217 | logt("route", routes_folder_err, Log_Type.error); 218 | break; 219 | } 220 | 221 | foreach(var file in files) { 222 | logt("route", $"Loading route {file}"); 223 | 224 | var route = Route.try_load_route(file, out var load_ex, out var load_error); 225 | 226 | if(route == null) { 227 | logt("route", load_error, Log_Type.error); 228 | if(load_ex != null) { 229 | logt("route", $"Exception: {load_ex}", Log_Type.error); 230 | } 231 | continue; 232 | } 233 | 234 | if(!route.enabled) { 235 | logt("route", "Route disabled, skipping.", Log_Type.warning); 236 | continue; 237 | } 238 | 239 | if(string.IsNullOrWhiteSpace(route.autostart_path)) { 240 | logt("route", "Doesn't specify autostart-path, no app will be started.", Log_Type.warning); 241 | } 242 | else { 243 | var data = Common.prep_autostart_data(route, out var error); 244 | if(error != null) { 245 | logt("route", error, Log_Type.warning); 246 | // NOTE(valuef): We still continue even if we errored as that function should give us valid autostart data all the time 247 | // 2023-06-21 248 | } 249 | 250 | var result = new Native.Autostart_App_Result(); 251 | Native.launch_autostart_app(new StringBuilder(data.full_path), new StringBuilder(data.args), new StringBuilder(data.working_dir), ref result); 252 | 253 | if(result.success) { 254 | child_processes.Add(result.id); 255 | logt("route", $"Started app {route.autostart_path} {data.args}"); 256 | } 257 | else { 258 | logt("route", $"Failed to start the autostart app at path {data.full_path} with args {data.args} and working directory {data.working_dir}. Error code: {result.error}"); 259 | } 260 | } 261 | 262 | if(!string.IsNullOrWhiteSpace(route.autoclose_executable_name)) { 263 | autoclose_exes.Add(route.autoclose_executable_name); 264 | logt("route", $"Registered an executable to autoclose: {route.autoclose_executable_name}"); 265 | } 266 | 267 | if(!route.routing_enabled) { 268 | logt("route", "OSC Routing has been explicitly disabled on this route.", Log_Type.warning); 269 | } 270 | else if(string.IsNullOrWhiteSpace(route.output_address)) { 271 | logt("route", "Doesn't specify output-address, no routing will be done.", Log_Type.warning); 272 | } 273 | else if(route.output_port <= 0) { 274 | logt("route", "Doesn't specify a valid output-port, no routing will be done.", Log_Type.warning); 275 | } 276 | else { 277 | IPAddress ip; 278 | if (!IPAddress.TryParse(route.output_address, out ip)) { 279 | logt("route", $"The address '{route.output_address}' is invalid! No routing will be done for this route.", Log_Type.error); 280 | } 281 | else { 282 | EndPoint endpoint = null; 283 | try { 284 | endpoint = new IPEndPoint(ip, route.output_port); 285 | } 286 | catch(ArgumentOutOfRangeException ex) { 287 | logt("route", $"Looks like the port '{route.output_port}' is not a valid port! No routing will be done for this route.", Log_Type.error); 288 | } 289 | 290 | if(endpoint != null) { 291 | var end = new Route_Endpoint(); 292 | end.endpoint = endpoint; 293 | end.address = route.output_address; 294 | end.port = route.output_port; 295 | route_endpoints.Add(end); 296 | logt("route", $"Registered route output to {route.output_address}:{route.output_port}"); 297 | } 298 | } 299 | } 300 | } 301 | 302 | } while(false); 303 | 304 | var do_routing = true; 305 | 306 | var receive_socket = new Socket(SocketType.Dgram, ProtocolType.Udp); 307 | receive_socket.ReceiveTimeout = 0; 308 | var send_socket = new Socket(SocketType.Dgram, ProtocolType.Udp); 309 | 310 | EndPoint receive_endpoint = null; 311 | do { 312 | IPAddress receive_ip = null; 313 | if(!IPAddress.TryParse(config.receive_address, out receive_ip)) { 314 | logt("router", $"Looks like the receive IP Address '{config.receive_address}' is not a valid IP address! No routing will be done.", Log_Type.error); 315 | do_routing = false; 316 | break; 317 | } 318 | 319 | try { 320 | receive_endpoint = new IPEndPoint(receive_ip, config.receive_port); 321 | } 322 | catch(ArgumentOutOfRangeException ex) { 323 | logt("router", $"Looks like the receive port '{config.receive_port}' is not a valid port! No routing will be done.", Log_Type.error); 324 | do_routing = false; 325 | break; 326 | } 327 | 328 | while(true) { 329 | try { 330 | receive_socket.Bind(receive_endpoint); 331 | break; 332 | } 333 | catch(Exception e) { 334 | logt("router", $"Failed to bind the receive socket to {config.receive_address}:{config.receive_port}.\r\nThat port may already be in use by another OSC program, please double-check.\r\n\r\nException info:{e}\r\n\r\nPress any key to retry binding...", Log_Type.error); 335 | Console.Read(); 336 | } 337 | } 338 | } while(false); 339 | 340 | if(vr_system != null) { 341 | logt("vr", "Startig SteamVR background thread."); 342 | var thread = new Thread(vr_thread); 343 | thread.Start(); 344 | } 345 | 346 | if(do_routing) { 347 | logt("router", "Startig OSC routing."); 348 | var buffer_size = 1024 * 4; 349 | var buffer = new byte[buffer_size]; 350 | 351 | while(true) { 352 | 353 | int num_bytes_received = 0; 354 | try { 355 | num_bytes_received = receive_socket.ReceiveFrom(buffer, 0, buffer_size, SocketFlags.None, ref receive_endpoint); 356 | } 357 | catch(Exception ex) { 358 | loud_crash($"Router malfunction! Failed to receive data from {config.receive_address}:{config.receive_port}.\r\n\r\nException info: {ex}\r\n\r\n\r\nThe router will now exit."); 359 | } 360 | 361 | foreach(var end in route_endpoints) { 362 | try { 363 | send_socket.SendTo(buffer, 0, num_bytes_received, SocketFlags.None, end.endpoint); 364 | } 365 | catch(Exception ex) { 366 | loud_crash($"Router malfunction! Failed to send data to {end.address}:{end.port}.\r\n\r\nException info: {ex}\r\n\r\n\r\nThe router will now exit."); 367 | } 368 | } 369 | } 370 | } 371 | else { 372 | msg_box("Failed to start OSC Routing. Please check the console for more information.", MB_OK | MB_ICONWARNING); 373 | } 374 | } 375 | } 376 | } 377 | -------------------------------------------------------------------------------- /vrcrouter-netf/vrcrouter/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("vrcrouter")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("vrcrouter")] 13 | [assembly: AssemblyCopyright("Copyright © 2023")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("a2d5b458-7e52-40b5-bba5-555b7b37a4f7")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /vrcrouter-netf/vrcrouter/Properties/app.manifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 50 | 58 | 59 | 73 | -------------------------------------------------------------------------------- /vrcrouter-netf/vrcrouter/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/valuef/VRCRouter/4aab0bd8c374e8f883d0a46b844b9c4f6d11519e/vrcrouter-netf/vrcrouter/icon.ico -------------------------------------------------------------------------------- /vrcrouter-netf/vrcrouter/vrcrouter.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {A2D5B458-7E52-40B5-BBA5-555B7B37A4F7} 8 | Exe 9 | ValueFactoryVRCRouter 10 | VRCRouter 11 | v4.8.1 12 | 512 13 | true 14 | true 15 | false 16 | publish\ 17 | true 18 | Disk 19 | false 20 | Foreground 21 | 7 22 | Days 23 | false 24 | false 25 | true 26 | 0 27 | 1.0.0.%2a 28 | false 29 | true 30 | 31 | 32 | x64 33 | true 34 | full 35 | false 36 | bin\Debug\ 37 | DEBUG;TRACE 38 | prompt 39 | 4 40 | 41 | 42 | x64 43 | pdbonly 44 | true 45 | bin\Release\ 46 | TRACE 47 | prompt 48 | 4 49 | 50 | 51 | Internet 52 | 53 | 54 | false 55 | 56 | 57 | Properties\app.manifest 58 | 59 | 60 | icon.ico 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | False 79 | Microsoft .NET Framework 4.8.1 %28x86 and x64%29 80 | true 81 | 82 | 83 | False 84 | .NET Framework 3.5 SP1 85 | false 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | {627a06c1-0cd0-40a5-b3ae-1e86eb8ec9f6} 94 | vrcrouter-common 95 | 96 | 97 | 98 | --------------------------------------------------------------------------------