flathub / org.godotengine.GodotSharp

https://flathub.org/apps/details/org.godotengine.GodotSharp
6 stars 5 forks source link

Add support for external C# script editors #2

Open Calinou opened 11 months ago

Calinou commented 11 months ago

I noticed the description mentions the Flatpak doesn't support external C# script editors. However, this is critical because Godot with C# support isn't really worth using if you can't use an external editor. The built-in editor for C# is very limited (it lacks autocompletion), so pretty much everyone using C# is using an external editor with it.

What are the roadblocks to using external C# editors that external GDScript editors don't have to deal with?

zocker-160 commented 11 months ago

I cannot speak for every C# editor, but when using Jetbrains Rider when setting up a configuration for a project it expects you to set a path to your C# runtime:

Screenshot_20231106_175018

AFAIK it is not easy to access the mono runtime inside the Flatpak (unless you also put Rider into the Flatpak maybe?) because of the sandbox.

geekley commented 11 months ago

In Editor Settings > Dotnet > Editor > External Editor it doesn't work if I select Visual Studio Code directly.

But it seems to work for me (at least to open a C# script from Godot) when selecting Custom and manually entering the exec path to VSCode through flatpak-spawn --host:

dotnet/editor/external_editor = 6 ; Custom
dotnet/editor/custom_exec_path = "flatpak-spawn"
dotnet/editor/custom_exec_path_args = "--host code --goto {file}:{line}:{col}"

This is for the snap version of Code. If Code is also a flatpak then --host flatpak run ... obviously.

I think the issue is just clarifying the instructions, that it doesn't work directly, but you should set it to Custom and add the arguments.

Of course, a better option would be for this flatpak to provide a shell script for code (and every other IDE in the dropdown) inside the exported bin folder, where it just runs something like flatpak-spawn --host code "$@" so that when you select the option it just works. If you need something else (e.g. flatpak run) you still have custom.

zocker-160 commented 11 months ago

You have just repeated the instructions already provided in the readme: https://github.com/flathub/org.godotengine.GodotSharp#using-an-external-script-editor

From my testing it will not give you any code completion or intellisense unless you have dotnet / mono installed on your host machine, which defeats the purpose of the Flatpak IMO.

geekley commented 11 months ago

defeats the purpose of the Flatpak IMO

I disagree. The purpose of the flatpak (for me at least) is to make Godot auto-update hassle-free, and use the sandboxing.

I assume it's pretty much expected that dotnet must be available on the IDE somehow and matching the version in the flatpak. Whether the IDE's dotnet is provided through host PM or the IDE extension, or whatever, depends on the user configuration. Of course, clarifying this in the README would be nice: "you must have dotnet available in the host IDE to get completion, etc".

it is impossible to access the mono runtime inside the Flatpak

Doing flatpak run --command="dotnet" org.godotengine.GodotSharp --version will run dotnet --version from outside the flatpak, using the flatpak's dotnet. So you could set up a script or IDE task to run dotnet build using the flatpak environment from outside. As for the completions, etc. I haven't tested setting dotnet.dotnetPath in VSCode to where it would be in the flatpak (e.g. ~/.local/share/flatpak/app/org.godotengine.GodotSharp/current/active/files/lib/dotnet/), so I don't know if it can work without installing dotnet on the host. But it might work...?

Zishan-Rahman commented 11 months ago

This Flatpak uses org.freedesktop.Sdk.Extension.dotnet7, and, if it hasn't already been installed, this Flatpak (GodotSharp) will also install that Flatpak (dotnet7, as well as openjdk11). Setting the C# runtime path in your editor of choice to point to where that Flatpak is installed could possibly work in my view, but I have yet to test that out myself.

Zishan-Rahman commented 10 months ago

This Flatpak uses org.freedesktop.Sdk.Extension.dotnet7, and, if it hasn't already been installed, this Flatpak (GodotSharp) will also install that Flatpak (dotnet7, as well as openjdk11). Setting the C# runtime path in your editor of choice to point to where that Flatpak is installed could possibly work in my view, but I have yet to test that out myself.

Late correction: I tried installing GodotSharp locally and it didn't install a separate Flatpak for the dotnet7 extension (I already have global installs of GodotSharp and the dotnet7 extension). I realise the .NET SDK gets installed as a module in the Flatpak manifest, thus the .NET SDK is in the /app/lib folder (/app being where the Flatpak and its files are installed). A possible modification to the manifest could be to copy the GodotSharp folder in /app/bin to /app/lib, so that both the SDK and the Godot-specific APIs are in one place and can be easily linked to. I haven't tried @geekley's suggestion yet, but maybe what I just described could fix the code completion and IntelliSense issues @zocker-160 described here? I'll have to look into all this when I can.

(Sorry for the pings, folks)

geekley commented 10 months ago

As for the completions, etc. I haven't tested setting dotnet.dotnetPath in VSCode to where it would be in the flatpak (e.g. ~/.local/share/flatpak/app/org.godotengine.GodotSharp/current/active/files/lib/dotnet/), so I don't know if it can work without installing dotnet on the host. But it might work...?

This seems to work fine. I just tested on VSCodium (my IDE is in unsandboxed snap, not flatpak). But if you use a local flatpak installation you have to set dotnet.dotnetPath replacing ~/ with your actual /home/<username>/... in user settings. I've also tried building with /home/.../current/active/files/lib/dotnet/dotnet build ... directly (instead of flatpak run ...) and it seems to work fine this way too. I'm getting completions and everything.

Oh, and I suppose it's better if someone with a flatpak'd IDE could test too. Maybe you just need to set a similar setting or perhaps DOTNET_ROOT environment var. But I assume it may be harder to access an SDK inside a flatpak from another flatpak (you might need to use /run/host/... paths).

So it's possible no changes need to be made, except maybe clarifying in the README how this can be done instead of saying "The Mono external editor support does not work".

Also, my suggestion above would still be the ideal to make it work out-of-the box, it would just require a bit of scripting:

Of course, a better option would be for this flatpak to provide a shell script for code (and every other IDE in the dropdown) inside the exported bin folder, where it just runs something like flatpak-spawn --host code "$@" so that when you select the option it just works. If you need something else (e.g. flatpak run) you still have custom.

Zishan-Rahman commented 9 months ago

I'm updating the .NET Freedesktop SDK extension to .NET 8 (instead of .NET 7) here: #8

Just letting you know in case anyone here is using this Flatpak and somehow any problems arise after the update gets merged and published, though that shouldn't be happening because the installation folder should still be /app/lib/dotnet. It should already be merged and maybe even published by the time you read this, and the Flatpak should work as it did before.

geekley commented 9 months ago

I have a solution for VSCode (tested) and MonoDevelop (should work too) to fix this issue with simple scripts. This is so that clicking a C# file (or line number) inside Godot opens the external IDE as expected. It'll work automatically when the IDE is non-flatpak in PATH, and guide the user in case of failure.

@Zishan-Rahman Please provide these scripts at /app/bin (need these exact filenames, and executable, i.e. chmod +x):

/app/bin/code

#!/usr/bin/sh
flatpak-spawn --host code "$@" && exit
zenity --name='godot_editor' --class='godot' --error --title='Godot: .NET External Editor' \
    --window-icon="/app/share/icons/hicolor/scalable/apps/org.godotengine.GodotSharp.svg" \
    --no-wrap --text="\
Trying to run external editor <b>VS Code</b> failed: EXIT_STATUS=$?.
Ensure the <b>code</b> program is available outside Flatpak via command line.

Or provide a custom command in the <b>editor settings</b>, e.g.:
<b>dotnet/editor/external_editor:</b> Custom
<b>dotnet/editor/custom_exec_path:</b> flatpak-spawn
<b>dotnet/editor/custom_exec_path_args:</b>
--host flatpak run com.visualstudio.code <i>{project}</i> -g <i>{file}</i>:<i>{line}</i>:<i>{col}</i>

You can also disable this editor setting to use the internal script editor:
<b>dotnet/editor/external_editor</b>"

Note: for people who use Codium (or Code-OSS, etc), it will also work directly if they were already redirecting the code command to e.g. codium in their host system.

/app/bin/monodevelop

#!/usr/bin/sh
flatpak-spawn --host monodevelop "$@" && exit
zenity --name='godot_editor' --class='godot' --error --title='Godot: .NET External Editor' \
    --window-icon="/app/share/icons/hicolor/scalable/apps/org.godotengine.GodotSharp.svg" \
    --no-wrap --text="\
Trying to run external editor <b>MonoDevelop</b> failed: EXIT_STATUS=$?.
Ensure the <b>monodevelop</b> program is available outside Flatpak via command line.

Or provide a custom command in the <b>editor settings</b>, e.g.:
<b>dotnet/editor/external_editor:</b> Custom
<b>dotnet/editor/custom_exec_path:</b> flatpak-spawn
<b>dotnet/editor/custom_exec_path_args:</b>
--host /path/to/monodevelop <i>{file}</i>;<i>{line}</i>;<i>{col}</i>

You can also disable this editor setting to use the internal script editor:
<b>dotnet/editor/external_editor</b>"

This method won't work for Rider because it's not relying on PATH environment variable. It's doing some complex logic to detect its installation path (e.g. inside ~/.local/... or in /snap/...) and run it directly. I haven't tested it - it's possible it already works currently with no changes. If not, then it needs to be solved upstream to make this logic detect presence of /usr/bin/flatpak-spawn, which only exists inside the sandbox, and in this case use it to run the command prepending --host <execName> to the args.

Zishan-Rahman commented 9 months ago

@geekley Thanks so much for your recent suggestion. I really do appreciate it, and sorry for both the ping and the lateness of my reply. However, I recently tried out your original proposed solution (either installing the .NET SDK natively or hardcoding the SDK path in our chosen editor to the /app/lib/dotnet/folder where the Flatpak has the .NET SDK installed), and | think that might be the better way going forward.

I tried working with a non-Flatpak build of Codium with the Flatpak initially and was surprised to have gotten IntelliSense and code completion working initially, not realising that Codium defaulted to the version of .NET that I already had installed on my system. I then tried hardcoding dotnet.DotNetPath with Codium, and it also worked that way too, so your original suggestion definitely is a viable alternative for those who cannot install the .NET 8 SDK system-wide (or choose not to).

That was with my natively installed build of Codium, though, so I decided to try it with a freshly installed Flatpak build of Codium for the sake of completeness, and I did get it to load when running it through the Godot editor itself, but I'm still not getting code completion that way just yet. The problems I'm having here boil down to the $PATH and $DOTNET_ROOT variables needed when using the Flatpak'd Codium (or any other Flatpak'd editor, for that matter), so that the .NET SDK installed with the Flatpak (in /app/lib/dotnet) can be used to start the OmniSharp server in Codium that allows for code completion. I did get the Codium Flatpak by itself to open a Godot project with C# IntelliSense, so I should be able to get the server to start when opening Codium through this Flatpak without having to install the dotnet8 Flatpak separately. I'll post another comment here when I feel an update on this is due, which will hopefully be when I finally get it to run with code completion when opening through Godot itself. Once I have a solution, it hopefully should work for other editors and IDEs as well. I haven't tried MonoDevelop with Godot yet.

