Skip to content

Commit

Permalink
Reverted new package layout (can't have nice things)
Browse files Browse the repository at this point in the history
  • Loading branch information
DaXcess committed May 31, 2024
1 parent 271b110 commit bf5eceb
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 44 deletions.
1 change: 0 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
- Added VR interactions to the big doors on Artiface

**Changes**:
- Changed the mod package layout, dropping the `RuntimeDeps` in favor of `package`
- Reworked the OpenXR loader, which will now attempt every runtime instead of only the default/preconfigured runtime
- Moved startup logic to a prefix, fixing an issue where occasionally the camera would be black when loading in

Expand Down
23 changes: 23 additions & 0 deletions Properties/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions Properties/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -122,4 +122,21 @@
<value>..\Resources\lethalcompanyvr;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="UnitySubsystemsManifest" xml:space="preserve">
<value>{
"name": "OpenXR XR Plugin",
"version": "1.8.2",
"libraryName": "UnityOpenXR",
"displays": [
{
"id": "OpenXR Display"
}
],
"inputs": [
{
"id": "OpenXR Input"
}
]
}</value>
</data>
</root>
136 changes: 93 additions & 43 deletions Source/Plugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
using System.Collections;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Reflection;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.Rendering;
Expand Down Expand Up @@ -51,9 +51,6 @@ private void Awake()
// configurations runs after the Input System has already been initialized
InputSystem.PerformDefaultPluginInitialization();

// Force the XR Interaction Toolkit assembly to load before we load asset bundles
_ = TrackedDeviceGraphicRaycaster.s_Corners;

// Plugin startup logic
LCVR.Logger.SetSource(Logger);

Expand All @@ -62,16 +59,6 @@ private void Awake()

Logger.LogInfo($"Plugin {PLUGIN_NAME} is starting...");

// Extract LCVR dependencies
if (!ExtractPackage(out var mustRestart))
{
Logger.LogError("Failed to extract LCVR dependencies, disabling mod");
return;
}

if (mustRestart)
Flags |= Flags.RestartRequired;

// Allow disabling VR via config and command line
var disableVr = Config.DisableVR.Value ||
Environment.GetCommandLineArgs().Contains("--disable-vr", StringComparer.OrdinalIgnoreCase);
Expand Down Expand Up @@ -118,6 +105,12 @@ private void Awake()
return;
}
}

if (!LoadEarlyRuntimeDependencies())
{
Logger.LogError("Disabling mod because required runtime dependencies could not be loaded!");
return;
}

if (!AssetManager.LoadAssets())
{
Expand Down Expand Up @@ -149,56 +142,113 @@ private bool VerifyGameVersion()
return GAME_ASSEMBLY_HASHES.Contains(shasum);
}

/// <summary>
/// Verifies and extracts the LCVR dependencies (if necessary), returning whether the game needs to restart
/// </summary>
private bool ExtractPackage(out bool mustRestart)
private bool LoadEarlyRuntimeDependencies()
{
mustRestart = false;

try
{
var basePath = Path.Combine(Paths.GameRootPath, "Lethal Company_Data");
using var zip = ZipFile.OpenRead(Path.Combine(Info.Location, "package"));
var deps = Path.Combine(Path.GetDirectoryName(Info.Location)!, "RuntimeDeps");

foreach (var entry in zip.Entries.Where(entry =>
!entry.FullName.EndsWith('/') || !string.IsNullOrEmpty(entry.Name)))
foreach (var file in Directory.GetFiles(deps, "*.dll"))
{
var fullPath = Path.Combine(basePath, entry.FullName);
var directoryName = Path.GetDirectoryName(fullPath)!;
var filename = Path.GetFileName(file);

if (!Directory.Exists(directoryName))
{
Directory.CreateDirectory(directoryName);
mustRestart = true;
}

using var stream = entry.Open();
using var reader = new BinaryReader(stream);

var bytes = reader.ReadBytes((int)entry.Length);
// Ignore known unmanaged libraries
if (filename == "UnityOpenXR.dll" || filename == "openxr_loader.dll")
continue;

// Check if file is up-to-date
if (File.Exists(fullPath) &&
Utils.ComputeHash(bytes).SequenceEqual(Utils.ComputeHash(File.ReadAllBytes(fullPath)))) continue;
Logger.LogDebug($"Early loading {filename}");

File.WriteAllBytes(fullPath, bytes);

mustRestart = true;
try
{
Assembly.LoadFile(file);
}
catch (Exception ex)
{
Logger.LogWarning($"Failed to early load {filename}: {ex.Message}");
}
}
}
catch (Exception ex)
{
Logger.LogError($"Failed to validate and extract LCVR package: {ex.Message}");
Logger.LogError($"Unexpected error occured while loading early runtime dependencies (incorrect folder structure?): {ex.Message}");
return false;
}


return true;
}

/// <summary>
/// Helper function for SetupRuntimeAssets() to copy resource files and return false if the source does not exist
/// </summary>
private bool CopyResourceFile(string sourceFile, string destinationFile)
{
if (!File.Exists(sourceFile))
return false;

if (File.Exists(destinationFile))
{
var sourceHash = Utils.ComputeHash(File.ReadAllBytes(sourceFile));
var destHash = Utils.ComputeHash(File.ReadAllBytes(destinationFile));

if (sourceHash.SequenceEqual(destHash))
return true;
}

File.Copy(sourceFile, destinationFile, true);

return true;
}

/// <summary>
/// Place required runtime libraries and configuration in the game files to allow VR to be started
/// </summary>
private bool SetupRuntimeAssets()
{
var mustRestart = false;

var root = Path.Combine(Paths.GameRootPath, "Lethal Company_Data");
var subsystems = Path.Combine(root, "UnitySubsystems");
if (!Directory.Exists(subsystems))
Directory.CreateDirectory(subsystems);

var openXr = Path.Combine(subsystems, "UnityOpenXR");
if (!Directory.Exists(openXr))
Directory.CreateDirectory(openXr);

var manifest = Path.Combine(openXr, "UnitySubsystemsManifest.json");
if (!File.Exists(manifest))
{
File.WriteAllText(manifest, Properties.Resources.UnitySubsystemsManifest);
mustRestart = true;
}

var plugins = Path.Combine(root, "Plugins");
var uoxrTarget = Path.Combine(plugins, "UnityOpenXR.dll");
var oxrLoaderTarget = Path.Combine(plugins, "openxr_loader.dll");

var current = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var uoxr = Path.Combine(current, "RuntimeDeps/UnityOpenXR.dll");
var oxrLoader = Path.Combine(current, "RuntimeDeps/openxr_loader.dll");

if (!CopyResourceFile(uoxr, uoxrTarget))
Logger.LogWarning("Could not find UnityOpenXR.dll to copy to the game, VR might not work!");

if (!CopyResourceFile(oxrLoader, oxrLoaderTarget))
Logger.LogWarning("Could not find openxr_loader.dll to copy to the game, VR might not work!");

return mustRestart;
}

private bool InitializeVR()
{
Logger.LogInfo("Loading VR...");

if (SetupRuntimeAssets())
{
Logger.LogWarning("You might have to restart the game to allow VR to function properly");
Flags |= Flags.RestartRequired;
}

if (!OpenXR.Loader.InitializeXR())
{
Expand Down

0 comments on commit bf5eceb

Please sign in to comment.