stride3d / stride

Stride (formerly Xenko), a free and open-source cross-platform C# game engine.
https://stride3d.net
MIT License
6.56k stars 947 forks source link

[Installer] Stride Installer fails to install required .NET Runtimes/SDKs #1883

Open JeromyWalsh opened 1 year ago

JeromyWalsh commented 1 year ago

Release Type: Official Release/GitHub (please choose appropriate option) Official Release

Version: Version number and/or git branch

Runtime/Editor version 4.1.0.1948 Installer/Launcher v5.0.6

Platform(s): Does the problem occur on Windows, Android...?

Windows

Describe the bug After downloading and installing the StrideSetup.exe on a (non-development environment), attempting to launch the editor is met with the error below, indicating that the user must install the .NET Desktop Runtime version 6.0.13 and is prompted if they'd like to do that.

To Reproduce Steps to reproduce the behavior:

  1. On a clean/non-development machine download StrideSetup.exe from the Stride3d website
  2. Install the launcher by executing the StrideSetup.exe
  3. Either accept the latest version of the editor or decline and click on the download button for the latest version
  4. Click the red start button in the top left

Expected behavior The Stride editor launches, without errors or exceptions, and with no further steps or dependencies.

Actual behavior The user is shown the below exception and must take additional steps before moving forward. As a rule, the installer should take care of installing all prerequisites or dependencies necessary to use an end-user application.

Screenshots

image

If you install follow the above directions but have already installed the .NET 7 SDK, you receive the following dialog instead:

image

JeromyWalsh commented 1 year ago

I found these two previous issues/pull requests, but neither seem to address the problem in the most direct/simplest manner.

https://github.com/stride3d/stride/pull/1809 https://github.com/stride3d/stride/issues/1753

First, 1809 seems to be a runtime check. But waiting until the user is attempting to build a project is too late to detect/install prerequisites. This should be done by the installer.

Issue 1753 seems to be closer, and indeed one of the comments in the discussion was to move the installation of the runtime and/or SDK to the installer, however, the initial issue was that the link directs the user to download the .NET Desktop Runtime, when it is in fact the .NET SDK that is required.

This issue clarifies the matter. The .NET SDK includes the runtime. So the solution here is for the Installer to install the .NET 6.0 SDK as a prerequisite.

JeromyWalsh commented 1 year ago

As a separate/additional comment, there was discussion under PR 1809 to use the detected .NET SDK version that the Game Studio was built with as a way to determine the required .NET SDK prerequisite. However, the .NET SDK version the game studio was built with is not necessarily the same .NET SDK version required for the Game Studio to build games.

As an example, we could build the editor in .NET 8.0, while still only requiring .NET SDK 6.0 to be installed by users to build games. Furthermore, there are two different runtimes involved. There's the runtime version required to build/run the studio, as well as the runtime version required to run games on user end machines.

In order to both build and run games on a developer's machine, it's possible that multiple SDKs or runtimes may need to be installed. The set should be identified and explicitly set in the installer's list of prerequisites.

JeromyWalsh commented 1 year ago

@Eideren Can you assign this to me?

JeromyWalsh commented 1 year ago

There's been some discussion about this in Discord, but I'd like to move it here so there's a record of it and a conclusion.

First, for the purpose of this discussion, there are three different "groups." I use the terms "contributor" to refer to those building the Game Studio, "developer" to refer to those developing games using the studio, and "end-user" to refer to those playing the games.

Second, there are two different "run-times" to be considered for the purpose of this discussion.

  1. There's the runtime required to run the editor on a developer's machine
  2. There's the runtime required to run the game on an end-user's machine

Likewise, there's two different SDKs to consider.

  1. There's the SDK contributors use to develop the Stride Game Studio & Runtime
  2. There's the SDK developers user to develop games with

In both cases above, #1 >= #2. For example, contributors could theoretically use the .NET 7 SDK (A non-LTS version) to target the .NET 7 runtime for developing & publishing the Game Studio, while only offering the .NET 6 SDK (LTS) & runtime for game developers to deploy to end-users.