As for Rider, as shown in the screenshot taken by @zocker-160 (sorry for the ping) in this comment, it may be worth trying to set the C# runtime there to the absolute path of the Flatpak's custom .NET SDK as we would do in VSCode/Codium (by selecting Custom for Runtime, I assume, though I haven't tested that yet).

What I want to do is get the specific information on at least running VSCode or Codium (both native and Flatpak), and more general information on running other editors, with this Flatpak into either README.md or its own Markdown file, and add the link to the relevant information in the AppData description. Once I do, I'll open a PR with the included information in the README.md. Hopefully I don't have to make any massive changes to the Flatpak manifest itself. I plan to do the same for org.godotengine.Godot3Sharp as well.

geekley commented 9 months ago

original proposal ... that might be the better way going forward.

Just to clarify, I made 2 proposals to tackle the 2 separate issues described here. They aren't mutually exclusive, because each is trying to solve a different issue:

  1. Wrapper scripts: solve the problem of clicking a C# script or script/line/column links in Godot when user selected one of the built-in IDE options in "External Editor" in Godot Editor settings instead of setting a custom exec/args that works for flatpak (i.e. flatpak-spawn). The scripts are not related to any communication between Godot and the IDE's C# features. In fact, unlike GDScript, C# Intellisense should work even with Godot closed and the VSCode godot-tools extension disabled if I'm not mistaken. It just needs the .NET SDK and that's it (someone with knowledge, please correct me if I'm wrong). Plus maybe some custom scripts if you really want to build the csproj exactly like Godot would.
  2. Setting the .NET SDK in the IDE: to solve the minor issue of getting Intellisense using the exact same dotnet version in the Flatpak. Like you said, it already worked before just fine using the host machine's dotnet (e.g. from apt), the only caveat is that dotnet version might be different (e.g. minor/patch). But it shouldn't really cause any issues - at least in Code, I dunno about Rider.

