diff --git a/.gitignore b/.gitignore index 72de34f1ea..faab97ce4f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,10 @@ ## ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore +# Nitrox DocFx files +Nitrox.DocFx/api/ +Nitrox.DocFx/_site/ + # User-specific files *.rsuser *.suo @@ -385,4 +389,4 @@ FodyWeavers.xsd # JetBrains Rider .idea/ -*.sln.iml \ No newline at end of file +*.sln.iml diff --git a/Directory.Build.props b/Directory.Build.props index b9087c4bc0..6903dbdf69 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -7,7 +7,7 @@ false false false - $(SolutionDir)Nitrox.BuildTool\bin\ + $(MSBuildThisFileDirectory)Nitrox.BuildTool\bin\ $(BuildToolDir)generated_files\ $(BuildGenDir)publicized_assemblies\ true @@ -22,7 +22,7 @@ true - + true @@ -43,15 +43,10 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + @@ -61,11 +56,11 @@ - + Nitrox.BuildTool false - + NitroxModel diff --git a/Nitrox.Analyzers/Diagnostics/DependencyInjectionMisuseAnalyzer.cs b/Nitrox.Analyzers/Diagnostics/DependencyInjectionMisuseAnalyzer.cs deleted file mode 100644 index 0f81382354..0000000000 --- a/Nitrox.Analyzers/Diagnostics/DependencyInjectionMisuseAnalyzer.cs +++ /dev/null @@ -1,89 +0,0 @@ -using System; -using System.Collections.Immutable; -using System.Linq; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; -using Nitrox.Analyzers.Extensions; - -namespace Nitrox.Analyzers.Diagnostics; - -/// -/// Dependency injection shouldn't be used in types that we can instantiate ourselves (i.e. not MonoBehaviours or Harmony patches). -/// We should use the Dependency Injection container only when we can apply the DI pattern to effect. If not, making said type static is often more readable and performant. -/// -[DiagnosticAnalyzer(LanguageNames.CSharp)] -public sealed class DependencyInjectionMisuseAnalyzer : DiagnosticAnalyzer -{ - public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rules.MisusedDependencyInjection); - - public override void Initialize(AnalysisContext context) - { - context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); - context.EnableConcurrentExecution(); - - context.RegisterCompilationStartAction(analysisContext => - { - INamedTypeSymbol unityEngineObjectTypeSymbol = analysisContext.Compilation.GetTypeByMetadataName("UnityEngine.Object"); - INamedTypeSymbol nitroxPatchTypeSymbol = analysisContext.Compilation.GetTypeByMetadataName("NitroxPatcher.Patches.NitroxPatch"); - - analysisContext.RegisterSyntaxNodeAction(c => AnalyzeDependencyInjectionMisuse(c, unityEngineObjectTypeSymbol, nitroxPatchTypeSymbol), SyntaxKind.InvocationExpression); - }); - } - - private static void AnalyzeDependencyInjectionMisuse(SyntaxNodeAnalysisContext context, params INamedTypeSymbol[] allowedTypesUsingDependencyInjection) - { - InvocationExpressionSyntax expression = (InvocationExpressionSyntax)context.Node; - if (expression.ChildNodes().FirstOrDefault(n => n is MemberAccessExpressionSyntax) is not MemberAccessExpressionSyntax memberAccessExpression) - { - return; - } - if (memberAccessExpression.DescendantNodes().FirstOrDefault(n => n is IdentifierNameSyntax) is not IdentifierNameSyntax memberAccessIdentifier) - { - return; - } - if (memberAccessIdentifier.Parent is TypeOfExpressionSyntax) - { - return; - } - if (!memberAccessIdentifier.GetName().Equals("NitroxServiceLocator", StringComparison.Ordinal)) - { - return; - } - TypeDeclarationSyntax declaringType = expression.FindInParents(); - if (declaringType == null) - { - return; - } - INamedTypeSymbol declaringTypeSymbol = context.SemanticModel.GetDeclaredSymbol(declaringType); - if (declaringTypeSymbol == null) - { - return; - } - foreach (INamedTypeSymbol allowedType in allowedTypesUsingDependencyInjection) - { - if (declaringTypeSymbol.IsType(allowedType)) - { - return; - } - } - - Rules.ReportMisusedDependencyInjection(context, declaringType, memberAccessExpression.GetLocation()); - } - - private static class Rules - { - public static readonly DiagnosticDescriptor MisusedDependencyInjection = new("DIMA001", - "Dependency Injection container is used directly", - "The DI container should not be used directly in type '{0}' as the requested service can be supplied via a constructor parameter.", - "Usage", - DiagnosticSeverity.Warning, - true); - - public static void ReportMisusedDependencyInjection(SyntaxNodeAnalysisContext context, TypeDeclarationSyntax declaringType, Location location) - { - context.ReportDiagnostic(Diagnostic.Create(MisusedDependencyInjection, location, declaringType.GetName())); - } - } -} diff --git a/Nitrox.Analyzers/Diagnostics/EnumeratorUsageAnalyzer.cs b/Nitrox.Analyzers/Diagnostics/EnumeratorUsageAnalyzer.cs deleted file mode 100644 index 22e93cac8f..0000000000 --- a/Nitrox.Analyzers/Diagnostics/EnumeratorUsageAnalyzer.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System.Collections; -using System.Collections.Immutable; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; -using Nitrox.Analyzers.Extensions; - -namespace Nitrox.Analyzers.Diagnostics; - -/// -/// Test that calls to a method returning an IEnumerator are iterated (MoveNext is called). If they aren't iterated than the code in them won't -/// continue after the first 'yield return'. -/// -[DiagnosticAnalyzer(LanguageNames.CSharp)] -public sealed class EnumeratorUsageAnalyzer : DiagnosticAnalyzer -{ - public const string UNUSED_ENUMERATOR = $"{nameof(EnumeratorUsageAnalyzer)}001"; - - private static readonly DiagnosticDescriptor unusedEnumerator = new(UNUSED_ENUMERATOR, - "IEnumerator is not iterated", - $"The IEnumerator '{{0}}' must be iterated by calling its {nameof(IEnumerator.MoveNext)} otherwise it will stop executing at the first 'yield return' expression", - "Usage", - DiagnosticSeverity.Warning, - true); - - public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(unusedEnumerator); - - public override void Initialize(AnalysisContext context) - { - context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); - context.EnableConcurrentExecution(); - - context.RegisterSyntaxNodeAction(static c => AnalyzeIEnumeratorInvocation(c), SyntaxKind.InvocationExpression); - } - - private static void AnalyzeIEnumeratorInvocation(SyntaxNodeAnalysisContext context) - { - InvocationExpressionSyntax expression = (InvocationExpressionSyntax)context.Node; - if (expression.Parent == null) - { - return; - } - IMethodSymbol methodSymbol = context.SemanticModel.GetSymbolInfo(expression, context.CancellationToken).Symbol as IMethodSymbol; - if (methodSymbol == null) - { - return; - } - // Ignore if method invoke is used/wrapped by something (variable declaration, as a parameter, etc). - if (!expression.Parent.IsKind(SyntaxKind.ExpressionStatement)) - { - return; - } - if (!methodSymbol.ReturnType.IsType(context.SemanticModel, "System.Collections.IEnumerator")) - { - return; - } - - context.ReportDiagnostic(Diagnostic.Create(unusedEnumerator, expression.GetLocation(), methodSymbol.Name)); - } -} diff --git a/Nitrox.Analyzers/Diagnostics/LocalizationAnalyzer.cs b/Nitrox.Analyzers/Diagnostics/LocalizationAnalyzer.cs deleted file mode 100644 index 0b8615d217..0000000000 --- a/Nitrox.Analyzers/Diagnostics/LocalizationAnalyzer.cs +++ /dev/null @@ -1,173 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.IO; -using System.Linq; -using System.Text.RegularExpressions; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; - -namespace Nitrox.Analyzers.Diagnostics; - -/// -/// Tests that requested localization keys exist in the English localization file. -/// -[DiagnosticAnalyzer(LanguageNames.CSharp)] -public sealed class LocalizationAnalyzer : DiagnosticAnalyzer -{ - public const string INVALID_LOCALIZATION_KEY_DIAGNOSTIC_ID = $"{nameof(LocalizationAnalyzer)}001"; - - private const string NITROX_LOCALIZATION_PREFIX = "Nitrox_"; - private static readonly string relativePathFromSolutionDirToEnglishLanguageFile = Path.Combine("Nitrox.Assets.Subnautica", "LanguageFiles", "en.json"); - private static readonly Regex localizationParseRegex = new(@"^\s*""([^""]+)""\s*:\s*""([^""]+)""", RegexOptions.Compiled | RegexOptions.Multiline); - - private static readonly DiagnosticDescriptor invalidLocalizationKeyRule = new(INVALID_LOCALIZATION_KEY_DIAGNOSTIC_ID, - "Tests localization usages are valid", - "Localization key '{0}' does not exist in '{1}'", - "Usage", - DiagnosticSeverity.Warning, - true, - "Tests that requested localization keys exist in the English localization file"); - - /// - /// Gets the list of rules of supported diagnostics. - /// - public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(invalidLocalizationKeyRule); - - /// - /// Initializes the analyzer by registering on symbol occurrence in the targeted code. - /// - public override void Initialize(AnalysisContext context) - { - context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); - context.EnableConcurrentExecution(); - - context.RegisterCompilationStartAction(startContext => - { - IMethodSymbol languageGetMethodSymbol = startContext.Compilation.GetTypesByMetadataName("Language").FirstOrDefault(a => a.ContainingAssembly.Name.Equals("Assembly-Csharp", StringComparison.OrdinalIgnoreCase))?.GetMembers("Get").FirstOrDefault(m => m.Kind == SymbolKind.Method) as IMethodSymbol; - if (languageGetMethodSymbol == null) - { - return; - } - - startContext.Options.AnalyzerConfigOptionsProvider.GlobalOptions.TryGetValue("build_property.projectdir", out string projectDir); - if (LocalizationHelper.Load(projectDir)) - { - startContext.RegisterSyntaxNodeAction(c => AnalyzeStringNode(c, languageGetMethodSymbol), SyntaxKind.StringLiteralExpression); - } - }); - } - - /// - /// Analyzes string literals in code that are passed as argument to 'Language.main.Get'. - /// - private void AnalyzeStringNode(SyntaxNodeAnalysisContext context, IMethodSymbol languageGetMethodSymbol) - { - LiteralExpressionSyntax expression = (LiteralExpressionSyntax)context.Node; - if (expression.Parent is not ArgumentSyntax argument) - { - return; - } - if (argument.Parent is not { Parent: InvocationExpressionSyntax invocation }) - { - return; - } - if (context.SemanticModel.GetSymbolInfo(invocation).Symbol is not IMethodSymbol method) - { - return; - } - if (!SymbolEqualityComparer.Default.Equals(method, languageGetMethodSymbol)) - { - return; - } - // Ignore language call for non-nitrox keys. - string stringValue = expression.Token.ValueText; - if (!stringValue.StartsWith(NITROX_LOCALIZATION_PREFIX, StringComparison.OrdinalIgnoreCase)) - { - return; - } - if (LocalizationHelper.ContainsKey(stringValue)) - { - return; - } - context.ReportDiagnostic(Diagnostic.Create(invalidLocalizationKeyRule, context.Node.GetLocation(), stringValue, LocalizationHelper.FileName)); - } - - /// - /// Wrapper API for synchronized access to the English localization file. - /// - private static class LocalizationHelper - { - private static readonly object locker = new(); - private static string EnglishLocalizationFileName { get; set; } = ""; - private static ImmutableDictionary EnglishLocalization { get; set; } = ImmutableDictionary.Empty; - - public static bool IsEmpty - { - get - { - lock (locker) - { - return EnglishLocalization.IsEmpty; - } - } - } - - public static string FileName - { - get - { - lock (locker) - { - return EnglishLocalizationFileName; - } - } - } - - public static bool ContainsKey(string key) - { - lock (locker) - { - return EnglishLocalization.ContainsKey(key); - } - } - - public static bool Load(string projectDir) - { - if (string.IsNullOrWhiteSpace(projectDir)) - { - return false; - } - string solutionDir = Directory.GetParent(projectDir)?.Parent?.FullName; - if (!Directory.Exists(solutionDir)) - { - return false; - } - - string enJson; - lock (locker) - { - EnglishLocalizationFileName = Path.Combine(solutionDir, relativePathFromSolutionDirToEnglishLanguageFile); - if (!File.Exists(EnglishLocalizationFileName)) - { - return false; - } - - enJson = File.ReadAllText(EnglishLocalizationFileName); - } - // Parse localization JSON to dictionary for lookup. - Dictionary keyValue = new(); - foreach (Match match in localizationParseRegex.Matches(enJson)) - { - keyValue.Add(match.Groups[1].Value, match.Groups[2].Value); - } - lock (locker) - { - EnglishLocalization = keyValue.ToImmutableDictionary(); - } - return true; - } - } -} diff --git a/Nitrox.Analyzers/Diagnostics/StringUsageAnalyzer.cs b/Nitrox.Analyzers/Diagnostics/StringUsageAnalyzer.cs deleted file mode 100644 index 26cd375970..0000000000 --- a/Nitrox.Analyzers/Diagnostics/StringUsageAnalyzer.cs +++ /dev/null @@ -1,80 +0,0 @@ -using System.Collections.Immutable; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; - -namespace Nitrox.Analyzers.Diagnostics; - -[DiagnosticAnalyzer(LanguageNames.CSharp)] -public sealed class StringUsageAnalyzer : DiagnosticAnalyzer -{ - public const string PREFER_INTERPOLATED_STRING_DIAGNOSTIC_ID = $"{nameof(StringUsageAnalyzer)}001"; - - private static readonly DiagnosticDescriptor preferInterpolatedStringRule = new(PREFER_INTERPOLATED_STRING_DIAGNOSTIC_ID, - "Prefer interpolated string over string concat", - "String concat can be turned into interpolated string", - "Usage", - DiagnosticSeverity.Warning, - true, - "Prefer interpolated string over concatenating strings"); - - public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(preferInterpolatedStringRule); - - public override void Initialize(AnalysisContext context) - { - context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); - context.EnableConcurrentExecution(); - - context.RegisterSyntaxNodeAction(AnalyzeAddNode, SyntaxKind.AddExpression); - } - - private void AnalyzeAddNode(SyntaxNodeAnalysisContext context) - { - bool IsPartOfStringConcat(SyntaxNode node) - { - switch (node) - { - case LiteralExpressionSyntax literal: - return literal.IsKind(SyntaxKind.StringLiteralExpression); - case InterpolatedStringExpressionSyntax: - return true; - case MemberAccessExpressionSyntax member: - string memberType = context.SemanticModel.GetTypeInfo(member).ConvertedType?.Name; - return memberType == "String"; - case BinaryExpressionSyntax binary: - // If one side is string-ish then the other side will get implicitly casted to string. - return binary.IsKind(SyntaxKind.AddExpression) && (IsPartOfStringConcat(binary.Right) || IsPartOfStringConcat(binary.Left)); - default: - return false; - } - } - - static bool IsLeftMostNodeInConcat(SyntaxNode node) - { - switch (node) - { - case BinaryExpressionSyntax: - case InterpolatedStringContentSyntax: - return false; - case ParenthesizedExpressionSyntax: - return IsLeftMostNodeInConcat(node.Parent); - } - return true; - } - - BinaryExpressionSyntax expression = (BinaryExpressionSyntax)context.Node; - // Deduplicate warnings. Only left most '+' of the expression should be handled here. - if (!IsLeftMostNodeInConcat(expression.Parent)) - { - return; - } - // Test if this should be interpolated. - if (!IsPartOfStringConcat(expression.Left) && !IsPartOfStringConcat(expression.Right)) - { - return; - } - - context.ReportDiagnostic(Diagnostic.Create(preferInterpolatedStringRule, expression.GetLocation(), expression)); - } -} diff --git a/Nitrox.Analyzers/Diagnostics/UnitySkippedObjectLifetimeAnalyzer.cs b/Nitrox.Analyzers/Diagnostics/UnitySkippedObjectLifetimeAnalyzer.cs deleted file mode 100644 index 26a6df3c29..0000000000 --- a/Nitrox.Analyzers/Diagnostics/UnitySkippedObjectLifetimeAnalyzer.cs +++ /dev/null @@ -1,123 +0,0 @@ -using System.Collections.Immutable; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; -using Nitrox.Analyzers.Extensions; - -namespace Nitrox.Analyzers.Diagnostics; - -/// -/// Test that Unity objects are properly checked for their lifetime. -/// The lifetime check is skipped when using "is null" or "obj?.member" as opposed to "== null". -/// -[DiagnosticAnalyzer(LanguageNames.CSharp)] -public sealed class UnitySkippedObjectLifetimeAnalyzer : DiagnosticAnalyzer -{ - public const string FIX_FUNCTION_NAME = "AliveOrNull"; - public const string FIX_FUNCTION_NAMESPACE = "NitroxClient.Unity.Helper"; - public const string CONDITIONAL_ACCESS_DIAGNOSTIC_ID = $"{nameof(UnitySkippedObjectLifetimeAnalyzer)}001"; - public const string IS_NULL_DIAGNOSTIC_ID = $"{nameof(UnitySkippedObjectLifetimeAnalyzer)}002"; - public const string NULL_COALESCE_DIAGNOSTIC_ID = $"{nameof(UnitySkippedObjectLifetimeAnalyzer)}003"; - private const string RULE_TITLE = "Tests that Unity object lifetime is not ignored"; - private const string RULE_DESCRIPTION = "Tests that Unity object lifetime checks are not ignored."; - - private static readonly DiagnosticDescriptor conditionalAccessRule = new(CONDITIONAL_ACCESS_DIAGNOSTIC_ID, - RULE_TITLE, - "'?.' is invalid on type '{0}' as it derives from 'UnityEngine.Object', bypassing the Unity object lifetime check", - "Usage", - DiagnosticSeverity.Error, - true, - RULE_DESCRIPTION); - - private static readonly DiagnosticDescriptor isNullRule = new(IS_NULL_DIAGNOSTIC_ID, - RULE_TITLE, - "'is null' is invalid on type '{0}' as it derives from 'UnityEngine.Object', bypassing the Unity object lifetime check", - "Usage", - DiagnosticSeverity.Error, - true, - RULE_DESCRIPTION); - - private static readonly DiagnosticDescriptor nullCoalesceRule = new(NULL_COALESCE_DIAGNOSTIC_ID, - RULE_TITLE, - "'??' is invalid on type '{0}' as it derives from 'UnityEngine.Object', bypassing the Unity object lifetime check", - "Usage", - DiagnosticSeverity.Error, - true, - RULE_DESCRIPTION); - - /// - /// Gets the list of rules of supported diagnostics. - /// - public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(conditionalAccessRule, isNullRule, nullCoalesceRule); - - /// - /// Initializes the analyzer by registering on symbol occurrence in the targeted code. - /// - public override void Initialize(AnalysisContext context) - { - context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); - context.EnableConcurrentExecution(); - - context.RegisterCompilationStartAction(compStartContext => - { - INamedTypeSymbol unityObjectTypeSymbol = compStartContext.Compilation.GetTypeByMetadataName("UnityEngine.Object"); - if (unityObjectTypeSymbol == null) - { - return; - } - - compStartContext.RegisterSyntaxNodeAction(c => AnalyzeIsNullNode(c, unityObjectTypeSymbol), SyntaxKind.IsPatternExpression); - compStartContext.RegisterSyntaxNodeAction(c => AnalyzeConditionalAccessNode(c, unityObjectTypeSymbol), SyntaxKind.ConditionalAccessExpression); - compStartContext.RegisterSyntaxNodeAction(c => AnalyzeCoalesceNode(c, unityObjectTypeSymbol), SyntaxKind.CoalesceExpression); - }); - } - - private void AnalyzeIsNullNode(SyntaxNodeAnalysisContext context, ITypeSymbol unityObjectSymbol) - { - IsPatternExpressionSyntax expression = (IsPatternExpressionSyntax)context.Node; - // Is this a "is null" check? - if (expression.Pattern is not ConstantPatternSyntax constantPattern) - { - return; - } - if (constantPattern.Expression is not LiteralExpressionSyntax literal || !literal.Token.IsKind(SyntaxKind.NullKeyword)) - { - return; - } - // Is it on a UnityEngine.Object? - if (IsUnityObjectExpression(context, expression.Expression, unityObjectSymbol, out ITypeSymbol originSymbol)) - { - context.ReportDiagnostic(Diagnostic.Create(isNullRule, constantPattern.GetLocation(), originSymbol!.Name)); - } - } - - private void AnalyzeConditionalAccessNode(SyntaxNodeAnalysisContext context, ITypeSymbol unityObjectSymbol) - { - static bool IsFixedWithAliveOrNull(SyntaxNodeAnalysisContext context, ConditionalAccessExpressionSyntax expression) - { - return (context.SemanticModel.GetSymbolInfo(expression.Expression).Symbol as IMethodSymbol)?.Name == FIX_FUNCTION_NAME; - } - - ConditionalAccessExpressionSyntax expression = (ConditionalAccessExpressionSyntax)context.Node; - if (IsUnityObjectExpression(context, expression.Expression, unityObjectSymbol, out ITypeSymbol originSymbol) && !IsFixedWithAliveOrNull(context, expression)) - { - context.ReportDiagnostic(Diagnostic.Create(conditionalAccessRule, context.Node.GetLocation(), originSymbol!.Name)); - } - } - - private void AnalyzeCoalesceNode(SyntaxNodeAnalysisContext context, ITypeSymbol unityObjectSymbol) - { - BinaryExpressionSyntax expression = (BinaryExpressionSyntax)context.Node; - if (IsUnityObjectExpression(context, expression.Left, unityObjectSymbol, out ITypeSymbol originSymbol)) - { - context.ReportDiagnostic(Diagnostic.Create(nullCoalesceRule, context.Node.GetLocation(), originSymbol!.Name)); - } - } - - private bool IsUnityObjectExpression(SyntaxNodeAnalysisContext context, ExpressionSyntax possibleUnityAccessExpression, ITypeSymbol compareSymbol, out ITypeSymbol possibleUnitySymbol) - { - possibleUnitySymbol = context.SemanticModel.GetTypeInfo(possibleUnityAccessExpression).Type; - return possibleUnitySymbol.IsType(compareSymbol); - } -} diff --git a/Nitrox.Analyzers/Extensions/CompilationExtensions.cs b/Nitrox.Analyzers/Extensions/CompilationExtensions.cs deleted file mode 100644 index 737482f0aa..0000000000 --- a/Nitrox.Analyzers/Extensions/CompilationExtensions.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Linq; -using Microsoft.CodeAnalysis; - -namespace Nitrox.Analyzers.Extensions; - -internal static class CompilationExtensions -{ - /// - /// Returns the found or null. - /// - /// The compilation that should have the type. - /// Assembly name, case sensitive. - /// Type name include the namespace, case sensitive. - /// - public static INamedTypeSymbol GetType(this Compilation compilation, string assemblyNameWithoutDll, string fullTypeName) => - compilation.GetTypesByMetadataName(fullTypeName).FirstOrDefault(a => a.ContainingAssembly.Name.Equals(assemblyNameWithoutDll, StringComparison.Ordinal)); -} diff --git a/Nitrox.Analyzers/Extensions/DebugExtensions.cs b/Nitrox.Analyzers/Extensions/DebugExtensions.cs deleted file mode 100644 index 8a3fe00265..0000000000 --- a/Nitrox.Analyzers/Extensions/DebugExtensions.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Diagnostics; -using System.IO; -using System.Threading.Tasks; - -namespace Nitrox.Analyzers.Extensions; - -public static class DebugExtensions -{ - private static readonly string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop); - private static readonly ConcurrentQueue<(object source, string message)> logQueue = new(); - private static readonly object logLocker = new(); - - /// - /// Can be used to test analyzers. - /// - [Conditional("DEBUG")] - public static void Log(this object analyzer, string message) - { - if (analyzer == null) - { - return; - } - - logQueue.Enqueue((analyzer, message)); - Task.Run(() => - { - while (!logQueue.IsEmpty) - { - if (!logQueue.TryDequeue(out (object source, string message) pair)) - { - continue; - } - - lock (logLocker) - { - File.AppendAllText(Path.Combine(desktopPath, $"{pair.source.GetType().Name}.log"), pair.message + Environment.NewLine); - } - } - }); - } -} diff --git a/Nitrox.Analyzers/Extensions/SymbolExtensions.cs b/Nitrox.Analyzers/Extensions/SymbolExtensions.cs deleted file mode 100644 index b27442febe..0000000000 --- a/Nitrox.Analyzers/Extensions/SymbolExtensions.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Microsoft.CodeAnalysis; - -namespace Nitrox.Analyzers.Extensions; - -public static class SymbolExtensions -{ - public static bool IsType(this ITypeSymbol symbol, SemanticModel semanticModel, string fullyQualifiedTypeName) - { - return symbol.IsType(semanticModel.Compilation.GetTypeByMetadataName(fullyQualifiedTypeName)); - } - - public static bool IsType(this ITypeSymbol symbol, ITypeSymbol targetingSymbol) - { - if (symbol == null || targetingSymbol == null) - { - return false; - } - if (SymbolEqualityComparer.Default.Equals(symbol, targetingSymbol)) - { - return true; - } - while (symbol.BaseType is { } baseTypeSymbol) - { - symbol = baseTypeSymbol; - if (SymbolEqualityComparer.Default.Equals(symbol, targetingSymbol)) - { - return true; - } - } - return false; - } -} diff --git a/Nitrox.Analyzers/Extensions/SyntaxExtensions.cs b/Nitrox.Analyzers/Extensions/SyntaxExtensions.cs deleted file mode 100644 index d528829f4f..0000000000 --- a/Nitrox.Analyzers/Extensions/SyntaxExtensions.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System.Linq; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -namespace Nitrox.Analyzers.Extensions; - -public static class SyntaxExtensions -{ - public static bool IsPartialType(this TypeDeclarationSyntax typeSyntax) => typeSyntax.Modifiers.Any(SyntaxKind.PartialKeyword); - - - public static string GetNamespaceName(this TypeDeclarationSyntax type) => type.Ancestors() - .Select(n => n switch - { - FileScopedNamespaceDeclarationSyntax f => f.Name.ToString(), - NamespaceDeclarationSyntax ns => ns.Name.ToString(), - _ => null - }) - .First(); - - public static string GetReturnTypeName(this MemberDeclarationSyntax member) => member switch - { - FieldDeclarationSyntax field => field.Declaration.ChildNodes().OfType().FirstOrDefault()?.Identifier.ValueText ?? "", - MethodDeclarationSyntax method => method.ReturnType.ToString(), - _ => "" - }; - - public static string GetName(this MemberDeclarationSyntax member) => member switch - { - FieldDeclarationSyntax field => field.Declaration.Variables.FirstOrDefault()?.Identifier.ValueText ?? "", - TypeDeclarationSyntax type => type.Identifier.Text, - _ => "" - }; - - public static T FindInParents(this SyntaxNode node) where T : SyntaxNode - { - if (node == null) - { - return null; - } - SyntaxNode cur = node.Parent; - while (cur != null && cur is not T) - { - cur = cur.Parent; - } - return (T)cur; - } - - public static string GetName(this SyntaxNode node) => node switch - { - FieldDeclarationSyntax field => field.Declaration.Variables.FirstOrDefault()?.Identifier.ValueText ?? "", - TypeDeclarationSyntax type => type.Identifier.ValueText, - _ => node.TryGetInferredMemberName() - }; -} diff --git a/Nitrox.Analyzers/Fixers/UnitySkippedObjectLifetimeFixProvider.cs b/Nitrox.Analyzers/Fixers/UnitySkippedObjectLifetimeFixProvider.cs deleted file mode 100644 index b86e600d20..0000000000 --- a/Nitrox.Analyzers/Fixers/UnitySkippedObjectLifetimeFixProvider.cs +++ /dev/null @@ -1,70 +0,0 @@ -extern alias JB; -using System.Collections.Immutable; -using System.Composition; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using JB::JetBrains.Annotations; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Text; -using Nitrox.Analyzers.Diagnostics; -using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; - -namespace Nitrox.Analyzers.Fixers; - -[Shared] -[UsedImplicitly] -[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(UnitySkippedObjectLifetimeFixProvider))] -public sealed class UnitySkippedObjectLifetimeFixProvider : CodeFixProvider -{ - private static readonly IdentifierNameSyntax aliveOrNull = IdentifierName(UnitySkippedObjectLifetimeAnalyzer.FIX_FUNCTION_NAME); - public override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create(UnitySkippedObjectLifetimeAnalyzer.CONDITIONAL_ACCESS_DIAGNOSTIC_ID); - - public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; - - public override async Task RegisterCodeFixesAsync(CodeFixContext context) - { - // Code template from: https://learn.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/tutorials/how-to-write-csharp-analyzer-code-fix - SyntaxNode root = await context.Document.GetSyntaxRootAsync(context.CancellationToken) - .ConfigureAwait(false); - Diagnostic diagnostic = context.Diagnostics.First(); - TextSpan diagnosticSpan = diagnostic.Location.SourceSpan; - ConditionalAccessExpressionSyntax declaration = root!.FindToken(diagnosticSpan.Start).Parent!.AncestorsAndSelf() - .OfType() - .First(); - context.RegisterCodeFix( - CodeAction.Create( - equivalenceKey: UnitySkippedObjectLifetimeAnalyzer.CONDITIONAL_ACCESS_DIAGNOSTIC_ID, - title: "Insert AliveOrNull() before conditional access of UnityEngine.Object", - createChangedDocument: c => InsertAliveOrNullAsync(context.Document, declaration, c) - ), - diagnostic); - } - - private async Task InsertAliveOrNullAsync(Document document, ConditionalAccessExpressionSyntax declaration, CancellationToken cancellationToken) - { - SyntaxNode root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - if (root == null) - { - return document; - } - - // 1. Wrap expression with an invocation to AliveOrNull, this will cause AliveOrNull to be called before the conditional access. - InvocationExpressionSyntax wrappedExpression = InvocationExpression(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, declaration.Expression, aliveOrNull)); - SyntaxNode newDeclaration = declaration.ReplaceNode(declaration.Expression, wrappedExpression); - root = root!.ReplaceNode(declaration, newDeclaration); - // 2. Ensure using statement for extension method .AliveOrNull(). - // This is done after the "AliveOrNull" wrap because the declaration instance can't be found when root instance updates. - if (root is CompilationUnitSyntax compilationRoot && compilationRoot.Usings.All(u => u.Name.ToString() != UnitySkippedObjectLifetimeAnalyzer.FIX_FUNCTION_NAMESPACE)) - { - root = compilationRoot.AddUsings(UsingDirective(aliveOrNull)); - } - - // Replace the old document with the new. - return document.WithSyntaxRoot(root); - } -} diff --git a/Nitrox.Analyzers/Generators/HarmonyRegisterPatchGenerator.cs b/Nitrox.Analyzers/Generators/HarmonyRegisterPatchGenerator.cs deleted file mode 100644 index c0a321e40a..0000000000 --- a/Nitrox.Analyzers/Generators/HarmonyRegisterPatchGenerator.cs +++ /dev/null @@ -1,213 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Text; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Nitrox.Analyzers.Extensions; - -namespace Nitrox.Analyzers.Generators; - -/// -/// Implements the harmony patch registry boilerplate for NitroxPatch inherited types by scanning its static MethodInfo -/// fields and static patch methods. -/// -[Generator(LanguageNames.CSharp)] -internal sealed class HarmonyRegisterPatchGenerator : IIncrementalGenerator -{ - private static readonly string[] harmonyMethodTypes = { "prefix", "postfix", "transpiler", "finalizer", "manipulator" }; - private static readonly string[] validTargetMethodNames = { "target_method", "targetmethod", "target", "method" }; - private static readonly Lazy generatedCodeAttribute = new(() => $@"[global::System.CodeDom.Compiler.GeneratedCode(""{typeof(HarmonyRegisterPatchGenerator).FullName}"", ""{typeof(HarmonyRegisterPatchGenerator).Assembly.GetName().Version}"")]"); - - [SuppressMessage("ReSharper", "SuggestVarOrType_Elsewhere")] - public void Initialize(IncrementalGeneratorInitializationContext context) - { - // Setup compilation pipeline for assemblies that use NitroxPatch. - var compilationPipeline = context.CompilationProvider.Select((c, _) => c.GetType("NitroxPatcher", "NitroxPatcher.Patches.NitroxPatch") != null); - // Look for partial types inheriting our NitroxPatch type, selecting all the harmony methods and target method infos. - var harmonyMethodsWithTargetMethods = context.SyntaxProvider - .CreateSyntaxProvider( - static (node, _) => IsSyntaxTargetForGeneration(node), - static (context, _) => GetSemanticTargetForGeneration(context)) - .Where(r => r is not null) - .WithComparer(NitroxHarmonyType.NitroxHarmonyTypeEqualityComparer.Instance); - - // Register the pipeline into the compiler. - var combinedPipeline = harmonyMethodsWithTargetMethods.Combine(compilationPipeline); - context.RegisterSourceOutput(combinedPipeline, static (context, source) => Execute(context, source.Left)); - } - - private static void Execute(SourceProductionContext context, NitroxHarmonyType nitroxHarmonyType) - { - // Build Patch method implementation. - StringBuilder patchImpl = new(); - for (int fieldIndex = 0; fieldIndex < nitroxHarmonyType.MethodInfoFields.Length; fieldIndex++) - { - FieldDeclarationSyntax methodInfoField = nitroxHarmonyType.MethodInfoFields[fieldIndex]; - patchImpl.Append("PatchMultiple(harmony, ") - .Append(methodInfoField.GetName()); - if (nitroxHarmonyType.HarmonyPatchMethods.Length > 0) - { - patchImpl.Append(", "); - foreach (MethodDeclarationSyntax harmonyPatchMethod in nitroxHarmonyType.HarmonyPatchMethods) - { - patchImpl.Append(harmonyPatchMethod.Identifier.ValueText.ToLowerInvariant()) - .Append(": ((Delegate)") - .Append(harmonyPatchMethod.Identifier.ValueText) - .Append(").Method, "); - } - patchImpl.Remove(patchImpl.Length - 2, 2); - } - patchImpl.Append(");"); - // Append new line if not last implementation line. - if (fieldIndex < nitroxHarmonyType.MethodInfoFields.Length - 1) - { - patchImpl.AppendLine(); - } - } - - // Append new code to the compilation. - context.AddSource($"{nitroxHarmonyType.NameSpace}.{nitroxHarmonyType.TypeName}.g.cs", $$""" - #pragma warning disable - using System; - using HarmonyLib; - - namespace {{nitroxHarmonyType.NameSpace}}; - - partial class {{nitroxHarmonyType.TypeName}} - { - {{generatedCodeAttribute.Value}} - public override void Patch(Harmony harmony) - { - {{patchImpl}} - } - } - """); - } - - private static bool IsSyntaxTargetForGeneration(SyntaxNode node) - { - if (node is not TypeDeclarationSyntax type) - { - return false; - } - if (!type.IsPartialType()) - { - return false; - } - // Skip if not deriving from "NitroxPatch". - if (type.BaseList?.Types.FirstOrDefault(t => t.ToString().Equals("NitroxPatch", StringComparison.Ordinal)) == null) - { - return false; - } - // Skip if "Patch" method is already defined. - if (type.Members.OfType().Any(m => m.Modifiers.Any(SyntaxKind.OverrideKeyword) && m.Identifier.ValueText == "Patch")) - { - return false; - } - return true; - } - - private static NitroxHarmonyType GetSemanticTargetForGeneration(GeneratorSyntaxContext context) - { - static bool IsValidPatchMethodName(string methodName) => harmonyMethodTypes.Contains(methodName.ToLowerInvariant()); - - static bool IsValidTargetMethodFieldName(string fieldName) - { - foreach (string n in validTargetMethodNames) - { - if (fieldName.StartsWith(n, StringComparison.OrdinalIgnoreCase)) - { - return true; - } - } - return false; - } - - TypeDeclarationSyntax type = context.Node as TypeDeclarationSyntax; - if (type == null) - { - return null; - } - ImmutableArray members = type.Members.ToImmutableArray(); - return new NitroxHarmonyType(type.GetNamespaceName(), - type.Identifier.ValueText, - members.OfType() - .Where(m => m.Modifiers.Any(SyntaxKind.StaticKeyword) && IsValidPatchMethodName(m.Identifier.ValueText)) - .ToImmutableArray(), - members.OfType() - .Where(m => m.Modifiers.Any(SyntaxKind.StaticKeyword) && m.GetReturnTypeName() == "MethodInfo" && IsValidTargetMethodFieldName(m.GetName())) - .ToImmutableArray()); - } - - internal record NitroxHarmonyType(string NameSpace, string TypeName, ImmutableArray HarmonyPatchMethods, ImmutableArray MethodInfoFields) - { - internal sealed class NitroxHarmonyTypeEqualityComparer : IEqualityComparer - { - public static IEqualityComparer Instance { get; } = new NitroxHarmonyTypeEqualityComparer(); - - public bool Equals(NitroxHarmonyType x, NitroxHarmonyType y) - { - if (ReferenceEquals(x, y)) return true; - if (ReferenceEquals(x, null)) return false; - if (ReferenceEquals(y, null)) return false; - if (x.GetType() != y.GetType()) return false; - return string.Equals(x.NameSpace, y.NameSpace) && - string.Equals(x.TypeName, y.TypeName) && - x.HarmonyPatchMethods.SequenceEqual(y.HarmonyPatchMethods, HarmonyMethodEqualityComparer.Instance) && - x.MethodInfoFields.SequenceEqual(y.MethodInfoFields, MethodInfoFieldEqualityComparer.Instance); - } - - public int GetHashCode(NitroxHarmonyType obj) - { - unchecked - { - int hashCode = obj.NameSpace != null ? obj.NameSpace.GetHashCode() : 0; - hashCode = (hashCode * 397) ^ (obj.TypeName != null ? obj.TypeName.GetHashCode() : 0); - return hashCode; - } - } - } - - private sealed class HarmonyMethodEqualityComparer : IEqualityComparer - { - public static IEqualityComparer Instance { get; } = new HarmonyMethodEqualityComparer(); - - public bool Equals(MethodDeclarationSyntax x, MethodDeclarationSyntax y) - { - if (ReferenceEquals(x, y)) return true; - if (ReferenceEquals(x, null)) return false; - if (ReferenceEquals(y, null)) return false; - if (x.GetType() != y.GetType()) return false; - return string.Equals(x.Identifier.ValueText, y.Identifier.ValueText); - } - - public int GetHashCode(MethodDeclarationSyntax obj) - { - return obj.Identifier.ValueText.GetHashCode(); - } - } - - private sealed class MethodInfoFieldEqualityComparer : IEqualityComparer - { - public static IEqualityComparer Instance { get; } = new MethodInfoFieldEqualityComparer(); - - public bool Equals(FieldDeclarationSyntax x, FieldDeclarationSyntax y) - { - if (ReferenceEquals(x, y)) return true; - if (ReferenceEquals(x, null)) return false; - if (ReferenceEquals(y, null)) return false; - if (x.GetType() != y.GetType()) return false; - return x.ToString() == y.ToString(); - } - - public int GetHashCode(FieldDeclarationSyntax obj) - { - return obj.ToString().GetHashCode(); - } - } - } -} diff --git a/Nitrox.Analyzers/Nitrox.Analyzers.csproj b/Nitrox.Analyzers/Nitrox.Analyzers.csproj deleted file mode 100644 index e9fcbafc79..0000000000 --- a/Nitrox.Analyzers/Nitrox.Analyzers.csproj +++ /dev/null @@ -1,18 +0,0 @@ - - - - - netstandard2.0 - - - - - - - diff --git a/Nitrox.BuildTool/Nitrox.BuildTool.csproj b/Nitrox.BuildTool/Nitrox.BuildTool.csproj index e60938f069..e22bc28891 100644 --- a/Nitrox.BuildTool/Nitrox.BuildTool.csproj +++ b/Nitrox.BuildTool/Nitrox.BuildTool.csproj @@ -1,4 +1,4 @@ - + net472 @@ -6,14 +6,15 @@ disable bin\ false + Debug;Release;Linux - + - + diff --git a/Nitrox.DocFx/.config/dotnet-tools.json b/Nitrox.DocFx/.config/dotnet-tools.json new file mode 100644 index 0000000000..4faeb59e1c --- /dev/null +++ b/Nitrox.DocFx/.config/dotnet-tools.json @@ -0,0 +1,12 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "docfx": { + "version": "2.74.1", + "commands": [ + "docfx" + ] + } + } +} \ No newline at end of file diff --git a/Nitrox.DocFx/Nitrox.DocFx.proj b/Nitrox.DocFx/Nitrox.DocFx.proj new file mode 100644 index 0000000000..f08b548a8b --- /dev/null +++ b/Nitrox.DocFx/Nitrox.DocFx.proj @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/Nitrox.DocFx/docfx.json b/Nitrox.DocFx/docfx.json new file mode 100644 index 0000000000..5b0a078a48 --- /dev/null +++ b/Nitrox.DocFx/docfx.json @@ -0,0 +1,52 @@ +{ + "metadata": [ + { + "src": [ + { + "src": "../", + "files": [ + "**/*.csproj" + ] + } + ], + "dest": "api", + "memberLayout": "separatePages", + "namespaceLayout": "nested" + } + ], + "build": { + "content": [ + { + "files": [ + "**/*.{md,yml}" + ], + "exclude": [ + "_site/**" + ] + } + ], + "resource": [ + { + "files": [ + "images/**" + ] + } + ], + "output": "_site", + "template": [ + "default", + "modern" + ], + + "globalMetadata": { + "_appName": "Nitrox", + "_appTitle": "Nitrox", + "_gitContribute": { + "repo": "https://github.com/SubnauticaNitrox/Nitrox", + "branch": "master" + }, + "_enableSearch": true, + "pdf": false + } + } +} diff --git a/Nitrox.DocFx/docs/getting-started.md b/Nitrox.DocFx/docs/getting-started.md new file mode 100644 index 0000000000..8b3a7945ce --- /dev/null +++ b/Nitrox.DocFx/docs/getting-started.md @@ -0,0 +1 @@ +# Getting Started \ No newline at end of file diff --git a/Nitrox.DocFx/docs/introduction.md b/Nitrox.DocFx/docs/introduction.md new file mode 100644 index 0000000000..f6ecaa676c --- /dev/null +++ b/Nitrox.DocFx/docs/introduction.md @@ -0,0 +1 @@ +# Introduction \ No newline at end of file diff --git a/Nitrox.DocFx/docs/toc.yml b/Nitrox.DocFx/docs/toc.yml new file mode 100644 index 0000000000..d7e9ea8cbd --- /dev/null +++ b/Nitrox.DocFx/docs/toc.yml @@ -0,0 +1,4 @@ +- name: Introduction + href: introduction.md +- name: Getting Started + href: getting-started.md \ No newline at end of file diff --git a/Nitrox.DocFx/toc.yml b/Nitrox.DocFx/toc.yml new file mode 100644 index 0000000000..061acc65fd --- /dev/null +++ b/Nitrox.DocFx/toc.yml @@ -0,0 +1,4 @@ +- name: Docs + href: docs/ +- name: API + href: api/ \ No newline at end of file diff --git a/Nitrox.Test/Nitrox.Test.csproj b/Nitrox.Test/Nitrox.Test.csproj index 7e032ebeec..b067349e8c 100644 --- a/Nitrox.Test/Nitrox.Test.csproj +++ b/Nitrox.Test/Nitrox.Test.csproj @@ -4,6 +4,7 @@ net472 disable false + Debug;Release;Linux diff --git a/Nitrox.sln b/Nitrox.sln index afe01605f9..73844200f0 100644 --- a/Nitrox.sln +++ b/Nitrox.sln @@ -30,57 +30,66 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nitrox.Test", "Nitrox.Test\ EndProject Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Nitrox.Assets.Subnautica", "Nitrox.Assets.Subnautica\Nitrox.Assets.Subnautica.shproj", "{79E92B6D-5D25-4254-AC9F-FA9A1CD3CBC6}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nitrox.Analyzers", "Nitrox.Analyzers\Nitrox.Analyzers.csproj", "{EB99BD64-EF43-4B06-BBFB-EB5DFA96E55D}" -EndProject Global - GlobalSection(SharedMSBuildProjectFiles) = preSolution - Nitrox.Assets.Subnautica\Nitrox.Assets.Subnautica.projitems*{79e92b6d-5d25-4254-ac9f-fa9a1cd3cbc6}*SharedItemsImports = 13 - EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Linux|Any CPU = Linux|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {CB7FAEFC-D9C0-40B8-A6D5-8B284683E79E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CB7FAEFC-D9C0-40B8-A6D5-8B284683E79E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CB7FAEFC-D9C0-40B8-A6D5-8B284683E79E}.Linux|Any CPU.ActiveCfg = Linux|Any CPU + {CB7FAEFC-D9C0-40B8-A6D5-8B284683E79E}.Linux|Any CPU.Build.0 = Linux|Any CPU {CB7FAEFC-D9C0-40B8-A6D5-8B284683E79E}.Release|Any CPU.ActiveCfg = Release|Any CPU {CB7FAEFC-D9C0-40B8-A6D5-8B284683E79E}.Release|Any CPU.Build.0 = Release|Any CPU {EBEBC7DC-FAE3-4FBC-BDD3-38ED8FC072D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EBEBC7DC-FAE3-4FBC-BDD3-38ED8FC072D9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EBEBC7DC-FAE3-4FBC-BDD3-38ED8FC072D9}.Linux|Any CPU.ActiveCfg = Linux|Any CPU + {EBEBC7DC-FAE3-4FBC-BDD3-38ED8FC072D9}.Linux|Any CPU.Build.0 = Linux|Any CPU {EBEBC7DC-FAE3-4FBC-BDD3-38ED8FC072D9}.Release|Any CPU.ActiveCfg = Release|Any CPU {EBEBC7DC-FAE3-4FBC-BDD3-38ED8FC072D9}.Release|Any CPU.Build.0 = Release|Any CPU {59EB8953-864F-4147-A210-7FC97E1A5294}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {59EB8953-864F-4147-A210-7FC97E1A5294}.Debug|Any CPU.Build.0 = Debug|Any CPU + {59EB8953-864F-4147-A210-7FC97E1A5294}.Linux|Any CPU.ActiveCfg = Linux|Any CPU {59EB8953-864F-4147-A210-7FC97E1A5294}.Release|Any CPU.ActiveCfg = Release|Any CPU {59EB8953-864F-4147-A210-7FC97E1A5294}.Release|Any CPU.Build.0 = Release|Any CPU {39E377AD-2163-4428-952D-EBECD402C8F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {39E377AD-2163-4428-952D-EBECD402C8F3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {39E377AD-2163-4428-952D-EBECD402C8F3}.Linux|Any CPU.ActiveCfg = Linux|Any CPU + {39E377AD-2163-4428-952D-EBECD402C8F3}.Linux|Any CPU.Build.0 = Linux|Any CPU {39E377AD-2163-4428-952D-EBECD402C8F3}.Release|Any CPU.ActiveCfg = Release|Any CPU {39E377AD-2163-4428-952D-EBECD402C8F3}.Release|Any CPU.Build.0 = Release|Any CPU {47D774E0-750C-427B-8C38-F8F985114A2E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {47D774E0-750C-427B-8C38-F8F985114A2E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {47D774E0-750C-427B-8C38-F8F985114A2E}.Linux|Any CPU.ActiveCfg = Linux|Any CPU + {47D774E0-750C-427B-8C38-F8F985114A2E}.Linux|Any CPU.Build.0 = Linux|Any CPU {47D774E0-750C-427B-8C38-F8F985114A2E}.Release|Any CPU.ActiveCfg = Release|Any CPU {47D774E0-750C-427B-8C38-F8F985114A2E}.Release|Any CPU.Build.0 = Release|Any CPU {5453E724-5A8B-46A4-850B-1F17FA2E938D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5453E724-5A8B-46A4-850B-1F17FA2E938D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5453E724-5A8B-46A4-850B-1F17FA2E938D}.Linux|Any CPU.ActiveCfg = Linux|Any CPU + {5453E724-5A8B-46A4-850B-1F17FA2E938D}.Linux|Any CPU.Build.0 = Linux|Any CPU {5453E724-5A8B-46A4-850B-1F17FA2E938D}.Release|Any CPU.ActiveCfg = Release|Any CPU {5453E724-5A8B-46A4-850B-1F17FA2E938D}.Release|Any CPU.Build.0 = Release|Any CPU {77692FDB-F713-41F1-B2AB-9019457B8909}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {77692FDB-F713-41F1-B2AB-9019457B8909}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77692FDB-F713-41F1-B2AB-9019457B8909}.Linux|Any CPU.ActiveCfg = Linux|Any CPU + {77692FDB-F713-41F1-B2AB-9019457B8909}.Linux|Any CPU.Build.0 = Linux|Any CPU {77692FDB-F713-41F1-B2AB-9019457B8909}.Release|Any CPU.ActiveCfg = Release|Any CPU {77692FDB-F713-41F1-B2AB-9019457B8909}.Release|Any CPU.Build.0 = Release|Any CPU {0CD6846B-EDA6-4FFD-A540-2A46CC1074BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0CD6846B-EDA6-4FFD-A540-2A46CC1074BF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0CD6846B-EDA6-4FFD-A540-2A46CC1074BF}.Linux|Any CPU.ActiveCfg = Linux|Any CPU + {0CD6846B-EDA6-4FFD-A540-2A46CC1074BF}.Linux|Any CPU.Build.0 = Linux|Any CPU {0CD6846B-EDA6-4FFD-A540-2A46CC1074BF}.Release|Any CPU.ActiveCfg = Release|Any CPU {0CD6846B-EDA6-4FFD-A540-2A46CC1074BF}.Release|Any CPU.Build.0 = Release|Any CPU {E4D8C360-34E4-4BE6-909F-3791DD9169B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E4D8C360-34E4-4BE6-909F-3791DD9169B5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E4D8C360-34E4-4BE6-909F-3791DD9169B5}.Linux|Any CPU.ActiveCfg = Linux|Any CPU + {E4D8C360-34E4-4BE6-909F-3791DD9169B5}.Linux|Any CPU.Build.0 = Linux|Any CPU {E4D8C360-34E4-4BE6-909F-3791DD9169B5}.Release|Any CPU.ActiveCfg = Release|Any CPU {E4D8C360-34E4-4BE6-909F-3791DD9169B5}.Release|Any CPU.Build.0 = Release|Any CPU - {EB99BD64-EF43-4B06-BBFB-EB5DFA96E55D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EB99BD64-EF43-4B06-BBFB-EB5DFA96E55D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EB99BD64-EF43-4B06-BBFB-EB5DFA96E55D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EB99BD64-EF43-4B06-BBFB-EB5DFA96E55D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -88,4 +97,7 @@ Global GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {AC56EA37-FBBC-4D19-8796-29A42A2331A2} EndGlobalSection + GlobalSection(SharedMSBuildProjectFiles) = preSolution + Nitrox.Assets.Subnautica\Nitrox.Assets.Subnautica.projitems*{79e92b6d-5d25-4254-ac9f-fa9a1cd3cbc6}*SharedItemsImports = 13 + EndGlobalSection EndGlobal diff --git a/NitroxClient/NitroxClient.csproj b/NitroxClient/NitroxClient.csproj index 178a2f6015..02e9a48903 100644 --- a/NitroxClient/NitroxClient.csproj +++ b/NitroxClient/NitroxClient.csproj @@ -1,8 +1,9 @@ - + net472 disable + Debug;Release;Linux diff --git a/NitroxLauncher/NitroxLauncher.csproj b/NitroxLauncher/NitroxLauncher.csproj index c9358bbb6c..1a637e1ed9 100644 --- a/NitroxLauncher/NitroxLauncher.csproj +++ b/NitroxLauncher/NitroxLauncher.csproj @@ -12,6 +12,7 @@ Nitrox Nitrox https://github.com/SubnauticaNitrox/Nitrox + Debug;Release;Linux diff --git a/NitroxModel-Subnautica/NitroxModel-Subnautica.csproj b/NitroxModel-Subnautica/NitroxModel-Subnautica.csproj index 8674facb17..cba3a303de 100644 --- a/NitroxModel-Subnautica/NitroxModel-Subnautica.csproj +++ b/NitroxModel-Subnautica/NitroxModel-Subnautica.csproj @@ -4,6 +4,7 @@ net472 NitroxModel_Subnautica disable + Debug;Release;Linux diff --git a/NitroxModel/NitroxModel.csproj b/NitroxModel/NitroxModel.csproj index 5f2d6c432d..727a89e884 100644 --- a/NitroxModel/NitroxModel.csproj +++ b/NitroxModel/NitroxModel.csproj @@ -1,8 +1,9 @@ - + net472 disable + Debug;Release;Linux diff --git a/NitroxPatcher/NitroxPatcher.csproj b/NitroxPatcher/NitroxPatcher.csproj index 26cdeee752..50000c0d9e 100644 --- a/NitroxPatcher/NitroxPatcher.csproj +++ b/NitroxPatcher/NitroxPatcher.csproj @@ -1,8 +1,9 @@ - + net472 disable + Debug;Release;Linux diff --git a/NitroxServer-Subnautica/NitroxServer-Subnautica.csproj b/NitroxServer-Subnautica/NitroxServer-Subnautica.csproj index b7268ed09e..e8fa2a283b 100644 --- a/NitroxServer-Subnautica/NitroxServer-Subnautica.csproj +++ b/NitroxServer-Subnautica/NitroxServer-Subnautica.csproj @@ -1,4 +1,4 @@ - + net472 @@ -6,6 +6,7 @@ NitroxServer_Subnautica disable true + Debug;Release;Linux diff --git a/NitroxServer/NitroxServer.csproj b/NitroxServer/NitroxServer.csproj index 7d7ceb4f6c..e65dc521b1 100644 --- a/NitroxServer/NitroxServer.csproj +++ b/NitroxServer/NitroxServer.csproj @@ -1,8 +1,9 @@ - + net472 disable + Debug;Release;Linux diff --git a/index.md b/index.md new file mode 100644 index 0000000000..f9859f8407 --- /dev/null +++ b/index.md @@ -0,0 +1,11 @@ +--- +_layout: landing +--- + +# This is the **HOMEPAGE**. + +Refer to [Markdown](http://daringfireball.net/projects/markdown/) for how to write markdown files. + +## Quick Start Notes: + +1. Add images to the *images* folder if the file is referencing an image. \ No newline at end of file