summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build/common.targets5
-rw-r--r--docs/release-notes.md2
-rw-r--r--docs/technical/mod-package.md10
-rw-r--r--docs/technical/smapi.md4
-rw-r--r--src/SMAPI.ModBuildConfig.Analyzer/SMAPI.ModBuildConfig.Analyzer.csproj2
-rw-r--r--src/SMAPI.ModBuildConfig/SMAPI.ModBuildConfig.csproj2
-rw-r--r--src/SMAPI.Toolkit.CoreInterfaces/SMAPI.Toolkit.CoreInterfaces.csproj2
-rw-r--r--src/SMAPI.Toolkit/Framework/SemanticVersionReader.cs7
-rw-r--r--src/SMAPI.Toolkit/Framework/UpdateData/UpdateKey.cs2
-rw-r--r--src/SMAPI.Toolkit/SMAPI.Toolkit.csproj2
-rw-r--r--src/SMAPI.Toolkit/SemanticVersion.cs14
-rw-r--r--src/SMAPI.Toolkit/Serialization/JsonHelper.cs7
-rw-r--r--src/SMAPI.Toolkit/Serialization/Models/Manifest.cs2
-rw-r--r--src/SMAPI.Toolkit/Serialization/Models/ManifestContentPackFor.cs2
-rw-r--r--src/SMAPI.Toolkit/Serialization/Models/ManifestDependency.cs2
-rw-r--r--src/SMAPI.Toolkit/Utilities/PathUtilities.cs42
-rw-r--r--src/SMAPI/Events/AssetRequestedEventArgs.cs2
-rw-r--r--src/SMAPI/Framework/SCore.cs4
-rw-r--r--src/SMAPI/Program.cs19
19 files changed, 105 insertions, 27 deletions
diff --git a/build/common.targets b/build/common.targets
index 258b48f2..c227190a 100644
--- a/build/common.targets
+++ b/build/common.targets
@@ -5,7 +5,10 @@
<Product>SMAPI</Product>
<LangVersion>latest</LangVersion>
<AssemblySearchPaths>$(AssemblySearchPaths);{GAC}</AssemblySearchPaths>
- <Nullable>enable</Nullable>
+
+ <!--enable nullable annotations, except in .NET Standard 2.0 where they aren't supported-->
+ <Nullable Condition="'$(TargetFramework)' == 'net5.0'">enable</Nullable>
+ <NoWarn Condition="'$(TargetFramework)' != 'net5.0'">$(NoWarn);CS8632</NoWarn>
<!--set platform-->
<DefineConstants Condition="$(OS) == 'Windows_NT'">$(DefineConstants);SMAPI_FOR_WINDOWS</DefineConstants>
diff --git a/docs/release-notes.md b/docs/release-notes.md
index bb30f31a..9fc0d432 100644
--- a/docs/release-notes.md
+++ b/docs/release-notes.md
@@ -20,7 +20,6 @@
* Added `data-*` attributes to the log parser page for external tools.
* Fixed JSON validator showing incorrect error for update keys without a subkey.
-
### For mod authors
This is a big release that includes the new APIs planned for SMAPI 4.0.0, alongside the old ones.
@@ -40,6 +39,7 @@ the C# mod that loads them is updated.
_This adds support for many previously unsupported cases: proxied interfaces in return values or input arguments, proxied enums if their values match, generic methods, and more. Existing mod APIs should work fine as-is._
* Mod files loaded through SMAPI APIs (including `helper.Content.Load`) are now case-insensitive, even on Linux.
* Other improvements:
+ * Added [command-line arguments](technical/smapi.md#command-line-arguments) to toggle developer mode (thanks to Tondorian!).
* Added `IContentPack.ModContent` property.
* Added `Constants.ContentPath`.
* Added `IAssetName` fields to the info received by `IAssetEditor` and `IAssetLoader` methods.
diff --git a/docs/technical/mod-package.md b/docs/technical/mod-package.md
index 6123b28f..4c31f69b 100644
--- a/docs/technical/mod-package.md
+++ b/docs/technical/mod-package.md
@@ -413,19 +413,9 @@ when you compile it.
## Release notes
## Upcoming release
-* Migrated from .NET Standard 2.0 to .NET 5.0.
* Added detection for Xbox app game folders.
* Internal refactoring.
-**Troubleshooting:**
-Due to the framework change, you might see build errors like _task failed unexpectedly_ that mentions `System.Runtime Version=5.0.0`. If so:
-
-1. Make sure you have Visual Studio 2022 or later.
-2. Exit all instances of Visual Studio.
-3. Delete the hidden `.vs` folder in your solution folder.
-4. Delete the `bin` and `obj` folders in each project folder.
-5. Reopen the solution and rebuild, and now it should work fine.
-
## 4.0.0
Released 30 November 2021.
diff --git a/docs/technical/smapi.md b/docs/technical/smapi.md
index 7da1e0f1..e117db2f 100644
--- a/docs/technical/smapi.md
+++ b/docs/technical/smapi.md
@@ -33,11 +33,12 @@ argument | purpose
`--uninstall` | Preselects the uninstall action, skipping the prompt asking what the user wants to do.
`--game-path "path"` | Specifies the full path to the folder containing the Stardew Valley executable, skipping automatic detection and any prompt to choose a path. If the path is not valid, the installer displays an error.
-SMAPI itself recognises two arguments **on Windows only**, but these are intended for internal use
+SMAPI itself recognises five arguments **on Windows only**, but these are intended for internal use
or testing and may change without warning. On Linux/macOS, see _environment variables_ below.
argument | purpose
-------- | -------
+`--developer-mode`<br />`--developer-mode-off` | Enable or disable features intended for mod developers. Currently this only makes `TRACE`-level messages appear in the console.
`--no-terminal` | The SMAPI launcher won't try to open a terminal window, and SMAPI won't log anything to the console. (Messages will still be written to the log file.)
`--use-current-shell` | The SMAPI launcher won't try to open a terminal window, but SMAPI will still log to the console. (Messages will still be written to the log file.)
`--mods-path` | The path to search for mods, if not the standard `Mods` folder. This can be a path relative to the game folder (like `--mods-path "Mods (test)"`) or an absolute path.
@@ -49,6 +50,7 @@ can set temporary environment variables instead. For example:
environment variable | purpose
-------------------- | -------
+`SMAPI_DEVELOPER_MODE` | Equivalent to `--developer-mode` and `--developer-mode-off` above. The value must be `true` or `false`.
`SMAPI_MODS_PATH` | Equivalent to `--mods-path` above.
`SMAPI_NO_TERMINAL` | Equivalent to `--no-terminal` above.
`SMAPI_USE_CURRENT_SHELL` | Equivalent to `--use-current-shell` above.
diff --git a/src/SMAPI.ModBuildConfig.Analyzer/SMAPI.ModBuildConfig.Analyzer.csproj b/src/SMAPI.ModBuildConfig.Analyzer/SMAPI.ModBuildConfig.Analyzer.csproj
index 69fd3dbd..7ac3277e 100644
--- a/src/SMAPI.ModBuildConfig.Analyzer/SMAPI.ModBuildConfig.Analyzer.csproj
+++ b/src/SMAPI.ModBuildConfig.Analyzer/SMAPI.ModBuildConfig.Analyzer.csproj
@@ -2,7 +2,7 @@
<PropertyGroup>
<RootNamespace>StardewModdingAPI.ModBuildConfig.Analyzer</RootNamespace>
<Version>3.0.0</Version>
- <TargetFramework>net5.0</TargetFramework>
+ <TargetFramework>netstandard2.0</TargetFramework>
<IncludeBuildOutput>false</IncludeBuildOutput>
<OutputPath>bin</OutputPath>
<LangVersion>latest</LangVersion>
diff --git a/src/SMAPI.ModBuildConfig/SMAPI.ModBuildConfig.csproj b/src/SMAPI.ModBuildConfig/SMAPI.ModBuildConfig.csproj
index 8e70293e..82eac7f6 100644
--- a/src/SMAPI.ModBuildConfig/SMAPI.ModBuildConfig.csproj
+++ b/src/SMAPI.ModBuildConfig/SMAPI.ModBuildConfig.csproj
@@ -2,7 +2,7 @@
<PropertyGroup>
<!--build-->
<RootNamespace>StardewModdingAPI.ModBuildConfig</RootNamespace>
- <TargetFramework>net5.0</TargetFramework>
+ <TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>latest</LangVersion>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
diff --git a/src/SMAPI.Toolkit.CoreInterfaces/SMAPI.Toolkit.CoreInterfaces.csproj b/src/SMAPI.Toolkit.CoreInterfaces/SMAPI.Toolkit.CoreInterfaces.csproj
index d69d53d5..4c92b4db 100644
--- a/src/SMAPI.Toolkit.CoreInterfaces/SMAPI.Toolkit.CoreInterfaces.csproj
+++ b/src/SMAPI.Toolkit.CoreInterfaces/SMAPI.Toolkit.CoreInterfaces.csproj
@@ -2,7 +2,7 @@
<PropertyGroup>
<RootNamespace>StardewModdingAPI</RootNamespace>
<Description>Provides toolkit interfaces which are available to SMAPI mods.</Description>
- <TargetFrameworks>net5.0</TargetFrameworks>
+ <TargetFrameworks>net5.0; netstandard2.0</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
diff --git a/src/SMAPI.Toolkit/Framework/SemanticVersionReader.cs b/src/SMAPI.Toolkit/Framework/SemanticVersionReader.cs
index 836b1134..939be771 100644
--- a/src/SMAPI.Toolkit/Framework/SemanticVersionReader.cs
+++ b/src/SMAPI.Toolkit/Framework/SemanticVersionReader.cs
@@ -105,7 +105,12 @@ namespace StardewModdingAPI.Toolkit.Framework
/// <param name="raw">The raw characters to parse.</param>
/// <param name="index">The index of the next character to read.</param>
/// <param name="tag">The parsed tag.</param>
- private static bool TryParseTag(char[] raw, ref int index, [NotNullWhen(true)] out string? tag)
+ private static bool TryParseTag(char[] raw, ref int index,
+#if NET5_0_OR_GREATER
+ [NotNullWhen(true)]
+#endif
+ out string? tag
+ )
{
// read tag length
int length = 0;
diff --git a/src/SMAPI.Toolkit/Framework/UpdateData/UpdateKey.cs b/src/SMAPI.Toolkit/Framework/UpdateData/UpdateKey.cs
index d40d8f2b..4c9ca2ff 100644
--- a/src/SMAPI.Toolkit/Framework/UpdateData/UpdateKey.cs
+++ b/src/SMAPI.Toolkit/Framework/UpdateData/UpdateKey.cs
@@ -16,7 +16,9 @@ namespace StardewModdingAPI.Toolkit.Framework.UpdateData
public ModSiteKey Site { get; }
/// <summary>The mod ID within the repository.</summary>
+#if NET5_0_OR_GREATER
[MemberNotNullWhen(true, nameof(LooksValid))]
+#endif
public string? ID { get; }
/// <summary>If specified, a substring in download names/descriptions to match.</summary>
diff --git a/src/SMAPI.Toolkit/SMAPI.Toolkit.csproj b/src/SMAPI.Toolkit/SMAPI.Toolkit.csproj
index 6a8c4c43..ec27bf79 100644
--- a/src/SMAPI.Toolkit/SMAPI.Toolkit.csproj
+++ b/src/SMAPI.Toolkit/SMAPI.Toolkit.csproj
@@ -2,7 +2,7 @@
<PropertyGroup>
<RootNamespace>StardewModdingAPI.Toolkit</RootNamespace>
<Description>A library which encapsulates mod-handling logic for mod managers and tools. Not intended for use by mods.</Description>
- <TargetFrameworks>net5.0</TargetFrameworks>
+ <TargetFrameworks>net5.0; netstandard2.0</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
diff --git a/src/SMAPI.Toolkit/SemanticVersion.cs b/src/SMAPI.Toolkit/SemanticVersion.cs
index cbdd7a85..2cb27e11 100644
--- a/src/SMAPI.Toolkit/SemanticVersion.cs
+++ b/src/SMAPI.Toolkit/SemanticVersion.cs
@@ -198,7 +198,12 @@ namespace StardewModdingAPI.Toolkit
/// <param name="version">The version string.</param>
/// <param name="parsed">The parsed representation.</param>
/// <returns>Returns whether parsing the version succeeded.</returns>
- public static bool TryParse(string? version, [NotNullWhen(true)] out ISemanticVersion? parsed)
+ public static bool TryParse(string? version,
+#if NET5_0_OR_GREATER
+ [NotNullWhen(true)]
+#endif
+ out ISemanticVersion? parsed
+ )
{
return SemanticVersion.TryParse(version, allowNonStandard: false, out parsed);
}
@@ -208,7 +213,12 @@ namespace StardewModdingAPI.Toolkit
/// <param name="allowNonStandard">Whether to allow non-standard extensions to semantic versioning.</param>
/// <param name="parsed">The parsed representation.</param>
/// <returns>Returns whether parsing the version succeeded.</returns>
- public static bool TryParse(string? version, bool allowNonStandard, [NotNullWhen(true)] out ISemanticVersion? parsed)
+ public static bool TryParse(string? version, bool allowNonStandard,
+#if NET5_0_OR_GREATER
+ [NotNullWhen(true)]
+#endif
+ out ISemanticVersion? parsed
+ )
{
if (version == null)
{
diff --git a/src/SMAPI.Toolkit/Serialization/JsonHelper.cs b/src/SMAPI.Toolkit/Serialization/JsonHelper.cs
index 7d1804e5..3c9308f2 100644
--- a/src/SMAPI.Toolkit/Serialization/JsonHelper.cs
+++ b/src/SMAPI.Toolkit/Serialization/JsonHelper.cs
@@ -37,7 +37,12 @@ namespace StardewModdingAPI.Toolkit.Serialization
/// <returns>Returns false if the file doesn't exist, else true.</returns>
/// <exception cref="ArgumentException">The given <paramref name="fullPath"/> is empty or invalid.</exception>
/// <exception cref="JsonReaderException">The file contains invalid JSON.</exception>
- public bool ReadJsonFileIfExists<TModel>(string fullPath, [NotNullWhen(true)] out TModel? result)
+ public bool ReadJsonFileIfExists<TModel>(string fullPath,
+#if NET5_0_OR_GREATER
+ [NotNullWhen(true)]
+#endif
+ out TModel? result
+ )
{
// validate
if (string.IsNullOrWhiteSpace(fullPath))
diff --git a/src/SMAPI.Toolkit/Serialization/Models/Manifest.cs b/src/SMAPI.Toolkit/Serialization/Models/Manifest.cs
index 3ab5edfb..01010602 100644
--- a/src/SMAPI.Toolkit/Serialization/Models/Manifest.cs
+++ b/src/SMAPI.Toolkit/Serialization/Models/Manifest.cs
@@ -115,7 +115,9 @@ namespace StardewModdingAPI.Toolkit.Serialization.Models
*********/
/// <summary>Normalize whitespace in a raw string.</summary>
/// <param name="input">The input to strip.</param>
+#if NET5_0_OR_GREATER
[return: NotNullIfNotNull("input")]
+#endif
private string? NormalizeWhitespace(string? input)
{
return input
diff --git a/src/SMAPI.Toolkit/Serialization/Models/ManifestContentPackFor.cs b/src/SMAPI.Toolkit/Serialization/Models/ManifestContentPackFor.cs
index dcdbcf74..f7dc8aa8 100644
--- a/src/SMAPI.Toolkit/Serialization/Models/ManifestContentPackFor.cs
+++ b/src/SMAPI.Toolkit/Serialization/Models/ManifestContentPackFor.cs
@@ -33,7 +33,9 @@ namespace StardewModdingAPI.Toolkit.Serialization.Models
*********/
/// <summary>Normalize whitespace in a raw string.</summary>
/// <param name="input">The input to strip.</param>
+#if NET5_0_OR_GREATER
[return: NotNullIfNotNull("input")]
+#endif
private string? NormalizeWhitespace(string? input)
{
return input?.Trim();
diff --git a/src/SMAPI.Toolkit/Serialization/Models/ManifestDependency.cs b/src/SMAPI.Toolkit/Serialization/Models/ManifestDependency.cs
index 5d1b73b1..fa254ea7 100644
--- a/src/SMAPI.Toolkit/Serialization/Models/ManifestDependency.cs
+++ b/src/SMAPI.Toolkit/Serialization/Models/ManifestDependency.cs
@@ -54,7 +54,9 @@ namespace StardewModdingAPI.Toolkit.Serialization.Models
*********/
/// <summary>Normalize whitespace in a raw string.</summary>
/// <param name="input">The input to strip.</param>
+#if NET5_0_OR_GREATER
[return: NotNullIfNotNull("input")]
+#endif
private string? NormalizeWhitespace(string? input)
{
return input?.Trim();
diff --git a/src/SMAPI.Toolkit/Utilities/PathUtilities.cs b/src/SMAPI.Toolkit/Utilities/PathUtilities.cs
index 9b7a78a0..136279f2 100644
--- a/src/SMAPI.Toolkit/Utilities/PathUtilities.cs
+++ b/src/SMAPI.Toolkit/Utilities/PathUtilities.cs
@@ -50,7 +50,9 @@ namespace StardewModdingAPI.Toolkit.Utilities
/// <summary>Normalize an asset name to match how MonoGame's content APIs would normalize and cache it.</summary>
/// <param name="assetName">The asset name to normalize.</param>
[Pure]
+#if NET5_0_OR_GREATER
[return: NotNullIfNotNull("assetName")]
+#endif
public static string? NormalizeAssetName(string? assetName)
{
assetName = assetName?.Trim();
@@ -64,7 +66,9 @@ namespace StardewModdingAPI.Toolkit.Utilities
/// <param name="path">The file path to normalize.</param>
/// <remarks>This should only be used for file paths. For asset names, use <see cref="NormalizeAssetName"/> instead.</remarks>
[Pure]
+#if NET5_0_OR_GREATER
[return: NotNullIfNotNull("path")]
+#endif
public static string? NormalizePath(string? path)
{
path = path?.Trim();
@@ -89,7 +93,7 @@ namespace StardewModdingAPI.Toolkit.Utilities
}
// keep trailing separator
- if ((!hasRoot || segments.Any()) && PathUtilities.PossiblePathSeparators.Contains(path[^1]))
+ if ((!hasRoot || segments.Any()) && PathUtilities.PossiblePathSeparators.Contains(path[path.Length - 1]))
newPath += PathUtilities.PreferredPathSeparator;
return newPath;
@@ -101,7 +105,43 @@ namespace StardewModdingAPI.Toolkit.Utilities
[Pure]
public static string GetRelativePath(string sourceDir, string targetPath)
{
+#if NET5_0
return Path.GetRelativePath(sourceDir, targetPath);
+#else
+ // NOTE:
+ // this is a heuristic implementation that works in the cases SMAPI needs it for, but it
+ // doesn't handle all edge cases (e.g. case-sensitivity on Linux, or traversing between
+ // UNC paths on Windows). SMAPI and mods will use the more robust .NET 5 version anyway
+ // though, this is only for compatibility with the mod build package.
+
+ // convert to URIs
+ Uri from = new(sourceDir.TrimEnd(PathUtilities.PossiblePathSeparators) + "/");
+ Uri to = new(targetPath.TrimEnd(PathUtilities.PossiblePathSeparators) + "/");
+ if (from.Scheme != to.Scheme)
+ throw new InvalidOperationException($"Can't get path for '{targetPath}' relative to '{sourceDir}'.");
+
+ // get relative path
+ string rawUrl = Uri.UnescapeDataString(from.MakeRelativeUri(to).ToString());
+ if (rawUrl.StartsWith("file://"))
+ rawUrl = PathUtilities.WindowsUncRoot + rawUrl.Substring("file://".Length);
+ string relative = PathUtilities.NormalizePath(rawUrl);
+
+ // normalize
+ if (relative == "")
+ relative = ".";
+ else
+ {
+ // trim trailing slash from URL
+ if (relative.EndsWith(PathUtilities.PreferredPathSeparator.ToString()))
+ relative = relative.Substring(0, relative.Length - 1);
+
+ // fix root
+ if (relative.StartsWith("file:") && !targetPath.Contains("file:"))
+ relative = relative.Substring("file:".Length);
+ }
+
+ return relative;
+#endif
}
/// <summary>Get whether a path is relative and doesn't try to climb out of its containing folder (e.g. doesn't contain <c>../</c>).</summary>
diff --git a/src/SMAPI/Events/AssetRequestedEventArgs.cs b/src/SMAPI/Events/AssetRequestedEventArgs.cs
index 82b59290..3c51c95d 100644
--- a/src/SMAPI/Events/AssetRequestedEventArgs.cs
+++ b/src/SMAPI/Events/AssetRequestedEventArgs.cs
@@ -101,7 +101,7 @@ namespace StardewModdingAPI.Events
mod: this.Mod,
priority: priority,
onBehalfOf: null,
- _ => this.Mod.Mod.Helper.Content.Load<TAsset>(relativePath))
+ _ => this.Mod.Mod.Helper.ModContent.Load<TAsset>(relativePath))
);
}
diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs
index 6ca463a2..1a58d84b 100644
--- a/src/SMAPI/Framework/SCore.cs
+++ b/src/SMAPI/Framework/SCore.cs
@@ -173,7 +173,8 @@ namespace StardewModdingAPI.Framework
/// <summary>Construct an instance.</summary>
/// <param name="modsPath">The path to search for mods.</param>
/// <param name="writeToConsole">Whether to output log messages to the console.</param>
- public SCore(string modsPath, bool writeToConsole)
+ /// <param name="developerMode">Whether to enable development features, or <c>null</c> to use the value from the settings file.</param>
+ public SCore(string modsPath, bool writeToConsole, bool? developerMode)
{
SCore.Instance = this;
@@ -190,6 +191,7 @@ namespace StardewModdingAPI.Framework
this.Settings = JsonConvert.DeserializeObject<SConfig>(File.ReadAllText(Constants.ApiConfigPath));
if (File.Exists(Constants.ApiUserConfigPath))
JsonConvert.PopulateObject(File.ReadAllText(Constants.ApiUserConfigPath), this.Settings);
+ this.Settings.DeveloperMode = developerMode ?? this.Settings.DeveloperMode;
this.LogManager = new LogManager(logPath: logPath, colorConfig: this.Settings.ConsoleColors, writeToConsole: writeToConsole, isVerbose: this.Settings.VerboseLogging, isDeveloperMode: this.Settings.DeveloperMode, getScreenIdForLog: this.GetScreenIdForLog);
this.CommandManager = new CommandManager(this.Monitor);
diff --git a/src/SMAPI/Program.cs b/src/SMAPI/Program.cs
index a8664160..b2e213fe 100644
--- a/src/SMAPI/Program.cs
+++ b/src/SMAPI/Program.cs
@@ -181,27 +181,40 @@ namespace StardewModdingAPI
bool writeToConsole = !args.Contains("--no-terminal") && Environment.GetEnvironmentVariable("SMAPI_NO_TERMINAL") == null;
// get mods path
+ bool? developerMode = null;
string modsPath;
{
string rawModsPath = null;
- // get from command line args
+ // get mods path from command line args
int pathIndex = Array.LastIndexOf(args, "--mods-path") + 1;
if (pathIndex >= 1 && args.Length >= pathIndex)
rawModsPath = args[pathIndex];
+ // get developer mode from command line args
+ if (args.Contains("--developer-mode"))
+ developerMode = true;
+ if (args.Contains("--developer-mode-off"))
+ developerMode = false;
+
// get from environment variables
if (string.IsNullOrWhiteSpace(rawModsPath))
rawModsPath = Environment.GetEnvironmentVariable("SMAPI_MODS_PATH");
+ if (developerMode is null)
+ {
+ string rawDeveloperMode = Environment.GetEnvironmentVariable("SMAPI_DEVELOPER_MODE");
+ if (rawDeveloperMode != null)
+ developerMode = bool.Parse(rawDeveloperMode);
+ }
- // normalise
+ // normalize
modsPath = !string.IsNullOrWhiteSpace(rawModsPath)
? Path.Combine(Constants.GamePath, rawModsPath)
: Constants.DefaultModsPath;
}
// load SMAPI
- using SCore core = new(modsPath, writeToConsole);
+ using SCore core = new(modsPath, writeToConsole, developerMode);
core.RunInteractively();
}