That all established, it's understandable that contributors looking to build the Game Studio and runtime may have a few steps to go through to establish a build environment. This might include downloading one or more .NET SDKs, as well as OS-specific native libraries and frameworks.

It should also be reasonably understood that end-users will have their required runtimes installed either as part of a publishing platform (such as Steam), or as part of a game's stand-alone installer. Ultimately, the .NET runtime required by end-users should be completely transparent to them.

The area that seems to be causing the most difficulty then, is the SDK & runtimes required for "developers" to build games using the Stride Game Studio or Runtime libraries. Here's my thoughts on this & my proposal.

For the rapid adoption of the Stride Game Studio & runtime, it's important there be as few steps as possible when installing the Stride Launcher and any additional versions of the Game Studio & Runtime. To that end, here's my recommendation.

The Launcher

The Game Studio

Example Scenario 1:

I'm installing StrideSetup.exe for the first time and have no .NET SDKs or runtimes on my machine. The latest version of the Game Studio requires the .NET 7 runtime to run. As part of StrideSetup.exe, the .NET 7 runtime is installed on my machine.

After launching the Launcher (.NET 7 runtime has already been installed), I install the latest Game Studio which is built with .NET 7 but allows development of games using the .NET 6 SDK/runtime. As part of downloading/installing that version of the Game Studio, the .NET 6 SDK/runtime are both installed on my machine.

I can now launch the launcher & Game Studio without issue, and I can both develop for and launch my .NET 6 game.

Example Scenario 2:

A new version of the Game Studio is made available that deploys the same version of the .NET runtime to end users. When downloading/installing the new version, a check is done by the launcher and no new versions of the .NET SDK/runtime are installed.

Example Scenario 3:

A new version of the Game Studio is made available that uses the .NET 8 runtime, but still deploys games that use .NET 6. In advance of the new Game Studio being made available, a new version of the Launcher is made available. As part of the update of the Launcher, the .NET 8 runtime is installed. After which, the user sees that there's a new version of the Game Studio available and can download it. It launches & runs fine, as the .NET 8 runtime has already been installed by the launcher.

Example Scenario 4:

A new version of the Game Studio is made available that now allows deployment of games using .NET 8. Upon downloading the latest version of the Game Studio, a check is done. As we've already installed the .NET 8 runtime as part of the Studio, no additional installation is required. However, as the .NET 8 SDK was not previously installed, it is now downloaded & installed along with the latest version of the Game Studio.

Example Scenario 5:

A new developer decides to download the StrideSetup.exe at this point in time. It installs the .NET 8 runtime. After installing the launcher, the user decides they do not want to use the latest version, and instead decides to install the previous version. A check is done, and as part of the download/installation, both the .NET 6 runtime and .NET 6 SDK are installed as they are required by the prior version for deploying games. The Game Studio itself continues to run under .NET 8 as in Example Scenario 3 above.

manio143 commented 1 year ago

I believe you have outlined the scenarios very well. đź‘Ť

I remember we used to ship an installer with GameStudio 4.0.x which was ensuring dependencies were present and if not invoking Visual Studio installer to install them. But it was built separately from the engine to it wasn't great and we dropped it with the move to .NET Core. If we can implement all the checks and downloads and installation within the launcher itself I think it will make things better.

On the point of GameStudio runtime vs Game runtime and the SDK required:

  1. GameStudio must be able to load user assemblies in process, which means user cannot target a higher TFM
  2. GameStudio integrates with a specific version of MSBuild which is where the SDK requirement comes from. I'm not sure how much flexibility is there and if we can integrate with a newer MSBuild but still operate on an older SDK if present.
  3. Less now, but often in the past if the user had installed a newer SDK than Stride depended on it broke everything because .NET was shipping breaking changes (especially to NuGet integration). So we need to look at programmatically asking the system what all SDKs are installed and it would be interesting to understand if the launcher can execute GameStudio in the context of a specific SDK host and not just latest.