(1) is because having wrapper scripts to escape the sandbox would make those settings behave as they would outside Flatpak - which is better than having to explain everything in the README and requiring custom setup.

(2) was just a reply to zocker-160 saying it may be possible to set up the IDE to work with fatpak'd Godot, but that's IMO out-of-scope here (IDE setup is up to the user) - except maybe by adding instructions in README like I said above.

geekley commented 9 months ago

I did get the Codium Flatpak by itself to open a Godot project with C# IntelliSense, so I should be able to get the server to start when opening Codium through this Flatpak without having to install the dotnet8 Flatpak separately.

I remember back when I used flatpak'd Codium, I had to do this i.e., add FLATPAK_ENABLE_SDK_EXT=* env before flatpak run com.vscodium.codium (or do a flatpak override so it's always added) in order for it to access my manually installed org.freedesktop.Sdk.Extension.dotnet. I don't know if that helps accessing Godot's version of .NET, but you could try adding that override in Codium. I don't know if Godot's setting for this would somehow support adding custom env variables for the executable, but I assume it's not possible this way.

zocker-160 commented 9 months ago

@geekley no I respectfully disagree with your second point.

This might be only a minor issue for "normal" distros, but it is a big deal for immutable distributions which do (almost) solely rely on Flatpak for their applications.

