From 3da23e8c4ab34a7a2efa8d0f0c4487f7aa3d0cc7 Mon Sep 17 00:00:00 2001 From: Measurity Date: Sat, 9 Oct 2021 17:39:01 +0200 Subject: [PATCH] Improved Nitrox build process (#1469) * Improved Nitrox build process - Automatically searches for Subnautica installation on build and caches the result. - Generates publicized assemblies from game files and references them automatically. * Added default logger if no Log.Setup is done --- .gitignore | 3 - DevVars.targets.example | 7 - Directory.Build.props | 30 +++- Directory.Build.targets | 155 ++++++++++-------- Nitrox.BuildTool/Nitrox.BuildTool.csproj | 38 +++++ Nitrox.BuildTool/Program.cs | 96 +++++++++++ Nitrox.BuildTool/Publicizer.cs | 124 ++++++++++++++ .../Nitrox.Subnautica.Assets.shproj | 4 +- Nitrox.sln | 7 +- NitroxClient/NitroxClient.csproj | 6 +- NitroxLauncher/NitroxLauncher.csproj | 4 +- .../NitroxModel-Subnautica.csproj | 2 +- NitroxModel/Discovery/GameInstallData.cs | 93 +++++++++++ .../Discovery/GameInstallationFinder.cs | 8 +- NitroxModel/Logger/Log.cs | 7 +- NitroxModel/NitroxModel.csproj | 1 + .../NitroxServer-Subnautica.csproj | 2 +- NitroxServer/NitroxServer.csproj | 2 +- NitroxTest/NitroxTest.csproj | 6 +- SharedConfig.targets | 37 ----- 20 files changed, 487 insertions(+), 145 deletions(-) delete mode 100644 DevVars.targets.example create mode 100644 Nitrox.BuildTool/Nitrox.BuildTool.csproj create mode 100644 Nitrox.BuildTool/Program.cs create mode 100644 Nitrox.BuildTool/Publicizer.cs create mode 100644 NitroxModel/Discovery/GameInstallData.cs delete mode 100644 SharedConfig.targets diff --git a/.gitignore b/.gitignore index a0299529ec..72de34f1ea 100644 --- a/.gitignore +++ b/.gitignore @@ -3,9 +3,6 @@ ## ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore -# Nitrox-specific files -DevVars.targets - # User-specific files *.rsuser *.suo diff --git a/DevVars.targets.example b/DevVars.targets.example deleted file mode 100644 index 2d9aeb8e49..0000000000 --- a/DevVars.targets.example +++ /dev/null @@ -1,7 +0,0 @@ - - - - - C:\Program Files\Epic Games\Subnautica - - \ No newline at end of file diff --git a/Directory.Build.props b/Directory.Build.props index 09827326dc..a212013c34 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,5 +1,5 @@ - + v4.7.2 @@ -7,6 +7,13 @@ false false false + $(SolutionDir)Nitrox.BuildTool\bin\ + $(BuildToolDir)generated_files\ + $(BuildGenDir)publicized_assemblies\ + AnyCPU + prompt + 4 + true TRACE;DEBUG @@ -27,26 +34,41 @@ true - + true true - + - + Properties\AssemblyInfoCommon.cs + + + + + {15C4C9C4-683C-4EF0-9E0F-0664A3BDA0CE} + Nitrox.BuildTool + + {B16F4DE7-21AD-4FEF-955B-0A5A365FA4E3} NitroxModel + + + + + + + diff --git a/Directory.Build.targets b/Directory.Build.targets index 9d37423945..8cae6b8ccb 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -1,272 +1,287 @@ - - - + .netstandard2.0 - + - - $(SubnauticaManaged)\Unity.Timeline.dll + $(GameManagedDir)\Unity.Timeline.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.dll + $(GameManagedDir)\UnityEngine.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.AccessibilityModule.dll + $(GameManagedDir)\UnityEngine.AccessibilityModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.AIModule.dll + $(GameManagedDir)\UnityEngine.AIModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.AndroidJNIModule.dll + $(GameManagedDir)\UnityEngine.AndroidJNIModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.AnimationModule.dll + $(GameManagedDir)\UnityEngine.AnimationModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.ARModule.dll + $(GameManagedDir)\UnityEngine.ARModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.AssetBundleModule.dll + $(GameManagedDir)\UnityEngine.AssetBundleModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.AudioModule.dll + $(GameManagedDir)\UnityEngine.AudioModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.ClothModule.dll + $(GameManagedDir)\UnityEngine.ClothModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.ClusterInputModule.dll + $(GameManagedDir)\UnityEngine.ClusterInputModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.ClusterRendererModule.dll + $(GameManagedDir)\UnityEngine.ClusterRendererModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.CoreModule.dll + $(GameManagedDir)\UnityEngine.CoreModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.CrashReportingModule.dll + $(GameManagedDir)\UnityEngine.CrashReportingModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.DirectorModule.dll + $(GameManagedDir)\UnityEngine.DirectorModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.DSPGraphModule.dll + $(GameManagedDir)\UnityEngine.DSPGraphModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.FileSystemHttpModule.dll + $(GameManagedDir)\UnityEngine.FileSystemHttpModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.GameCenterModule.dll + $(GameManagedDir)\UnityEngine.GameCenterModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.GridModule.dll + $(GameManagedDir)\UnityEngine.GridModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.HotReloadModule.dll + $(GameManagedDir)\UnityEngine.HotReloadModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.ImageConversionModule.dll + $(GameManagedDir)\UnityEngine.ImageConversionModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.IMGUIModule.dll + $(GameManagedDir)\UnityEngine.IMGUIModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.InputLegacyModule.dll + $(GameManagedDir)\UnityEngine.InputLegacyModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.InputModule.dll + $(GameManagedDir)\UnityEngine.InputModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.JSONSerializeModule.dll + $(GameManagedDir)\UnityEngine.JSONSerializeModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.LocalizationModule.dll + $(GameManagedDir)\UnityEngine.LocalizationModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.ParticleSystemModule.dll + $(GameManagedDir)\UnityEngine.ParticleSystemModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.PerformanceReportingModule.dll + $(GameManagedDir)\UnityEngine.PerformanceReportingModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.Physics2DModule.dll + $(GameManagedDir)\UnityEngine.Physics2DModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.PhysicsModule.dll + $(GameManagedDir)\UnityEngine.PhysicsModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.ProfilerModule.dll + $(GameManagedDir)\UnityEngine.ProfilerModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.ScreenCaptureModule.dll + $(GameManagedDir)\UnityEngine.ScreenCaptureModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.SharedInternalsModule.dll + $(GameManagedDir)\UnityEngine.SharedInternalsModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.SpriteMaskModule.dll + $(GameManagedDir)\UnityEngine.SpriteMaskModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.SpriteShapeModule.dll + $(GameManagedDir)\UnityEngine.SpriteShapeModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.StreamingModule.dll + $(GameManagedDir)\UnityEngine.StreamingModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.SubstanceModule.dll + $(GameManagedDir)\UnityEngine.SubstanceModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.TerrainModule.dll + $(GameManagedDir)\UnityEngine.TerrainModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.TerrainPhysicsModule.dll + $(GameManagedDir)\UnityEngine.TerrainPhysicsModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.TextCoreModule.dll + $(GameManagedDir)\UnityEngine.TextCoreModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.TextRenderingModule.dll + $(GameManagedDir)\UnityEngine.TextRenderingModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.TilemapModule.dll + $(GameManagedDir)\UnityEngine.TilemapModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.TLSModule.dll + $(GameManagedDir)\UnityEngine.TLSModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.UI.dll + $(GameManagedDir)\UnityEngine.UI.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.UIElementsModule.dll + $(GameManagedDir)\UnityEngine.UIElementsModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.UIModule.dll + $(GameManagedDir)\UnityEngine.UIModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.UmbraModule.dll + $(GameManagedDir)\UnityEngine.UmbraModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.UNETModule.dll + $(GameManagedDir)\UnityEngine.UNETModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.UnityAnalyticsModule.dll + $(GameManagedDir)\UnityEngine.UnityAnalyticsModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.UnityConnectModule.dll + $(GameManagedDir)\UnityEngine.UnityConnectModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.UnityTestProtocolModule.dll + $(GameManagedDir)\UnityEngine.UnityTestProtocolModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.UnityWebRequestAssetBundleModule.dll + $(GameManagedDir)\UnityEngine.UnityWebRequestAssetBundleModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.UnityWebRequestAudioModule.dll + $(GameManagedDir)\UnityEngine.UnityWebRequestAudioModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.UnityWebRequestModule.dll + $(GameManagedDir)\UnityEngine.UnityWebRequestModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.UnityWebRequestTextureModule.dll + $(GameManagedDir)\UnityEngine.UnityWebRequestTextureModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.UnityWebRequestWWWModule.dll + $(GameManagedDir)\UnityEngine.UnityWebRequestWWWModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.VehiclesModule.dll + $(GameManagedDir)\UnityEngine.VehiclesModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.VFXModule.dll + $(GameManagedDir)\UnityEngine.VFXModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.VideoModule.dll + $(GameManagedDir)\UnityEngine.VideoModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.VRModule.dll + $(GameManagedDir)\UnityEngine.VRModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.WindModule.dll + $(GameManagedDir)\UnityEngine.WindModule.dll $(TestLibrary) - $(SubnauticaManaged)\UnityEngine.XRModule.dll + $(GameManagedDir)\UnityEngine.XRModule.dll $(TestLibrary) + + + + + + + - $(SubnauticaManaged)\Assembly-CSharp.dll + $(BuildGenDllDir)\Assembly-CSharp.dll + $(TestLibrary) + + + $(BuildGenDllDir)\Assembly-CSharp-firstpass.dll + $(TestLibrary) + + + + + + + $(GameManagedDir)\Assembly-CSharp.dll $(TestLibrary) - - $(SubnauticaManaged)\Assembly-CSharp-firstpass.dll + $(GameManagedDir)\Assembly-CSharp-firstpass.dll $(TestLibrary) diff --git a/Nitrox.BuildTool/Nitrox.BuildTool.csproj b/Nitrox.BuildTool/Nitrox.BuildTool.csproj new file mode 100644 index 0000000000..55f878f4ba --- /dev/null +++ b/Nitrox.BuildTool/Nitrox.BuildTool.csproj @@ -0,0 +1,38 @@ + + + + + Debug + AnyCPU + {15C4C9C4-683C-4EF0-9E0F-0664A3BDA0CE} + Exe + Properties + BuildTool + Nitrox.BuildTool + 512 + + bin\ + true + full + false + + + + + + + + + + + + + + 0.11.3 + + + + + + + diff --git a/Nitrox.BuildTool/Program.cs b/Nitrox.BuildTool/Program.cs new file mode 100644 index 0000000000..0e8513217a --- /dev/null +++ b/Nitrox.BuildTool/Program.cs @@ -0,0 +1,96 @@ +using System; +using System.IO; +using System.Reflection; +using System.Threading.Tasks; +using NitroxModel.Discovery; + +namespace BuildTool +{ + /// + /// Entry point of the build automation project. + /// 1. Search for Subnautica install. + /// 2. Publicize the .NET dlls and persist for subsequent Nitrox builds. + /// + public static class Program + { + private static readonly Lazy processDir = + new(() => Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location ?? Directory.GetCurrentDirectory())); + + public static string ProcessDir => processDir.Value; + + public static string GeneratedOutputDir => Path.Combine(ProcessDir, "generated_files"); + + public static async Task Main(string[] args) + { + AppDomain.CurrentDomain.UnhandledException += (sender, eventArgs) => + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine(eventArgs.ExceptionObject); + Console.ResetColor(); + + Exit((eventArgs.ExceptionObject as Exception)?.HResult ?? 1); + }; + + GameInstallData game = await Task.Factory.StartNew(EnsureGame).ConfigureAwait(false); + Console.WriteLine($"Found game at {game.InstallDir}"); + await Task.Factory.StartNew(() => EnsurePublicizedAssemblies(game)).ConfigureAwait(false); + + Exit(); + } + + private static void Exit(int exitCode = 0) + { + Console.WriteLine(); + Console.WriteLine("Press any key to continue . . ."); + Console.ReadKey(true); + Environment.Exit(exitCode); + } + + private static GameInstallData EnsureGame() + { + static bool ValidateUnityGame(GameInstallData game, out string error) + { + if (!File.Exists(Path.Combine(game.InstallDir, "UnityPlayer.dll"))) + { + error = $"Game at: '{game.InstallDir}' is not a Unity game"; + return false; + } + if (!Directory.Exists(game.ManagedDllsDir)) + { + error = $"Invalid Unity managed DLLs directory: {game.ManagedDllsDir}"; + return false; + } + + error = null; + return true; + } + + string cacheFile = Path.Combine(GeneratedOutputDir, "game.props"); + if (GameInstallData.TryFrom(cacheFile, out GameInstallData game) && !ValidateUnityGame(game, out string error)) + { + throw new Exception(error); + } + + game ??= new GameInstallData(GameInstallationFinder.Instance.FindGame()); + game.TrySave(cacheFile); + return game; + } + + private static void EnsurePublicizedAssemblies(GameInstallData game) + { + if (Directory.Exists(Path.Combine(GeneratedOutputDir, "publicized_assemblies"))) + { + Console.WriteLine("Assemblies are already publicized."); + return; + } + + string[] dllsToPublicize = Directory.GetFiles(game.ManagedDllsDir, "Assembly-*.dll"); + foreach (string publicizedDll in Publicizer.Execute(dllsToPublicize, + "", + Path.Combine(GeneratedOutputDir, "publicized_assemblies"))) + { + Console.WriteLine($"Publicized dll: {publicizedDll}"); + } + } + } +} \ No newline at end of file diff --git a/Nitrox.BuildTool/Publicizer.cs b/Nitrox.BuildTool/Publicizer.cs new file mode 100644 index 0000000000..c59e79a0ec --- /dev/null +++ b/Nitrox.BuildTool/Publicizer.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using Mono.Cecil; + +namespace BuildTool +{ + public static class Publicizer + { + public static IEnumerable Execute(IEnumerable files, string outputSuffix = "", string outputPath = null) + { + // Ensure target directory exists. + if (string.IsNullOrWhiteSpace(outputPath)) + { + outputPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + } + if (!string.IsNullOrWhiteSpace(outputPath)) + { + Directory.CreateDirectory(outputPath); + } + + // Create dependency resolve for cecil (needed to write dlls that have other dependencies). + DefaultAssemblyResolver resolver = new(); + + foreach (string file in files) + { + if (!File.Exists(file)) + { + throw new FileNotFoundException("Dll to publicize not found", file); + } + resolver.AddSearchDirectory(Path.GetDirectoryName(file)); + + string outputName = $"{Path.GetFileNameWithoutExtension(file)}{outputSuffix}{Path.GetExtension(file)}"; + string outputFile = Path.Combine(outputPath, outputName); + Publicize(file, resolver).Write(outputFile); + yield return outputFile; + } + } + + public static IEnumerable FilterBackingEventFields(List allTypes) + { + List eventNames = allTypes.SelectMany(t => t.Events) + .Select(eventDefinition => eventDefinition.Name) + .ToList(); + + return allTypes.SelectMany(x => x.Fields) + .Where(fieldDefinition => !eventNames.Contains(fieldDefinition.Name)); + } + + /// + /// Method which returns all Types of the given module, including nested ones (recursively). + /// + /// .NET module to search through for types. + /// Types found in module. + public static IEnumerable GetAllTypes(ModuleDefinition moduleDefinition) => GetAllNestedTypes(moduleDefinition.Types); + + private static AssemblyDefinition Publicize(string file, BaseAssemblyResolver dllResolver) + { + AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly(file, + new ReaderParameters + { + AssemblyResolver = dllResolver + }); + List allTypes = GetAllTypes(assembly.MainModule).ToList(); + foreach (TypeDefinition type in allTypes) + { + if (type == null) + { + continue; + } + + // Publicize type and nested types. + if (!type.IsPublic || !type.IsNestedPublic) + { + if (type.IsNested) + { + type.IsNestedPublic = true; + } + else + { + type.IsPublic = true; + } + } + // Publicize methods on type. + foreach (MethodDefinition method in type.Methods) + { + if (!method?.IsPublic ?? false) + { + method.IsPublic = true; + } + } + } + + // Publicize all fields (excludes fields if they would cause name conflicts on a type). + foreach (FieldDefinition field in FilterBackingEventFields(allTypes)) + { + if (!field?.IsPublic ?? false) + { + field.IsPublic = true; + } + } + + return assembly; + } + + /// + /// Recursive method to get all nested types. Use + /// + /// + /// + private static IEnumerable GetAllNestedTypes(IEnumerable typeDefinitions) + { + IEnumerable defs = typeDefinitions as TypeDefinition[] ?? typeDefinitions.ToArray(); + if (!defs.Any()) + { + return Array.Empty(); + } + + return defs.Concat(GetAllNestedTypes(defs.SelectMany(t => t.NestedTypes))); + } + } +} diff --git a/Nitrox.Subnautica.Assets/Nitrox.Subnautica.Assets.shproj b/Nitrox.Subnautica.Assets/Nitrox.Subnautica.Assets.shproj index 04a5cbd8b4..73ea54b512 100644 --- a/Nitrox.Subnautica.Assets/Nitrox.Subnautica.Assets.shproj +++ b/Nitrox.Subnautica.Assets/Nitrox.Subnautica.Assets.shproj @@ -1,5 +1,5 @@ - + {0763D551-B1A8-4960-B88A-98833F957936} @@ -12,4 +12,4 @@ - \ No newline at end of file + diff --git a/Nitrox.sln b/Nitrox.sln index a01826d583..63aa45aae7 100644 --- a/Nitrox.sln +++ b/Nitrox.sln @@ -18,7 +18,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .editorconfig = .editorconfig Directory.Build.props = Directory.Build.props Directory.Build.targets = Directory.Build.targets - SharedConfig.targets = SharedConfig.targets EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NitroxModel-Subnautica", "NitroxModel-Subnautica\NitroxModel-Subnautica.csproj", "{0A377218-6B36-4522-89A3-A39CFC999209}" @@ -33,6 +32,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nitrox.Bootloader", "Nitrox EndProject Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Nitrox.Subnautica.Assets", "Nitrox.Subnautica.Assets\Nitrox.Subnautica.Assets.shproj", "{0763D551-B1A8-4960-B88A-98833F957936}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nitrox.BuildTool", "Nitrox.BuildTool\Nitrox.BuildTool.csproj", "{15C4C9C4-683C-4EF0-9E0F-0664A3BDA0CE}" +EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution Nitrox.Subnautica.Assets\Nitrox.Subnautica.Assets.projitems*{0763d551-b1a8-4960-b88a-98833f957936}*SharedItemsImports = 13 @@ -82,6 +83,10 @@ Global {E4226522-9189-410B-93B2-792942FBD588}.Debug|Any CPU.Build.0 = Debug|Any CPU {E4226522-9189-410B-93B2-792942FBD588}.Release|Any CPU.ActiveCfg = Release|Any CPU {E4226522-9189-410B-93B2-792942FBD588}.Release|Any CPU.Build.0 = Release|Any CPU + {15C4C9C4-683C-4EF0-9E0F-0664A3BDA0CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {15C4C9C4-683C-4EF0-9E0F-0664A3BDA0CE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {15C4C9C4-683C-4EF0-9E0F-0664A3BDA0CE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {15C4C9C4-683C-4EF0-9E0F-0664A3BDA0CE}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/NitroxClient/NitroxClient.csproj b/NitroxClient/NitroxClient.csproj index b2f1e4c015..cb9787fc30 100644 --- a/NitroxClient/NitroxClient.csproj +++ b/NitroxClient/NitroxClient.csproj @@ -374,11 +374,11 @@ - $(SubnauticaManaged)\iTween.dll + $(GameManagedDir)\iTween.dll false - $(SubnauticaManaged)\LitJson.dll + $(GameManagedDir)\LitJson.dll false @@ -389,4 +389,4 @@ - \ No newline at end of file + diff --git a/NitroxLauncher/NitroxLauncher.csproj b/NitroxLauncher/NitroxLauncher.csproj index 1b6d744d22..69b7a7acf7 100644 --- a/NitroxLauncher/NitroxLauncher.csproj +++ b/NitroxLauncher/NitroxLauncher.csproj @@ -1,5 +1,5 @@  - + Debug @@ -269,4 +269,4 @@ - \ No newline at end of file + diff --git a/NitroxModel-Subnautica/NitroxModel-Subnautica.csproj b/NitroxModel-Subnautica/NitroxModel-Subnautica.csproj index 8886deff6b..e0bcbf09c2 100644 --- a/NitroxModel-Subnautica/NitroxModel-Subnautica.csproj +++ b/NitroxModel-Subnautica/NitroxModel-Subnautica.csproj @@ -94,4 +94,4 @@ - \ No newline at end of file + diff --git a/NitroxModel/Discovery/GameInstallData.cs b/NitroxModel/Discovery/GameInstallData.cs new file mode 100644 index 0000000000..0a43832adb --- /dev/null +++ b/NitroxModel/Discovery/GameInstallData.cs @@ -0,0 +1,93 @@ +using System; +using System.IO; +using System.Linq; +using System.Text; +using System.Xml; +using NitroxModel.Helper; + +namespace NitroxModel.Discovery +{ + /// + /// Game definition data gathered from Steam's files. + /// + public class GameInstallData + { + private string managedDllsDir; + public string InstallDir { get; private set; } + + public string ManagedDllsDir => managedDllsDir ??= Directory.EnumerateDirectories(InstallDir, "Managed", SearchOption.AllDirectories).FirstOrDefault(); + + private GameInstallData() + { + // Required for serialization + } + + public GameInstallData(string installDir) + { + Validate.NotNull(installDir, $"Argument '{nameof(installDir)}' must not be null."); + InstallDir = installDir; + } + + public static bool TryFrom(string path, out GameInstallData result) + { + try + { + GameInstallData game = new(); + XmlDocument xDoc = new(); + xDoc.Load(path); + XmlNamespaceManager nsManager = new(xDoc.NameTable); + nsManager.AddNamespace("d", xDoc.DocumentElement.NamespaceURI); + foreach (XmlElement elem in xDoc.DocumentElement.SelectNodes("//d:PropertyGroup/*", nsManager)) + { + switch (elem.Name) + { + case "GameDir": + game.InstallDir = elem.LastChild.Value; + break; + case "GameManagedDir": + if (!Directory.Exists(elem.LastChild.Value)) + { + throw new DirectoryNotFoundException(); + } + game.managedDllsDir = elem.LastChild.Value; + break; + } + } + + result = game; + return true; + } + catch (Exception) + { + result = null; + return false; + } + } + + public void TrySave(string path) + { + static string PostfixBackslash(string text) + { + return text switch + { + null => null, + "" => "\\", + _ => text.TrimEnd('\\') + '\\' + }; + } + + Directory.CreateDirectory(Path.GetDirectoryName(path)); + using XmlTextWriter writer = new(path, Encoding.UTF8) + { + Formatting = Formatting.Indented + }; + writer.WriteStartElement("Project"); + writer.WriteAttributeString("xmlns", "http://schemas.microsoft.com/developer/msbuild/2003"); + writer.WriteStartElement("PropertyGroup"); + writer.WriteElementString("GameDir", PostfixBackslash(InstallDir)); + writer.WriteElementString("GameManagedDir", PostfixBackslash(ManagedDllsDir)); + writer.WriteEndElement(); + writer.WriteEndElement(); + } + } +} diff --git a/NitroxModel/Discovery/GameInstallationFinder.cs b/NitroxModel/Discovery/GameInstallationFinder.cs index c444bc1205..f8e0889422 100644 --- a/NitroxModel/Discovery/GameInstallationFinder.cs +++ b/NitroxModel/Discovery/GameInstallationFinder.cs @@ -28,11 +28,7 @@ public class GameInstallationFinder : IFindGameInstallation public string FindGame(IList errors = null) { - if (errors == null) - { - errors = new List(); - } - + errors ??= new List(); foreach (IFindGameInstallation finder in finders) { string path = finder.FindGame(errors); @@ -55,7 +51,7 @@ public static bool IsSubnauticaDirectory(string directory) return false; } - return Directory.EnumerateFileSystemEntries(directory, "*.exe") + return Directory.EnumerateFiles(directory, "*.exe") .Any(file => Path.GetFileName(file)?.Equals("subnautica.exe", StringComparison.OrdinalIgnoreCase) ?? false); } } diff --git a/NitroxModel/Logger/Log.cs b/NitroxModel/Logger/Log.cs index 7c93dc4797..38794e2753 100644 --- a/NitroxModel/Logger/Log.cs +++ b/NitroxModel/Logger/Log.cs @@ -14,7 +14,8 @@ namespace NitroxModel.Logger { public static class Log { - private static ILogger logger; + private static ILogger logger = Serilog.Core.Logger.None; + private static bool isSetup; public static string PlayerName { @@ -27,10 +28,12 @@ public static string PlayerName public static void Setup(bool asyncConsoleWriter = false, InGameLogger inGameLogger = null, bool isConsoleApp = false, bool useConsoleLogging = true) { - if (logger != null) + if (isSetup) { throw new Exception($"{nameof(Log)} setup should only be executed once."); } + isSetup = true; + PlayerName = ""; logger = new LoggerConfiguration() .MinimumLevel.Debug() diff --git a/NitroxModel/NitroxModel.csproj b/NitroxModel/NitroxModel.csproj index 62ea5a5c58..5867fc485c 100644 --- a/NitroxModel/NitroxModel.csproj +++ b/NitroxModel/NitroxModel.csproj @@ -77,6 +77,7 @@ + diff --git a/NitroxServer-Subnautica/NitroxServer-Subnautica.csproj b/NitroxServer-Subnautica/NitroxServer-Subnautica.csproj index 1fcf742764..1d95f43d33 100644 --- a/NitroxServer-Subnautica/NitroxServer-Subnautica.csproj +++ b/NitroxServer-Subnautica/NitroxServer-Subnautica.csproj @@ -1,5 +1,5 @@  - + Debug diff --git a/NitroxServer/NitroxServer.csproj b/NitroxServer/NitroxServer.csproj index f36e1ab695..cb7ba90dc3 100644 --- a/NitroxServer/NitroxServer.csproj +++ b/NitroxServer/NitroxServer.csproj @@ -248,4 +248,4 @@ - \ No newline at end of file + diff --git a/NitroxTest/NitroxTest.csproj b/NitroxTest/NitroxTest.csproj index 941ced3e95..4997a1ed51 100644 --- a/NitroxTest/NitroxTest.csproj +++ b/NitroxTest/NitroxTest.csproj @@ -1,5 +1,5 @@  - + Debug AnyCPU @@ -109,10 +109,6 @@ {ed5034bd-66b5-4596-94b7-66a28d3eff49} NitroxClient - - {b16f4de7-21ad-4fef-955b-0a5a365fa4e3} - NitroxModel - {0a377218-6b36-4522-89a3-a39cfc999209} NitroxModel-Subnautica diff --git a/SharedConfig.targets b/SharedConfig.targets deleted file mode 100644 index 03d328ca4a..0000000000 --- a/SharedConfig.targets +++ /dev/null @@ -1,37 +0,0 @@ - - - - DevVars.targets - $(SolutionDir)\$(DevVars) - C:\Program Files (x86)\Steam\steamapps\common\Subnautica - - - - false - true - - - - - - - - - - - - - - - - $(SubnauticaDir)\Subnautica_Data\Managed - - - - - - - - $(SolutionDir)\bin\$(Configuration)\ - -