JeromyWalsh commented 1 year ago

@manio143 Thanks for the great feedback!

Regarding (1), that should be handled by the case I mentioned in the write-up where Studio SDK/Runtime must be greater than equal to the Game's SDK and required runtime. Did I misunderstand something?

(2) The SDK/Runtime used by the editor should be independent of which version of MSBuild it calls. I assume the MSBuild version would need to be the same as the runtime/sdk version of the game, not the Editor. That being said, my understanding is that MSBuild can target any version of .NET Core or the .NET Framework it was built with.

(3) This is a bug we should probably address separately. As part of an application's runtime config is a flag that determines whether the application will roll-forward to newer versions of the runtime or not - and if so, major or minor versions. As for the SDK, whether VS Code, Visual Studio, or Rider it's possible to use an earlier SDK than the most recent available on the system, and I know that you can use the global.json to control which SDK is being used. See this documentation:

image

Thoughts?

JeromyWalsh commented 1 year ago

While I wrote the initial first of this proposal with the current situation in mind, there's an alternative. Rather than splitting the Game Studio SDK/runtime and Game SDK/runtime into two potentially different versions, we could collapse them into a single version in all cases, ie. the SDK/runtime used to build/run an instance of the Game Studio is the same SDK/runtime targeted for all games made with that version.

We'd still need to deploy SDK/runtimes as part of Game Studio releases, but might minimize our dependency chain in exchange for limited flexibility. The main advantage here is fewer dependencies per release. The main disadvantage is that newer versions of the Game Studio couldn't build games for older versions of .NET. So if a user wanted/needed to target an older runtime, they'd have to go back to a previous version of the Game Studio, missing out on any new features/functionality released in later version. Thoughts?

MeharDT commented 1 year ago

Good summary of the issue. Regarding PR #1809, the check occurs when the user attempts to launch Stride 4.1 or above, not when they are building a project within Stride. The check is also independent of the launcher and occurs if the engine is launched as a standalone exe.

There's also a pending PR( #1872) that will check for the presence of MSBuild on a system from within the launcher, that should help debug these issues before a user actually opens Stride.

For development purposes because the .NET SDK and runtime required to use Stride are currently designed to be in sync, and because the .NET SDK now installs both, maybe it would be easier to only focus on the SDK to find a solution? Unless there's an advantage to splitting them up.

manio143 commented 1 year ago

The SDK/Runtime used by the editor should be independent of which version of MSBuild it calls

I'm not 100% clear on this, but my understanding is that the MSBuild dependency we consume in Stride is not the full and independent MSBuild and it's more like a way to load an existing MSBuild installation (from the installed SDK) into the Stride process. So if the user has a different SDK than the one corresponding to the MSBuild version Stride integrates with then there's friction.

It's separate from targeting altogether. An SDK can target anything that came before it (within some constraints). So we can enforce the user to install the specific SDK Stride uses, but the user should be theoretically be able to target an earlier runtime. However, saying that - we would have to keep runtime libraries targeting the earlier runtime, which means lesser ability to use latest .NET features in code. Thus, we most likely will still choose to say this version of Stride (editor and runtime) corresponds both in SDK and target runtime to version 6, or 8, etc.

VaclavElias commented 1 year ago

Related issues? https://github.com/stride3d/stride/issues/856 https://github.com/stride3d/stride/issues/493

Ethereal77 commented 1 year ago

It's not so "easy" to separate the SDK the GameStudio uses from those of the games. As @manio143 says above, the GameStudio doesn't launch a standalone MSBuild process. Instead, the Project System that loads the user projects and analyzes them, restores the NuGet packages, etc., loads an in-process version of Microsoft.Build, for which it uses the MSBuild.Locator to discover the most up-to-date SDK, then loads the projects with it.