It is not about having slightly different versions of .NET in editor vs Godot, it is about not being able to rely on .NET existing on the host at all + IDE also running in a Flatpak.

Unless I am missing something, from my testing I came to the conclusion that this use case is hitting a limitation of Flatpak itself since I could only make it work with some major and ugly hacks, that can and will stop working randomly.

Zishan-Rahman commented 9 months ago

This might be only a minor issue for "normal" distros, but it is a big deal for immutable distributions which do (almost) solely rely on Flatpak for their applications.

It is not about having slightly different versions of .NET in editor vs Godot, it is about not being able to rely on .NET existing on the host at all + IDE also running in a Flatpak.

From here, I have a few suggestions:

To demonstrate my latter suggestion, in /app/bin/code, instead of just running

flatpak-spawn --host code "$@" && exit

we could instead run

(flatpak-spawn --host code "$@" || flatpak-spawn --host flatpak run com.visualstudio.code "$@") && exit

so that it tries running a natively installed version of VSCode first, then it tries looking for a Flatpak'd version. This way, the script works even for immutable distros that will almost only use Flatpaks for apps. We could do the same for Rider as well, and maybe even MonoDevelop for those who, instead of building and installing it natively, may want to build and install the Flatpak'd version of it themselves using flatpak-builder, which can be installed as a Flatpak. Come to think of it, if this works, we could also do the same for Blender, in both this and the standard Flatpak.

From what I understand now, @geekley's point of the simple shell scripts is so that, when Godot itself is looking for a code or monodevelop executable in the path that's available to it in Flatpak's sandbox (/app/bin:/app/jre/bin:/app/lib at the moment), so that we don't have to set a custom executable each time and Godot can just run that.

