diff --git a/AUTHORS.md b/AUTHORS.md
index 4d2450b3..fc3478c7 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -3,15 +3,15 @@ We thank all of the following individuals for their contributions to Nautilus. T
| Name | Picture | Commits | Name | Picture | Commits | Name | Picture | Commits |
| ---- | --------------- | ------ | ---- | --------------- | ------ | ---- | --------------- | ------ |
-| [MrPurple6411](https://github.com/MrPurple6411) | | 433 | [PrimeSonic](https://github.com/PrimeSonic) | | 312 | [Metious](https://github.com/Metious) | | 297 |
+| [MrPurple6411](https://github.com/MrPurple6411) | | 451 | [PrimeSonic](https://github.com/PrimeSonic) | | 312 | [Metious](https://github.com/Metious) | | 309 |
| [toebeann](https://github.com/toebeann) | | 228 | [ahk1221](https://github.com/ahk1221) | | 212 | [Alexejhero](https://github.com/Alexejhero) | | 207 |
-| [LeeTwentyThree](https://github.com/LeeTwentyThree) | | 122 | [zorgesho](https://github.com/zorgesho) | | 42 | [JKohlman](https://github.com/JKohlman) | | 37 |
+| [LeeTwentyThree](https://github.com/LeeTwentyThree) | | 126 | [zorgesho](https://github.com/zorgesho) | | 42 | [JKohlman](https://github.com/JKohlman) | | 37 |
| [K07H](https://github.com/K07H) | | 22 | [Vlad-00003](https://github.com/Vlad-00003) | | 11 | [EckoTheBat](https://github.com/EckoTheBat) | | 11 |
-| [github-actions[bot]](https://github.com/apps/github-actions) | | 6 | [Cattlesquat](https://github.com/Cattlesquat) | | 5 | [DingoDjango](https://github.com/DingoDjango) | | 4 |
+| [github-actions[bot]](https://github.com/apps/github-actions) | | 8 | [Cattlesquat](https://github.com/Cattlesquat) | | 5 | [DingoDjango](https://github.com/DingoDjango) | | 4 |
| [celvro](https://github.com/celvro) | | 4 | [NeisesMike](https://github.com/NeisesMike) | | 3 | [VELD-Dev](https://github.com/VELD-Dev) | | 3 |
| [vlyon](https://github.com/vlyon) | | 3 | [EldritchCarMaker](https://github.com/EldritchCarMaker) | | 3 | [jonahnm](https://github.com/jonahnm) | | 3 |
-| [brett-taylor](https://github.com/brett-taylor) | | 2 | [RamuneNeptune](https://github.com/RamuneNeptune) | | 2 | [DeadMor0z](https://github.com/DeadMor0z) | | 1 |
-| [SamuramongeDev](https://github.com/SamuramongeDev) | | 1 |
+| [brett-taylor](https://github.com/brett-taylor) | | 2 | [RamuneNeptune](https://github.com/RamuneNeptune) | | 2 | [tinyhoot](https://github.com/tinyhoot) | | 2 |
+| [DeadMor0z](https://github.com/DeadMor0z) | | 1 | [SamuramongeDev](https://github.com/SamuramongeDev) | | 1 |
If you notice a problem with this file, feel free to report an issue in the repository.
diff --git a/CLA/Signatures.json b/CLA/Signatures.json
index e27f4706..b5d0b204 100644
--- a/CLA/Signatures.json
+++ b/CLA/Signatures.json
@@ -55,6 +55,14 @@
"created_at": "2023-09-25T14:00:10Z",
"repoId": 123711758,
"pullRequestNo": 468
+ },
+ {
+ "name": "Govorunb",
+ "id": 3830522,
+ "comment_id": 1825332180,
+ "created_at": "2023-11-24T08:52:03Z",
+ "repoId": 123711758,
+ "pullRequestNo": 505
}
]
}
\ No newline at end of file
diff --git a/Example mod/Example mod.csproj b/Example mod/Example mod.csproj
index fa1791b4..5b4e2d93 100644
--- a/Example mod/Example mod.csproj
+++ b/Example mod/Example mod.csproj
@@ -1,47 +1,48 @@
-
-
-
- net472
- false
- false
- Nautilus.Examples
- Nautilus.Example
- true
- true
- AnyCPU
- latest
- SN.STABLE;BZ.STABLE
- AnyCPU
- Copyright © 2023
- false
- false
-
-
-
- bin\SN.STABLE\
- SUBNAUTICA;SUBNAUTICA_STABLE
-
-
- bin\BZ.STABLE\
- BELOWZERO;BELOWZERO_STABLE
-
-
-
- False
-
-
-
-
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
+
+
+
+
+ net472
+ false
+ false
+ Nautilus.Examples
+ Nautilus.Example
+ $(VersionPrefix).$(SuffixNumber)
+ true
+ true
+ AnyCPU
+ latest
+ SN.STABLE;BZ.STABLE
+ AnyCPU
+ Copyright © 2023
+ false
+ false
+
+
+ bin\SN.STABLE\
+ SUBNAUTICA;SUBNAUTICA_STABLE
+
+
+ bin\BZ.STABLE\
+ BELOWZERO;BELOWZERO_STABLE
+
-
+
+ False
+
-
-
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Nautilus.sln b/Nautilus.sln
index 1a386e0f..d8f313ae 100644
--- a/Nautilus.sln
+++ b/Nautilus.sln
@@ -13,10 +13,12 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{5152F028-6D03-43C0-88C5-986313017D4F}"
ProjectSection(SolutionItems) = preProject
common.props = common.props
+ Directory.Build.props = Directory.Build.props
+ PostBuild.targets = PostBuild.targets
Version.targets = Version.targets
EndProjectSection
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UploadHelper", "UploadHelper\UploadHelper.csproj", "{1D6AED20-87CC-4756-8E6A-1E8A10E16EB1}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UploadHelper", "UploadHelper\UploadHelper.csproj", "{1D6AED20-87CC-4756-8E6A-1E8A10E16EB1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
diff --git a/Nautilus/Assets/Gadgets/ScanningGadget.cs b/Nautilus/Assets/Gadgets/ScanningGadget.cs
index a84dec1e..367a66ad 100644
--- a/Nautilus/Assets/Gadgets/ScanningGadget.cs
+++ b/Nautilus/Assets/Gadgets/ScanningGadget.cs
@@ -318,7 +318,7 @@ public ScanningGadget WithAnalysisTech(
/// A reference to this instance after the operation has completed.
public ScanningGadget WithAnalysisTech(
Sprite popupSprite,
- List storyGoalsToTrigger = null,
+ List storyGoalsToTrigger,
FMODAsset unlockSound = null,
string unlockMessage = null
)
diff --git a/Nautilus/Assets/ModPrefabCache.cs b/Nautilus/Assets/ModPrefabCache.cs
index ef74e5a8..933c546d 100644
--- a/Nautilus/Assets/ModPrefabCache.cs
+++ b/Nautilus/Assets/ModPrefabCache.cs
@@ -111,7 +111,7 @@ public void EnterPrefabIntoCache(GameObject prefab)
if(!Entries.ContainsKey(prefabIdentifier.classId))
{
Entries.Add(prefabIdentifier.classId, prefab);
- InternalLogger.Debug($"ModPrefabCache: adding prefab {prefab}");
+ InternalLogger.Debug($"ModPrefabCache: added prefab {prefab}");
}
else // this should never happen
{
diff --git a/Nautilus/Assets/ModPrefabRequest.cs b/Nautilus/Assets/ModPrefabRequest.cs
index dc5e67ed..c3154ca5 100644
--- a/Nautilus/Assets/ModPrefabRequest.cs
+++ b/Nautilus/Assets/ModPrefabRequest.cs
@@ -7,12 +7,14 @@
namespace Nautilus.Assets;
// request for getting ModPrefab asynchronously
-internal class ModPrefabRequest: IPrefabRequest, IEnumerator
+internal class ModPrefabRequest: IPrefabRequest
{
+ internal bool Done { get; private set; }
+
private readonly PrefabInfo prefabInfo;
-
- private int state = 0;
+
private CoroutineTask task;
+
private TaskResult taskResult;
public ModPrefabRequest(PrefabInfo prefabInfo)
@@ -50,13 +52,22 @@ public object Current
public bool TryGetPrefab(out GameObject result)
{
result = taskResult.Get();
+ if (!Done)
+ {
+ Done = result;
+ }
return result != null;
}
public bool MoveNext()
{
Init();
- return state++ == 0;
+ if (task == null)
+ {
+ return false;
+ }
+
+ return !TryGetPrefab(out _);
}
public void Reset() {}
diff --git a/Nautilus/Commands/ConsoleCommand.cs b/Nautilus/Commands/ConsoleCommand.cs
index 6955feb7..66c24ce2 100644
--- a/Nautilus/Commands/ConsoleCommand.cs
+++ b/Nautilus/Commands/ConsoleCommand.cs
@@ -3,6 +3,7 @@
using System.Linq;
using System.Reflection;
using HarmonyLib;
+using Nautilus.Extensions;
namespace Nautilus.Commands;
@@ -24,7 +25,11 @@ internal class ConsoleCommand
///
/// The parameters for the command.
///
- public IEnumerable Parameters { get; }
+ public IReadOnlyList Parameters { get; }
+ ///
+ /// The minimum number of parameters required to invoke the command.
+ ///
+ public int RequiredParameterCount { get; }
///
/// The types of the parameters.
@@ -53,8 +58,9 @@ public ConsoleCommand(string trigger, MethodInfo targetMethod, bool isDelegate =
IsDelegate = isDelegate;
Instance = instance;
ModName = DeclaringType.Assembly.GetName().Name;
- Parameters = targetMethod.GetParameters().Select(param => new Parameter(param));
+ Parameters = targetMethod.GetParameters().Select(param => new Parameter(param)).ToList();
ParameterTypes = Parameters.Select(param => param.ParameterType).ToArray();
+ RequiredParameterCount = Parameters.Count(param => !param.IsOptional);
}
///
@@ -66,73 +72,96 @@ public bool HasValidInvoke()
return IsDelegate || Instance != null || IsMethodStatic;
}
- ///
- /// Determines whether the target methods parameters are valid.
- ///
- ///
- public bool HasValidParameterTypes()
- {
- foreach (Parameter parameter in Parameters)
- {
- if (!parameter.IsValidParameterType)
- {
- return false;
- }
- }
-
- return true;
- }
-
///
/// Returns a list of all invalid parameters.
///
///
public IEnumerable GetInvalidParameters()
{
- return Parameters.Where(param => !param.IsValidParameterType);
+ return Parameters.Where(p => p.ValidState != Parameter.ValidationError.Valid);
}
///
/// Attempts to parse input parameters into appropriate types as defined in the target method.
///
- /// The parameters as input by the user.
+ /// The parameters as input by the user.
/// The parameters that have been successfully parsed.
- /// Whether or not all parameters were succesfully parsed.
- public bool TryParseParameters(IEnumerable inputParameters, out object[] parsedParameters)
+ ///
+ /// A tuple containing:
+ ///
+ /// - The number of input items consumed.
+ /// - The number of command parameters that were successfully parsed.
+ ///
+ ///
+ public (int consumed, int parsed) TryParseParameters(IReadOnlyList input, out object[] parsedParameters)
{
parsedParameters = null;
// Detect incorrect number of parameters (allow for optional)
- if (Parameters.Count() < inputParameters.Count() ||
- Parameters.Where(param => !param.IsOptional).Count() > inputParameters.Count())
+ int paramCount = Parameters.Count;
+ int inputCount = input.Count;
+ int paramsArrayLength = Math.Max(0, input.Count - (paramCount - 1));
+
+ if (inputCount < RequiredParameterCount)
{
- return false;
+ return default;
}
- parsedParameters = new object[Parameters.Count()];
- for (int i = 0; i < Parameters.Count(); i++)
+ parsedParameters = new object[paramCount];
+ for (int i = 0; i < paramCount; i++)
{
- Parameter parameter = Parameters.ElementAt(i);
-
- if (i >= inputParameters.Count()) // It's an optional parameter that wasn't passed by the user
- {
- parsedParameters[i] = Type.Missing;
- continue;
- }
+ Type paramType = Parameters[i].ParameterType;
+ parsedParameters[i] = paramType.TryUnwrapArrayType(out Type elementType)
+ ? Array.CreateInstance(elementType, paramsArrayLength)
+ : DBNull.Value;
+ }
- string input = inputParameters.ElementAt(i);
+ int consumed = 0;
+ int parsed = 0;
+ while (consumed < inputCount)
+ {
+ if (parsed >= paramCount) break;
+
+ Parameter parameter = Parameters[parsed];
+ string inputItem = input[consumed];
+ object parsedItem;
try
{
- parsedParameters[i] = parameter.Parse(input);
+ parsedItem = parameter.Parse(inputItem);
}
catch (Exception)
{
- return false; // couldn't parse, wasn't a valid conversion
+ return (consumed, parsed);
+ }
+ consumed++;
+
+ if (parameter.ParameterType.IsArray)
+ {
+ Array parsedArr = (Array)parsedParameters[parsed];
+ parsedArr.SetValue(parsedItem, consumed - parsed - 1);
+ if (consumed >= inputCount)
+ {
+ parsed++;
+ }
+ }
+ else
+ {
+ parsedParameters[parsed] = parsedItem;
+ parsed++;
}
}
- return true;
+ // Optional parameters that weren't passed by the user
+ // at this point all required parameters should've been parsed
+ for (int i = parsed; i < paramCount; i++)
+ {
+ if (parsedParameters[i] == DBNull.Value)
+ parsedParameters[i] = Type.Missing;
+ parsed++;
+ }
+
+ return (consumed, parsed);
}
///
diff --git a/Nautilus/Commands/Parameter.cs b/Nautilus/Commands/Parameter.cs
index c24ee012..f9aa3f3c 100644
--- a/Nautilus/Commands/Parameter.cs
+++ b/Nautilus/Commands/Parameter.cs
@@ -1,15 +1,21 @@
-
using System;
using System.Collections.Generic;
using System.Globalization;
-using System.Linq;
using System.Reflection;
+using Nautilus.Extensions;
namespace Nautilus.Commands;
internal struct Parameter
{
- private static Dictionary> TypeConverters = new()
+ [Flags]
+ public enum ValidationError
+ {
+ Valid = 0,
+ UnsupportedType = 1,
+ ArrayNotParams = 2,
+ }
+ private static Dictionary> _typeConverters = new()
{
[typeof(string)] = (s) => s,
[typeof(bool)] = (s) => bool.Parse(s),
@@ -18,23 +24,47 @@ internal struct Parameter
[typeof(double)] = (s) => double.Parse(s, CultureInfo.InvariantCulture.NumberFormat)
};
- public static IEnumerable SupportedTypes => TypeConverters.Keys;
+ public static IEnumerable SupportedTypes => _typeConverters.Keys;
public Type ParameterType { get; }
+ public Type UnderlyingValueType { get; }
public bool IsOptional { get; }
public string Name { get; }
- public bool IsValidParameterType { get; }
+ public ValidationError ValidState { get; }
public Parameter(ParameterInfo parameter)
{
ParameterType = parameter.ParameterType;
- IsOptional = parameter.IsOptional;
+ UnderlyingValueType = ParameterType.GetUnderlyingType();
+ IsOptional = parameter.IsOptional || ParameterType.IsArray;
Name = parameter.Name;
- IsValidParameterType = SupportedTypes.Contains(ParameterType);
+ ValidState = ValidateParameter(parameter);
+ }
+
+ private readonly ValidationError ValidateParameter(ParameterInfo paramInfo)
+ {
+ ValidationError valid = ValidationError.Valid;
+ // arrays MUST be a "params T[]" parameter
+ // this enforces them being last *and* only having one
+ if (ParameterType.IsArray && !paramInfo.IsDefined(typeof(ParamArrayAttribute), false))
+ valid |= ValidationError.ArrayNotParams;
+ if (!_typeConverters.ContainsKey(UnderlyingValueType))
+ valid |= ValidationError.UnsupportedType;
+
+ return valid;
}
public object Parse(string input)
{
- return TypeConverters[ParameterType](input);
+ Type paramType = ParameterType;
+ if (paramType.TryUnwrapArrayType(out Type elementType))
+ paramType = elementType;
+ if (paramType.TryUnwrapNullableType(out _))
+ {
+ if (string.Equals(input, "null", StringComparison.OrdinalIgnoreCase))
+ return null;
+ }
+
+ return _typeConverters[UnderlyingValueType](input);
}
}
\ No newline at end of file
diff --git a/Nautilus/Extensions/GeneralExtensions.cs b/Nautilus/Extensions/GeneralExtensions.cs
index b85bfe9f..819f52d6 100644
--- a/Nautilus/Extensions/GeneralExtensions.cs
+++ b/Nautilus/Extensions/GeneralExtensions.cs
@@ -1,4 +1,6 @@
using System;
+using System.Collections.Generic;
+using System.Text;
using UnityEngine;
namespace Nautilus.Extensions;
@@ -43,4 +45,18 @@ public static void AddHint(this ErrorMessage @this, string message)
else if (msg.timeEnd <= Time.time + @this.timeFadeOut)
msg.timeEnd += @this.timeFadeOut + @this.timeInvisible;
}
+
+ ///
+ /// Concatenates string representations of the provided ,
+ /// using the specified between them.
+ ///
+ /// Type of value that will be converted to .
+ /// The .
+ /// The to insert between each pair of values.
+ /// Values to concatenate into the .
+ /// The provided .
+ public static StringBuilder AppendJoin(this StringBuilder builder, string separator, IEnumerable values)
+ {
+ return builder.Append(string.Join(separator, values));
+ }
}
\ No newline at end of file
diff --git a/Nautilus/Extensions/TypeExtensions.cs b/Nautilus/Extensions/TypeExtensions.cs
new file mode 100644
index 00000000..87536ae3
--- /dev/null
+++ b/Nautilus/Extensions/TypeExtensions.cs
@@ -0,0 +1,85 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using HarmonyLib;
+
+namespace Nautilus.Extensions;
+
+internal static class TypeExtensions
+{
+ private static readonly List _builtinTypeAliases = new()
+ {
+ "void",
+ null, // all other types
+ "DBNull",
+ "bool",
+ "char",
+ "sbyte",
+ "byte",
+ "short",
+ "ushort",
+ "int",
+ "uint",
+ "long",
+ "ulong",
+ "float",
+ "double",
+ "decimal",
+ null, // DateTime?
+ null, // ???
+ "string"
+ };
+
+ ///
+ /// Format the given 's name into a more developer-friendly form.
+ ///
+ ///
+ ///
+ public static string GetFriendlyName(this Type type)
+ {
+ if (type.TryUnwrapArrayType(out Type elementType))
+ return GetFriendlyName(elementType) + "[]";
+
+ if (type.TryUnwrapNullableType(out Type valueType))
+ return GetFriendlyName(valueType) + "?";
+
+ // TODO: format tuples as well
+
+ if (type.IsConstructedGenericType)
+ return type.Name[..type.Name.LastIndexOf('`')]
+ + $"<{type.GenericTypeArguments.Select(GetFriendlyName).Join()}>";
+
+ return _builtinTypeAliases[(int) Type.GetTypeCode(type)] ?? type.Name;
+ }
+
+ ///
+ /// "Unwraps" the inner from an array and/or nullable type.
+ ///
+ ///
+ ///
+ /// The inner type - for example, from a string[], or from bool?.
+ /// If the isn't wrapped, it is returned as-is.
+ ///
+ public static Type GetUnderlyingType(this Type type)
+ {
+ if (type.TryUnwrapArrayType(out Type elementType))
+ type = elementType;
+ if (type.TryUnwrapNullableType(out Type valueType))
+ type = valueType;
+ return type;
+ }
+
+ public static bool TryUnwrapArrayType(this Type type, out Type elementType)
+ {
+ // GetElementType checks if it's an array, pointer, or reference
+ elementType = type.GetElementType();
+ return type.IsArray // restrict to arrays only
+ && elementType != null;
+ }
+
+ public static bool TryUnwrapNullableType(this Type type, out Type valueType)
+ {
+ valueType = Nullable.GetUnderlyingType(type);
+ return valueType != null;
+ }
+}
diff --git a/Nautilus/Handlers/LanguageHandler.cs b/Nautilus/Handlers/LanguageHandler.cs
index 2f8fc85d..ef75ff58 100644
--- a/Nautilus/Handlers/LanguageHandler.cs
+++ b/Nautilus/Handlers/LanguageHandler.cs
@@ -45,7 +45,22 @@ public static void RegisterLocalizationFolder(string languageFolderName = "Local
foreach (var file in Directory.GetFiles(path))
{
- var content = JsonConvert.DeserializeObject>(File.ReadAllText(file));
+ if (Path.GetExtension(file) != ".json")
+ {
+ continue;
+ }
+
+ // I hate this
+ Dictionary content = null;
+ try
+ {
+ content = JsonConvert.DeserializeObject>(File.ReadAllText(file));
+ }
+ catch (Exception e)
+ {
+ InternalLogger.Error($"Exception caught while trying to deserialize localization file at path: '{file}'. Exception: {e}");
+ }
+
if (content is null)
{
InternalLogger.Warn($"Localization file '{file}' is empty, skipping registration.");
diff --git a/Nautilus/Handlers/LootDistributionHandler.cs b/Nautilus/Handlers/LootDistributionHandler.cs
index 59087292..00f180b7 100644
--- a/Nautilus/Handlers/LootDistributionHandler.cs
+++ b/Nautilus/Handlers/LootDistributionHandler.cs
@@ -95,6 +95,8 @@ public static void AddLootDistributionData(string classId, LootDistributionData.
/// The dictating how the prefab should spawn in the world.
public static void AddLootDistributionData(string classId, params LootDistributionData.BiomeData[] biomeDistribution)
{
+ CraftData.PreparePrefabIDCache();
+
if (!PrefabDatabase.TryGetPrefabFilename(classId, out var filename))
{
InternalLogger.Error($"Could not find prefab file path for class ID '{classId}'. Cancelling loot distribution addition.");
@@ -112,6 +114,8 @@ public static void AddLootDistributionData(string classId, params LootDistributi
/// The dictating how the prefab should spawn in the world.
public static void AddLootDistributionData(string classId, WorldEntityInfo info, params LootDistributionData.BiomeData[] biomeDistribution)
{
+ CraftData.PreparePrefabIDCache();
+
if (!PrefabDatabase.TryGetPrefabFilename(classId, out var filename))
{
InternalLogger.Error($"Could not find prefab file path for class ID '{classId}'. Cancelling loot distribution addition.");
diff --git a/Nautilus/Nautilus.csproj b/Nautilus/Nautilus.csproj
index 8123602d..ae1db35c 100644
--- a/Nautilus/Nautilus.csproj
+++ b/Nautilus/Nautilus.csproj
@@ -1,4 +1,4 @@
-
+
@@ -8,7 +8,7 @@
Nautilus
Nautilus
com.snmodding.nautilus
- $(VersionPrefix)
+ $(VersionPrefix).$(SuffixNumber)
11
true
true
diff --git a/Nautilus/Patchers/ConsoleCommandsPatcher.cs b/Nautilus/Patchers/ConsoleCommandsPatcher.cs
index 197488d9..12a08dd5 100644
--- a/Nautilus/Patchers/ConsoleCommandsPatcher.cs
+++ b/Nautilus/Patchers/ConsoleCommandsPatcher.cs
@@ -1,11 +1,13 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
+using System.Text;
using System.Text.RegularExpressions;
using BepInEx.Logging;
using HarmonyLib;
using Nautilus.Commands;
+using Nautilus.Extensions;
using Nautilus.Utility;
using UnityEngine;
@@ -13,7 +15,7 @@ namespace Nautilus.Patchers;
internal static class ConsoleCommandsPatcher
{
- private static Dictionary ConsoleCommands = new();
+ private static Dictionary ConsoleCommands = new(StringComparer.OrdinalIgnoreCase);
private static Color CommandColor = new(1, 1, 0);
private static Color ParameterTypeColor = new(0, 1, 1);
@@ -63,17 +65,41 @@ public static void AddCustomCommand(string command, MethodInfo targetMethod, boo
return;
}
- // if any of the parameter types of the method are unsupported, print an error and don't add it
- if (!consoleCommand.HasValidParameterTypes())
+ // if any of the parameters of the method aren't valid, print an error and don't add it
+ if (consoleCommand.GetInvalidParameters().Any())
{
- string error = $"Could not register custom command {GetColoredString(consoleCommand)} for mod " +
- $"{GetColoredString(consoleCommand.ModName, ModOriginColor)}\n" +
- "The following parameters have unsupported types:\n" +
- consoleCommand.GetInvalidParameters().Select(param => GetColoredString(param)).Join(delimiter: "\n") +
- "Supported parameter types:\n" +
- Parameter.SupportedTypes.Select(type => type.Name).Join();
+ List parametersWithUnsupportedTypes = new();
+ List nonParamsArrayParameters = new();
+ foreach (var parameter in consoleCommand.GetInvalidParameters())
+ {
+ Parameter.ValidationError state = parameter.ValidState;
+ if (state.HasFlag(Parameter.ValidationError.UnsupportedType))
+ parametersWithUnsupportedTypes.Add(parameter);
+ if (state.HasFlag(Parameter.ValidationError.ArrayNotParams))
+ nonParamsArrayParameters.Add(parameter);
+ }
+
+ StringBuilder error = new StringBuilder(
+ $"Could not register custom command {GetColoredString(consoleCommand)} for mod " +
+ $"{GetColoredString(consoleCommand.ModName, ModOriginColor)}\n"
+ );
- LogAndAnnounce(error, LogLevel.Error);
+ if (parametersWithUnsupportedTypes.Count > 0)
+ {
+ error.AppendLine("The following parameters have unsupported types:");
+ error.AppendJoin("\n", parametersWithUnsupportedTypes.Select(GetColoredString));
+ error.AppendLine("\nSupported parameter types:");
+ error.AppendJoin(",", Parameter.SupportedTypes.Select(type => type.GetFriendlyName()));
+ }
+
+ if (nonParamsArrayParameters.Count > 0)
+ {
+ error.AppendLine("Array parameters must be marked as 'params'.");
+ error.AppendLine("Incorrect parameters:");
+ error.AppendJoin(",", nonParamsArrayParameters.Select(GetColoredString));
+ }
+
+ LogAndAnnounce(error.ToString(), LogLevel.Error);
return;
}
@@ -148,7 +174,7 @@ private static bool HandleCommand(string input)
input = input.Trim();
string[] components = input.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
- string trigger = components[0].ToLowerInvariant();
+ string trigger = components[0];
if (!ConsoleCommands.TryGetValue(trigger, out ConsoleCommand command))
{
@@ -156,30 +182,39 @@ private static bool HandleCommand(string input)
return false;
}
- IEnumerable parameters = components.Skip(1);
+ List inputParameters = components.Skip(1).ToList();
+
+ (int consumed, int parsed) = command.TryParseParameters(inputParameters, out object[] parsedParameters);
// If the parameters couldn't be parsed by the command, print a user and developer-friendly error message both
// on-screen and in the log.
- if (!command.TryParseParameters(parameters, out object[] parsedParameters))
+ bool consumedAll = consumed >= inputParameters.Count;
+ bool parsedAll = parsed == command.Parameters.Count;
+ if (!consumedAll || !parsedAll)
{
- if (parsedParameters != null)
+ if (!parsedAll)
{
- // Find the first invalid parameter
- string invalidParameter = null;
- string parameterTypeName = null;
- for (int i = 0; i < parsedParameters.Length; i++)
+ if (parsedParameters != null)
{
- if (parsedParameters[i] == null)
- {
- invalidParameter = parameters.ElementAt(i);
- parameterTypeName = command.ParameterTypes[i].Name;
- break;
- }
- }
+ // Get the first invalid parameter
+ string invalidInput = inputParameters[consumed];
+
+ var invalidParameter = command.Parameters[parsed];
+ string parameterTypeName = invalidParameter.UnderlyingValueType.GetFriendlyName();
- // Print a message about why it is invalid
- string error = $"{GetColoredString(invalidParameter, ParameterInputColor)} is not a valid " +
- $"{GetColoredString(parameterTypeName, ParameterTypeColor)}!";
+ // Print a message about why it is invalid
+ string error = $"Parameter {GetColoredString(invalidParameter.Name, ParameterOptionalColor)} could not be parsed:\n" +
+ $"{GetColoredString(invalidInput, ParameterInputColor)} is not a valid " +
+ $"{GetColoredString(parameterTypeName, ParameterTypeColor)}!";
+
+ LogAndAnnounce(error, LogLevel.Error);
+ }
+ }
+ else if (!consumedAll)
+ {
+ string error = "Received too many parameters!\n" +
+ $"expected {GetColoredString(command.Parameters.Count.ToString(), ParameterTypeColor)} " +
+ $"but got {GetColoredString(inputParameters.Count.ToString(), ParameterInputColor)}";
LogAndAnnounce(error, LogLevel.Error);
}
@@ -187,14 +222,14 @@ private static bool HandleCommand(string input)
// Print a message about what parameters the command expects
string parameterInfoString = $"{GetColoredString(command.Trigger, CommandColor)} " +
"expects the following parameters\n" +
- command.Parameters.Select(param => GetColoredString(param)).Join(delimiter: "\n");
+ command.Parameters.Select(GetColoredString).Join(delimiter: "\n");
LogAndAnnounce(parameterInfoString, LogLevel.Error);
// Print a message detailing all received parameters.
- if (parameters.Any())
+ if (inputParameters.Any())
{
- InternalLogger.Announce($"Received parameters: {parameters.Join()}", LogLevel.Error, true);
+ InternalLogger.Announce($"Received parameters: {inputParameters.Join()}", LogLevel.Error, true);
}
return true; // We've handled the command insofar as we've handled and reported the user error to them.
@@ -232,7 +267,7 @@ private static string GetColoredString(ConsoleCommand command)
private static string GetColoredString(Parameter parameter)
{
- return $"{parameter.Name}: {GetColoredString(parameter.ParameterType.Name, ParameterTypeColor)}" +
+ return $"{parameter.Name}: {GetColoredString(parameter.ParameterType.GetFriendlyName(), ParameterTypeColor)}" +
(parameter.IsOptional ? $" {GetColoredString("(optional)", ParameterOptionalColor)}" : string.Empty);
}
diff --git a/Nautilus/Patchers/PrefabDatabasePatcher.cs b/Nautilus/Patchers/PrefabDatabasePatcher.cs
index 8ea415bc..172f2251 100644
--- a/Nautilus/Patchers/PrefabDatabasePatcher.cs
+++ b/Nautilus/Patchers/PrefabDatabasePatcher.cs
@@ -80,7 +80,14 @@ private static IPrefabRequest GetModPrefabAsync(string classId)
}
if(ModPrefabCache.Requests.TryGetValue(prefabInfo.ClassID, out var request))
+ {
+ if (request.Done && !request.TryGetPrefab(out _))
+ {
+ return new ModPrefabRequest(prefabInfo);
+ }
+
return request;
+ }
return new ModPrefabRequest(prefabInfo);
}
diff --git a/PostBuild.targets b/PostBuild.targets
index 6b3968e5..52552125 100644
--- a/PostBuild.targets
+++ b/PostBuild.targets
@@ -1,11 +1,11 @@
-
+
Nautilus
$(OutDir)\plugins
$([System.IO.Path]::Combine('$(PluginsDir)', '$(BuildPath)'))
- $([System.IO.Path]::Combine('$(TargetDir)', 'Nautilus_$(ConfigurationName).zip'))
+ $([System.IO.Path]::Combine('$(TargetDir)', 'Nautilus_$(ConfigurationName)_$(BepInExPluginVersion).zip'))
$([System.IO.Path]::Combine('$(OutDir)', 'TemporaryDir'))
$([System.IO.Path]::Combine('$(TemporaryDir)', 'Build'))
diff --git a/UploadHelper/Program.cs b/UploadHelper/Program.cs
index e8022ae6..2ae9414a 100644
--- a/UploadHelper/Program.cs
+++ b/UploadHelper/Program.cs
@@ -8,10 +8,15 @@ internal static class Program
private const string VersionPrefixStart = "";
private const string VersionPrefixEnd = "";
+ private const string SuffixNumberStart = "";
+ private const string SuffixNumberEnd = "";
private const string VersionSuffixStart = "";
private const string VersionSuffixEnd = "";
+ private static string _versionPrefix;
+ private static int _versionSuffix;
+ private static Version _version;
- private static string[] _uploadPageURLs = new string[]
+ private static readonly string[] _uploadPageURLs = new string[]
{
"https://github.com/SubnauticaModding/Nautilus/releases",
"https://www.submodica.xyz/mods/sn1/250",
@@ -22,171 +27,275 @@ internal static class Program
public static void Main(string[] args)
{
+ Console.Clear();
// essential variables
- _nautilusDirectory = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "..", "..", "..");
-
- // greeting
- Console.WriteLine("Welcome to the Upload Helper for Nautilus. This program should become obsolete as soon as we set up a proper build deployment system.\n");
+ _nautilusDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+ var solutionPath = "";
+ while (_nautilusDirectory != null)
+ {
+ var file = Path.Combine(_nautilusDirectory, "Nautilus.sln");
+ if (File.Exists(file))
+ {
+ solutionPath = file;
+ break;
+ }
- Console.WriteLine("Press ENTER to begin...");
- Console.ReadLine();
+ _nautilusDirectory = Directory.GetParent(_nautilusDirectory)?.FullName;
+ }
- // get old version
- Console.WriteLine("First of all, we need to determine the version string.");
- var oldVersion = GetCurrentVersionString();
- Console.WriteLine($"I think the current version string is {oldVersion} (suffixes such as the pre-release number might not be shown here), but it's a good idea to double check.\n");
- Console.WriteLine("When you press enter, I will open https://www.submodica.xyz/mods/sn1/246 and https://github.com/SubnauticaModding/Nautilus/releases in your browser so you can find the version string that is currently uploaded to the internet.");
- Console.WriteLine("If you don't want this, just type something before you hit enter.");
- if (string.IsNullOrEmpty(Console.ReadLine()))
+ if (string.IsNullOrWhiteSpace(solutionPath))
{
- Process.Start("explorer", "https://www.submodica.xyz/mods/sn1/250");
- Thread.Sleep(1000);
- Process.Start("explorer", "https://github.com/SubnauticaModding/Nautilus/releases");
+ Console.WriteLine($"Could not find the Nautilus solution in any parent directory.");
+ Console.ReadLine();
+ return;
}
- // determine new version
-
- Console.WriteLine("Please write the NEW version number here (do NOT include the pre-release suffix): ");
- var versionPrefix = Console.ReadLine();
- if (versionPrefix != null && versionPrefix.StartsWith("v"))
+ var nautilusProjectPath = Path.Combine(_nautilusDirectory, "Nautilus", "Nautilus.csproj");
+ if (!File.Exists(nautilusProjectPath))
{
- Console.WriteLine("Hm, why does it start with a V? Are you sure you meant to do that? If not, type R and we can retry that.");
- var line = Console.ReadLine();
- if (line != null && line.ToLower() == "r")
- {
- Console.Write("Version number: ");
- versionPrefix = Console.ReadLine();
- }
+ Console.WriteLine($"Could not find the Nautilus project at {nautilusProjectPath}");
+ Console.ReadLine();
+ return;
}
- Console.WriteLine("\nAnd now, if applicable, send the pre-release number (or leave empty): ");
- string prereleaseNum = Console.ReadLine();
- string versionString = versionPrefix;
+ // greeting
+ Console.WriteLine("Welcome to the Upload Helper for Nautilus. \nThis program should become obsolete as soon as we set up a proper build deployment system.\n");
- if (!string.IsNullOrEmpty(prereleaseNum))
+ if (!Ask("Do you want to begin? (y/n)"))
{
- versionString += "-pre." + prereleaseNum;
- Console.WriteLine($"\nOh, you want to add \"pre.{prereleaseNum}\" to the version suffix? Sure, just remember we have to remove that when making the builds that we distribute.");
- Console.WriteLine("If you are wondering why, that is because the BepInEx plugin.");
+ Console.Clear();
+ Console.WriteLine("Alright, goodbye!");
+ return;
}
- Console.WriteLine($"\nAlright, thanks! We’ll use {versionString} for this release.");
-
- SetCurrentVersionString(versionPrefix, "pre." + prereleaseNum);
-
- Console.WriteLine("\nThe Version.targets file was automatically updated. Remember that we have to fix that later.");
- Console.WriteLine("\nNow, let's work on getting the NuGet package up and running.");
+ Console.Clear();
- Console.WriteLine("I should warn you now that you’ll need to log in to upload your update (for security reasons)." +
- "\nPlease contact an administrator if you need help, otherwise we’ll continue from here.");
-
- WalkThroughNuGetSteps("SN.STABLE");
-
- WalkThroughNuGetSteps("BZ.STABLE");
+ // get old version
+ Console.WriteLine("First of all, we need to determine the version string.");
- Console.WriteLine("\nRemember, these two versions you built are ONLY used for NUGET DEPENDENCIES. The version you have now should NOT be used in-game.");
+ Console.WriteLine("You can check https://github.com/SubnauticaModding/Nautilus/releases to find the version that is currently uploaded.");
- Console.WriteLine("Alright, great, did everything work out? I’m having connection issues and I can’t see your responses, so I’ll assume that’s a yes.");
+ if (Ask("Do you want to open this in your browser? (y/n)"))
+ Process.Start("explorer", "https://github.com/SubnauticaModding/Nautilus/releases");
- SetCurrentVersionString(versionPrefix, null);
+ try
+ {
+ GetCurrentVersionString();
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine(e.Message);
+ Console.ReadLine();
+ return;
+ }
+ var versionPrefix = _versionPrefix;
+ var suffixNumber = _versionSuffix;
- Console.WriteLine("\nNow let’s work on getting this update pushed out to our users. First, we need to remove the prerelease tags from the assembly." +
- "\nI’ve done that for you already. Now build for BOTH versions of the game.");
+ // determine new version
+ if (!Ask($"I think the current version string is {versionPrefix}.{suffixNumber}. Do you want to keep it? (y/n)"))
+ {
+ Console.WriteLine("Alright, we'll change it.");
+ Console.WriteLine("Please write the NEW 3 digit version number here (eg: 1.0.0) (do NOT include the pre-release suffix): ");
+ versionPrefix = RequestVersion();
+ Version v;
+ while (!Version.TryParse(versionPrefix, out v) || v.Major < _version.Major || v.Minor < _version.Minor || v.Build < _version.Build)
+ {
+ Console.Clear();
+ Console.WriteLine("That doesn't look like a valid version number or is less than the current version. Please try again.");
+ Console.WriteLine("Please write the NEW 3 digit version number here (eg: 1.0.0) (do NOT include the pre-release suffix): ");
+ versionPrefix = RequestVersion();
+ }
- Console.WriteLine("Press enter after you have ONCE AGAIN built the project for both SN.STABLE and BZ.STABLE.");
+ var isEqual = v.Major == _version.Major && v.Minor == _version.Minor && v.Build == _version.Build;
+ var response = isEqual || Ask("Do you want to add a pre-release suffix? (y/n)");
+ while (response)
+ {
+ Console.WriteLine("What should the pre-release suffix be?");
+ var prereleaseNum = Console.ReadLine();
+ if (string.IsNullOrEmpty(prereleaseNum) || !int.TryParse(prereleaseNum, out suffixNumber))
+ {
+ if (!Ask("That doesn't look like an intiger. Do you want to try again? (y/n)"))
+ break;
+
+ Console.Clear();
+ continue;
+ }
+
+ if (isEqual && suffixNumber < _versionSuffix)
+ {
+ Console.Clear();
+ Console.WriteLine("That's less than the current pre-release suffix. I'm not sure what you're trying to do, but I'm not going to let you do it.");
+ continue;
+ }
+
+ if (suffixNumber < 0)
+ {
+ Console.Clear();
+ Console.WriteLine("That's a negative number. I'm not sure what you're trying to do, but I'm not going to let you do it.");
+ continue;
+ }
+
+ if (suffixNumber == 0)
+ {
+ Console.Clear();
+ Console.WriteLine("That's a zero. I'm not sure what you're trying to do, but I'm not going to let you do it.");
+ if (!Ask("Do you want to try again? (y/n)"))
+ break;
+ continue;
+ }
+
+ Console.Clear();
+ if (Ask($"\nAdd \"{prereleaseNum}\" as the pre-release suffix? (y/n)"))
+ break;
+ Console.Clear();
+ response = isEqual || Ask("Do you want to add a pre-release suffix? (y/n)");
+ }
+ }
- Console.ReadLine();
+ Console.WriteLine($"\nAlright, thanks! We’ll use {versionPrefix}.{suffixNumber} for this release.");
+ SetCurrentVersionString(versionPrefix, suffixNumber);
- Console.WriteLine("Press enter and I will open all the relevant pages where these mods should be uploaded in your browser.");
+ Console.WriteLine("\nNow, let's work on getting the NuGet packages up and running.");
+ Console.WriteLine("I should warn you now that you’ll need to log in to upload your update (for security reasons)." +
+ "\nPlease contact an administrator if you need help, otherwise we’ll continue from here.");
- Console.ReadLine();
+ RebuildNautilus(nautilusProjectPath);
foreach (var url in _uploadPageURLs)
{
+ if (!Ask($"Do you want to open {url} to upload? (y/n)"))
+ continue;
+
Console.WriteLine("Opening " + url + "...");
Process.Start("explorer", url);
Thread.Sleep(500);
+ Console.Clear();
}
+ Console.Clear();
+
Console.WriteLine("Congratulations, you're done!!!");
// END
Console.ReadLine();
}
- private static bool Ask(string prompt)
+ private static void RebuildNautilus(string projectPath)
{
- Console.WriteLine(prompt);
- var l = Console.ReadLine();
- return !string.IsNullOrEmpty(l) && l.ToLower() == "y";
- }
-
- private static string VersionTargetsPath => Path.Combine(_nautilusDirectory, "Version.targets");
+ // Start a new process
+ var process = new Process()
+ {
+ StartInfo = new ProcessStartInfo()
+ {
+ FileName = "cmd.exe",
+ RedirectStandardInput = true,
+ RedirectStandardOutput = true,
+ UseShellExecute = false
+ }
+ };
- private static void RebuildAuthorsTable()
- {
- var proc = new Process();
- proc.StartInfo.FileName = Path.Combine(_nautilusDirectory, "AuthorsTableGenerator", "AuthorTableGenerator.exe");
- proc.Start();
- proc.WaitForExit();
- proc.Close();
- }
+ process.Start();
- private static void WalkThroughNuGetSteps(string branch)
- {
- Console.WriteLine($"\nIn your IDE, switch to the {branch} build configuration, build the project, and then press ENTER in this window when you have finished.");
+ // Define the configurations to build
+ var configurations = new string[] { "SN.STABLE", "BZ.STABLE" };
- Console.ReadLine();
+ // Execute the build with nuget restore for each configuration.
+ foreach (var configuration in configurations)
+ process.StandardInput.WriteLine($"dotnet build \"{projectPath}\" /restore /p:Configuration={configuration}");
- Console.WriteLine("All built and ready? When you press ENTER I will open the folder containing the built files.");
+ process.StandardInput.WriteLine("exit");
- Console.ReadLine();
+ Console.WriteLine(process.StandardOutput.ReadToEnd());
+ process.WaitForExit();
+ Console.WriteLine("Now you need to upload the.nupkg files.");
- Process.Start("explorer", Path.Combine(_nautilusDirectory, "Nautilus", "bin", branch));
+ if (Ask("Do you want to open https://www.nuget.org/packages/manage/upload in your browser? (y/n)"))
+ Process.Start("explorer", "https://www.nuget.org/packages/manage/upload");
- Console.WriteLine("Got it? Now you need to upload the correct .nupkg file at https://www.nuget.org/packages/manage/upload. Press ENTER to open that in your browser.");
+ if (Ask($"Do you want to open the output paths in explorer? (y/n)"))
+ foreach (var configuration in configurations)
+ Process.Start("explorer", Path.Combine(_nautilusDirectory, "Nautilus", "bin", configuration));
+ Console.WriteLine("Press enter to continue.");
Console.ReadLine();
+ }
+
+ private static string RequestVersion()
+ {
+ var versionPrefix = Console.ReadLine();
+ if (!string.IsNullOrWhiteSpace(versionPrefix) && versionPrefix.StartsWith("v"))
+ {
+ Console.WriteLine("Hm, why does it start with a V? Are you sure you meant to do that? If not, type R and we can retry that.");
+ var line = Console.ReadLine();
+ if (line != null && line.ToLower() == "r")
+ {
+ Console.Write("Version number: ");
+ versionPrefix = Console.ReadLine();
+ }
+ }
+ else if (string.IsNullOrWhiteSpace(versionPrefix))
+ {
+ Console.WriteLine("You didn't write anything. I'll assume you want to keep the current version number.");
+ versionPrefix = _versionPrefix;
+ }
+
+ return versionPrefix;
+ }
- Process.Start("explorer", "https://www.nuget.org/packages/manage/upload");
+ private static bool Ask(string prompt)
+ {
+ Console.WriteLine(prompt);
+ var l = Console.ReadLine();
+ while (string.IsNullOrWhiteSpace(l) || (l.ToLower() != "y" && l.ToLower() != "n"))
+ {
+ Console.WriteLine("Please answer with y or n.");
+ Console.WriteLine(prompt);
+ l = Console.ReadLine();
+ }
+ return l.ToLower() == "y";
}
- private static string GetCurrentVersionString()
+ private static string VersionTargetsPath => Path.Combine(_nautilusDirectory, "Version.targets");
+
+ private static void GetCurrentVersionString()
{
var text = File.ReadAllText(VersionTargetsPath);
var prefixStartIndex = text.IndexOf(VersionPrefixStart) + VersionPrefixStart.Length;
var prefixLength = text.IndexOf(VersionPrefixEnd) - prefixStartIndex;
- string prefix = text.Substring(prefixStartIndex, prefixLength);
+ var prefix = text.Substring(prefixStartIndex, prefixLength);
string suffix = null;
- if (text.Contains(VersionSuffixStart))
+ if (text.Contains(SuffixNumberStart))
{
- var suffixStartIndex = text.IndexOf(VersionSuffixStart) + VersionSuffixStart.Length;
- var suffixLength = text.IndexOf(VersionSuffixEnd) - suffixStartIndex;
+ var suffixStartIndex = text.IndexOf(SuffixNumberStart) + SuffixNumberStart.Length;
+ var suffixLength = text.IndexOf(SuffixNumberEnd) - suffixStartIndex;
suffix = text.Substring(suffixStartIndex, suffixLength);
}
- if (string.IsNullOrEmpty(suffix)) return prefix;
+ if (string.IsNullOrWhiteSpace(prefix) || !Version.TryParse(prefix, out _version))
+ throw new Exception("The VersionPrefix in Version.targets is not a valid version number. I'm not sure what you're trying to do, but I'm not going to let you do it.");
- else return prefix + "-" + suffix;
+ _versionPrefix = prefix;
+ if (string.IsNullOrWhiteSpace(suffix) || !int.TryParse(suffix, out _versionSuffix))
+ throw new Exception("The SuffixNumber in Version.targets is not a number. I'm not sure what you're trying to do, but I'm not going to let you do it.");
}
- public static void SetCurrentVersionString(string prefix, string suffix = null)
+ public static void SetCurrentVersionString(string prefix, int suffix)
{
var text = File.ReadAllText(VersionTargetsPath);
- var split = text.Split(new string[] { VersionSuffixStart, VersionSuffixEnd, VersionPrefixStart, VersionPrefixEnd }, StringSplitOptions.None);
+ var split = text.Split(new string[] { VersionSuffixStart, VersionSuffixEnd, SuffixNumberStart, SuffixNumberEnd, VersionPrefixStart, VersionPrefixEnd }, StringSplitOptions.None);
- StringBuilder sb = new StringBuilder();
+ var sb = new StringBuilder();
sb.AppendLine(split[0].TrimEnd());
sb.AppendLine(" " + VersionPrefixStart + prefix + VersionPrefixEnd);
- if (!string.IsNullOrEmpty(suffix))
- {
- sb.AppendLine(" " + VersionSuffixStart + suffix + VersionSuffixEnd);
- }
- sb.Append(" " + split[split.Length - 1].TrimStart());
+ sb.AppendLine(" " + SuffixNumberStart + suffix + SuffixNumberEnd);
+ if (suffix > 0)
+ sb.AppendLine(" " + VersionSuffixStart + "pre.$(SuffixNumber)" + VersionSuffixEnd);
+
+ sb.Append(" " + split[^1].TrimStart());
var final = sb.ToString();
diff --git a/Version.targets b/Version.targets
index 9e43a2da..69107f7f 100644
--- a/Version.targets
+++ b/Version.targets
@@ -3,5 +3,7 @@
1.0.0
+ 23
+ pre.$(SuffixNumber)
\ No newline at end of file