And it is this in-process instance that is invoked for compiles, generation, etc. It also constructs a RoslynWorkspace on this project representation for the in-editor C# editor.

JeromyWalsh commented 1 year ago

@MeharDT @manio143 @VaclavElias @Ethereal77 Thank you all for joining the discussion and taking time to provide feedback. Much appreciated! Let me see if I can address each of you. :+1:

@MeharDT When there are multiple checks for something scattered throughout a code-base, whether before a project is loaded, built, or published, it creates opportunities for inconsistencies and makes it harder to maintain. The bigger problem addressed by this issue is that dependencies shouldn't be checked/validated by the Game Studio at all. The dependencies should be checked by the launcher and installed simultaneously with new version of the Game Studio. This creates a single source of truth and is much easier to maintain and less prone to creating inconsistencies.

Because the dependencies are installed at the same time as a version of the Game Studio, it won't matter whether the Game Studio is launched by the launcher or as a stand-alone executable.

For development purposes because the .NET SDK and runtime required to use Stride are currently designed to be in sync, and because the .NET SDK now installs both, maybe it would be easier to only focus on the SDK to find a solution? Unless there's an advantage to splitting them up.

In my proposal above I had mentioned that...

  • I further recommend that the .NET SDK & runtime required to run a game developed with that version of the Game Studio be installed with that version of the Game Studio.

I was only explicitly calling them both out because there is the situation in which the SDK/runtime used to build/contribute to the Game Studio is not (may not be?) the same as the SDK required to ship games. In which case, it makes sense to install the runtime required by the Game Studio, but not the SDK. But I'm fine with combining the Game's SDK/runtime into a single concept of the Game Studio SDK.

@manio143

I'm not 100% clear on this, but my understanding is that the MSBuild dependency we consume in Stride is not the full and independent MSBuild and it's more like a way to load an existing MSBuild installation (from the installed SDK) into the Stride process. So if the user has a different SDK than the one corresponding to the MSBuild version Stride integrates with then there's friction.

Do you know why we load MSBuild into the Stride process rather than just invoking MSBuild as a child process? It seems that would decouple a lot of dependencies, as MSBuild is always backwards compatible and can build apps from previous SDKs. So with the installation of the latest SDK we always get an MSBuild that works for all cases.

@VaclavElias I think it's safe to put any missing SDK, Framework, etc. issues that exist under the umbrella of this issue, if one possible solution to the existing issue is to have the launcher install the dependencies.

@Ethereal77 Thanks for pointing out the challenges. I'm not familiar with the historical reasons for the current architecture, and certainly not criticizing it. But, is there a reason MSBuild, etc. must be integrated into the process? Can we just pre-download the necessary Nuget packages, copy them into a new project on creation, and invoke MSBuild as an external process to build the game project.

I guess I should also point out that our current build system is likely more complex than it needs to be. There's a difference between building a published build and an editor build. For the editor build, it might be worthwhile to consider Unity's model of prebuilding a launcher and only having the project build the necessary code files into an assembly. That assembly name is then passed as a command-line argument to the player when they hit "play", which loads the managed assembly.

There's really no need to build a full player each time the player hits play - if that's what we're doing.

Thoughts?

(Thank you all again for helping me understand the scope of the problem so quickly)

manio143 commented 1 year ago

Do you know why we load MSBuild into the Stride process rather than just invoking MSBuild as a child process?

Stride is a bit like Visual Studio - it manages the solution and projects inside. To ensure consistency with the MSBuild model it is being done through MSBuild libraries instead of reimplementing the project model.

I think we could offload *** to be invoked by the operating system, however, given all the other integration points where MSBuild has to be invoked and the results communicated back to Stride, I'm not sure how much of a benefit it would be. And we're anyways running the build out of proc already if I'm reading this right

https://github.com/stride3d/stride/blob/b4442c12b20d9659d421d3d9ce7fead4728a66db/sources/assets/Stride.Core.Assets/PackageSessionPublicHelper.cs#L112-L122