Sorry for all the pings, by the way.

Zishan-Rahman commented 9 months ago

Heck, if we end up applying both of the above suggestions, then, per geekley's comment, we could run this in /app/bin/code:

(flatpak-spawn --host code "$@" || flatpak-spawn --host FLATPAK_ENABLE_SDK_EXT=* flatpak run com.visualstudio.code "$@") && exit

And hopefully that would work as it should.

geekley commented 9 months ago

We could do the same for Rider as well

Like I said above, for Rider/Fleet it's not that simple, as the executable it'll try to call depends on very complex upstream code (I've used ILSpy on a dll in GodotTools to investigate how it works). So it can't be solved with wrapper scripts - that is, assuming this problem even exists for Rider at all (which I'm not testing since I don't have it installed).

As for Flatpak-only systems, there's also the need to test this, i.e. if .NET SDK included in Godot-flatpak can be used from another flatpak, the IDE; preferably testing on such immutable systems:

I assume it may be harder to access an SDK inside a flatpak from another flatpak (you might need to use /run/host/... paths).

Zishan-Rahman commented 9 months ago

We could do the same for Rider as well

Like I said above, for Rider/Fleet it's not that simple, as the executable it'll try to call depends on very complex upstream code (I've used ILSpy on a dll in GodotTools to investigate how it works). So it can't be solved with wrapper scripts - that is, assuming this problem even exists for Rider at all (which I'm not testing since I don't have it installed).

I'll admit that, due to my own misunderstanding, I somehow thought the complex upstream code you repeatedly kept mentioning was in Rider itself until I read this🤦‍♂️

I am willing to look into said upstream code for myself to see if/where anything could be done, as I don't want to give up on this just yet. @geekley (sorry for the ping) Please can you tell me which DLL you dissassembled with ILSpy so that I can try investigating it myself as well?

As for Flatpak-only systems, there's also the need to test this, i.e. if .NET SDK included in Godot-flatpak can be used from another flatpak, the IDE; preferably testing on such immutable systems:

I assume it may be harder to access an SDK inside a flatpak from another flatpak (you might need to use /run/host/... paths).

I assume that installing this Flatpak, with the dotnet8 Flatpak installation change, will install the dotnet8 Flatpak in /usr, which refers to where the Flatpak runtimes, SDKs and extensions are installed on our respective computers (either /var/lib/flatpak/runtime/... or $HOME/.local/share/flatpak/runtime/...), and not directly inside this Flatpak. Therefore, the dotnet8 Flatpak should already be available for other Flatpaks to use after installing this Flatpak. Of course, for that to happen, the aforementioned change that enforces the dotnet8 Flatpak's installation gets made in this Flatpak. I'll post another comment here when I've tested that out. Anyone else who wants to try actioning this change in the Flatpak manifest and testing it out themselves is free to do so as well.

geekley commented 9 months ago

which DLL you dissassembled with ILSpy

@Zishan-Rahman You should see the method logged in Godot Editor when it fails to find Rider (it's not installed). The DLL is under <godot-install-path>/GodotSharp/Tools. IIRC it was GodotTools.IdeMessaging.dll for Godot 4 (there's a class with Rider in the name) and GodotTools.dll I think (if not this, then it was GodotTools.Core.dll).

There's methods for each OS. The Linux code was doing stuff like loading some settings JSON file to find a possible path to add to the list, finally adding the /snap/... path (the only one that's simple).

But I don't know for sure it doesn't work (as I think it would be possible to access both Rider paths, local and snap, from this Flatpak), so it's better to install Rider or Fleet first and see if there's even an issue at all with opening C# scripts from Godot.

geekley commented 9 months ago

It was something similar to this: https://github.com/JetBrains/resharper-unity/blob/0b25daf05acebdc1a0acd3d2c71d27294286acd0/unity/PathLocator/RiderPathLocator.cs#L60 It seems it's not even code from Godot itself.

Referenced from here I think: https://github.com/godotengine/godot/blob/master/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs