#Godot and FSharp

1 messages ยท Page 2 of 1

open dagger
#

Yeah

mild tiger
#

What does reflection, that makes it disadvantaged here?

open dagger
#

Reflection is generally slow

#

And it would be used in normal code, not generators

mossy whale
#

and isn't allowed on some targets (consoles, iphones, android(?))

mossy whale
#

you don't have a System.Type of the types you are working with

open dagger
#

Yeah, I meant reflection generally

#

The best we have is LongIdent and Ident

mossy whale
#

it also breaks AOT builds, so that isn't great either...

mossy whale
open dagger
#

yeah?

mossy whale
#

you can ask the F# compiler to check an file and then you get typed AST back

#

and then you get a lot more information

open dagger
#

Yeah, I saw that, but what Myriad has is simpler, and for what I do it works good enough

mossy whale
#

๐Ÿคท

I found the typed thing to be easier to work with

open dagger
#

I did not really understand it at all, and did not want to bother too much

#

The examples posted here also seemed outdated

mossy whale
#

yea, the examples are not great ๐Ÿ˜…

But, unless you want to spit out code that uses reflection you kinda need to use it due to the "Needing to get a Variant.Type" thing

open dagger
#

Hmm, oh yeah, my current approach won't work for complex types, so I have to check that later again

mossy whale
open dagger
mild tiger
#

Thanks so much guys, for doing all this โœŒ๐Ÿป

balmy bison
#

I wish I could help more but Ive not even installed godot :-p

#

ast/tast/quotations/reflection is second nature though

#

The benefit to tast is its reflection friendly, quotations rely on reflection

#

You can get round that though with some judicious hacking ๐Ÿ™‚

#

reflection can be cached too, so generally fine for a generator, you dont want much reflection at runtime or repetedly called without any caching.

#

You can reconstruct the System.Type from the tast by using various methods depending on what type of type it is

mild tiger
balmy bison
#

Well I havent even downloaded it then ๐Ÿ™‚

balmy bison
#

If i get some time I'll have a look at godot, its just tricky finding any time lately

open dagger
#

Ah, I think I tried with the newest version of FSharp.Compiler.Service... but as long as it works

open dagger
#

Hmm, for some reason the compiler can't find the FSharp.Compiler.Service assembly when trying to use the project as a nuget

mossy whale
#

oh joy >_<

open dagger
#

It is listed as a dependency, but somehow it seems to at least not be available when Myriad runs.

mossy whale
#

well, at least it isn't a segfault

open dagger
open dagger
#

Okay, copying the files manually works, but we need to find how to do that automatically at some point

mossy whale
#

<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> ?

open dagger
#

Tried, did nothing differently

mossy whale
#

just put it in every .fsproj and .csproj file

#

then sooner or later you have the correct one :P

open dagger
#

I only have one fsproj for the nuget ^^

untold fern
#

alternately you could likely add a function to AssemblyLoadContext.Default.add_Resolving to supply the FSharp.Compiler.Service assembly

open dagger
#

Unless it is to copy in the referencing project, not the generating one

mossy whale
untold fern
#

assuming FCS exists somewhere

open dagger
#

One project in that issue above seems to use a build system (FAKE?) and instructions to copy dependencies, but we'd have to look into that... unless someone already has experience with that

untold fern
#
/// Renames necessary files in the buildDir so they are ready to run
let renameInBuildDir buildDir publishDir =
    let nativeDll = FileInfo.ofPath (Path.combine buildDir "SharpCells.XLLNE.dll")
    // This path must match what is set in XllControl.fs
    let xllFile = Path.combine publishDir "SharpCells.XLL.xll"
    nativeDll.MoveTo(xllFile, true)

    // SharpCells.Ribbon.comhost.dll expects a runtimeconfig.json of the same name
    let runtimeConfig =
        FileInfo.ofPath (Path.combine publishDir "SharpCells.XLL.runtimeconfig.json")

    let runtimeRibbonPath =
        Path.combine publishDir "SharpCells.Ribbon.runtimeconfig.json"

    let _ = runtimeConfig.CopyTo(runtimeRibbonPath, true)
    ()
#

moving and copying files as part of a FAKE script

open dagger
untold fern
#

it's almost equivalent to using the System.IO classes

open dagger
#

Well, I guess it is a good thing to look into FAKE at one point.

#

(afk for a while, just fyi)

untold fern
#

FAKE is pretty easy but I found it best to keep it in a project rather than fsx files as they seem to recommend

mossy whale
#

would introducing FAKE in the project leak through to people that just want to use this library?

untold fern
#

nah FAKE is purely build time

mossy whale
#

sure, I get that it is at build time
but that doesn't say much when talking about a library as it doesn't say which build time ๐Ÿ˜…

untold fern
#

lots of F# projects use FAKE and it doesn't end up in their nugets

#

hmm maybe different as you are relying on Myriad at client build time

#

the other way would be to put a post-build task with MSBuild

#

post-build your lib or pre-build their lib

balmy bison
#

CopyLocalLockFileAssemblies works for me

#

Maybe the newer dotnet have broken it

#

Im on 6.0.202

#

In building and distributing Myraid I merge two nugets together due to tools assemblies having issues

#

Also makign sure you have the correct props file and import it when testing and local building helps with tests etc

balmy bison
#

Hows it going @mossy whale / @open dagger are you making much progress?

open dagger
#

I had no time to work on it since my last message

open dagger
#

@balmy bison Idk, I have checked, but the file still does not seem to be available when Myriad is running the generator.

#

I can manually specify to copy the files for a package... but I also would need to do that for each dependency

balmy bison
#

What version of dotnet? I can only think they broke something again

open dagger
#

For runtime, or msbuild?

#

The project uses 6.0, though the output says that it uses the 7.0 binary to build

#

Use build tool: C:\Program Files\dotnet\sdk\7.0.202\MSBuild.dll

untold fern
#

Where it references System.Runtime 7 based on the SDK even though the project is 6

open dagger
#

Hmm... when I try to specify a 6.0 msbuild using the ide, msbuild complains about not being able to find fsc.exe

untold fern
#

Yeah only thing I found that works was uninstalling net 7 completely and keeping 6 or making the project 7. The latest comment suggests another workaround though

open dagger
#

Yeah, I tried that last one. Did not fix it.

#

I'll see about using (or removing, it's not like I need it ) net 7

#

Anyway, here is the log from the build, maybe it helps finding another solution:

         - 'd:\Users\Beliar\.nuget\packages\godot.fsharp.sourcegenerators\0.0.1\lib\net6.0\Godot.FSharp.SourceGenerators.dll'
         OTHER: System.Reflection.ReflectionTypeLoadException: Unable to load one or more of the requested types.
         Could not load file or assembly 'FSharp.Compiler.Service, Version=41.0.7.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. The system cannot find the specified file .
            at System.Reflection.RuntimeModule.GetTypes(RuntimeModule module)
            at System.Reflection.Assembly.GetTypes()
            at Myriad.Implementation.findPlugins(String path) in D:\a\myriad\myriad\src\Myriad\Program.fs:line 22
            at [email protected](String path)
            at Myriad.Main.main(String[] argv) in D:\a\myriad\myriad\src\Myriad\Program.fs:line 136```
open dagger
#

Uninstalling net 7 did not fix it

mild tiger
#

Do we think it is sensible, to share one common denominator container?

open dagger
#

^

mild tiger
#

dev container

#

So the development could be more fluid, future contributors would just download that, and not run into a ton of issues

#

Reproducible environments

open dagger
#

You mean more like a container for building?

#

Like github workflows?

mild tiger
#

There are containers with all SDKs, libraries and stuff, ready to develop

mossy whale
#

ah, you want a docker container.

Ideally that won't be needed and I kind of dislike developing it in a "pristine, carefully crafted" environment compared to the messy setups that people will actually use it in as it will have to run in those same environments as well (something that now seems to give problems)

mild tiger
mild tiger
#

And also for me, its hell of a pain to setup a new environment

open dagger
#

For me containers are more for building the final binary or package

mild tiger
#

In combination with Godot, I think it's fine to combine both in one container, maybe with a presetup VSCode

mild tiger
mossy whale
#

yea.... no...

we need to get this working on people's PC's without containers

open dagger
#

Well, when considering development, not deployment

mild tiger
#

I could try to assemble something, it's maybe one of the few things I can see myself doing for this

mossy whale
#

no one will want to use this if they suddenly need to grab docker or something

mild tiger
#

I mean for us, to work together

mild tiger
#

that would be crazy

mossy whale
#

I think the current problems are related to using the thing correctly

mild tiger
#

Honestly, I think F# and specifically Ionide is far away from just working on everybody's computer

#

Particularly out of the box

mossy whale
#

๐Ÿคท

mild tiger
#

You guys are all using Linux?

mossy whale
#

yep

#

at least, I am

open dagger
#

No

mild tiger
#

Windows?

#

WSL

open dagger
#

Yes, sadly

mild tiger
#

ok

open dagger
#

I have WSL

#

Also, Roslyn Source Generators/Analyzers have a similar problem when using nuget packages. One has to specify that files from there should be included so they can be found when running those. It is even mentioned in the official documentation.

balmy bison
#

The inline Roslyn msbuild code gen is a pain like that

#

I use that in myriad too for some msbuild gymnastics

#

Ionide has only intermittently worked when building myriad so I just use Rider

balmy bison
#

I re-opend the issue in Myriad anyway, if anyone wants to add an assembly resolver then I'll merge a PR.

open dagger
#

I think unless it is possible to call nuget restore, or something then the only way is to have these files in the package

#

Though, these build plugins are not something I know anything about, so feel free to ignore my message

#

I guess dotnet would benefit from having something like build packages, similar to cargos (rust) build dependencies

untold fern
#

I have some code I could contribute that tries to find dlls in the NuGet cache based on a deps.json if that would help?

balmy bison
#

I thought this was just a fails to build thing rather than runtime bllop

#

i.e. transient reference fails to copy to build directory

#

Thais was also an issue in F#4 years back, I rember addign a fix to build script generation for #r generation

#

Its annoying there are lots of various facets that make things just fiddly to do when it should be simple

#

Do you have the private attribute set?

<ProjectReference Include="../../src/Myriad.Plugins/Myriad.Plugins.fsproj" PrivateAssets="All" />
<ProjectReference Include="../Myriad.Plugins.Example1/Myriad.Plugins.Example1.fsproj" PrivateAssets="All" />
open dagger
#

In the project that builds the nuget, or that uses it?

#

I have it in the generator project:
<PackageReference Include="FSharp.Compiler.Service" Version="41.0.7" PrivateAssets="all" />

balmy bison
#

I only have the one project

#
  <ItemGroup>
    <PackageReference Include="Myriad.Sdk" Version="0.5.1" />
    <PackageReference Include="Myriad.Core" Version="0.5.1" />
    <PackageReference Include="Myriad.Plugins" Version="0.5.1" />
  </ItemGroup>
#

Consumer ^

open dagger
#

I have 2 projects, one for the myriad generator plugin, which creates the nuget, and one that consumes that. The error is in the consumer project.

balmy bison
#

I dont think Myriad has a hard reference to FSharp.Compler.Service that comes in via Fantomas

unborn leafBOT
mossy whale
open dagger
#

Ah, I see, Fantomas is just a formatter, or at the very least can't be used in the same way

open dagger
open dagger
#

According to the results from the checker the types have no attributes, which is incorrect

#

Or I am just using it wrong, probably more likely

mossy whale
#

sorry, been a while since I worked on this. Not entirely sure what you are referring to ๐Ÿ˜…
It might be that I made a mistake somewhere though

#

when working on that commit I basically did no testing

open dagger
#

I tried answer.ImplementationFile and the Declarations in it, which I can extract a list of FSharpEntity from, which match with the entities in the file, but none have the attribute that is set on them. Then I saw that answer.PartialAssemblySignature.Entities also is a list of FSharpEntity but same results.

frail dune
#

Fantomas.Core new version 6 has a simplified AST called Oak

mossy whale
open dagger
#

The information from Myriad had these... but it would be suboptimal to have to use different parsers just because both have incomplete information

mossy whale
#

yea....

open dagger
#

Well... it must be available somehow. I wonder what we are missing

mossy whale
#

๐Ÿคท

open dagger
#

Hmm... I just tested by adding Serializable (because it is available from the framework) and the log actually writes that attribute from the list, just not the one defined by me... so the problem is something else

#

So, basically the code I wrote to get attributes work, just not for the custom attribute. I guess the checker needs information about that one...

mossy whale
#

maybe if we split the attributes into their own library?

#

just throwing random things at this wall ๐Ÿ˜…

open dagger
#

Well, it is defined in another file. Maybe getting the project options from a single source file is not the best way... still looking for what is, though

open dagger
#

I think GetProjectOptionsFromScript is really just for fsx files

open dagger
untold fern
#

I seem to remember there was a project to read fsproj but it was discontinued as the possible contents of fsproj is too varied due to msbuild

open dagger
#

Yeah, that is one I also found, but that was some years old, but this seems recent

open dagger
#

Well, it works using the ionide proj-info package to read the project and get the options

balmy bison
#

parsing project info has always been an utter car crash.

open dagger
#

Well, we get better results when having good project project options, and getting them from the actual project seems like the best way.

balmy bison
#

yep, thats the only way, its how tooling works.

crisp moth
#

yep, the easiest source of errors and breaks ionide every few sdk/f# versions

mossy whale
#

I start to think that reading the .dll and generating code based on that after the compile step is more sane ๐Ÿ˜…

open dagger
#

Well, I almost get the same result as just with Myriad now, but with way less code.

#

Just convertManagedTypeToVariantType does not seem to work correctly yet. But that is for another time.

balmy bison
#

The tast and its quirks can be overwhelming, it took a few years of use before the null references were plastered over with options ๐Ÿ™‚

open dagger
#

Ah, the problem was that I passed an abbreviated type definition

balmy bison
#

Yeah, they can be recursive too

open dagger
#

Okay, union as godot resource is working (with simple types, others are untested) , at least the f# file, though the c# file just needs to be a skeleton calling the methods.

open dagger
#

Hmm... if we want our generators to create the c# classes, we need to keep in mind that godot only supports one class per file, which also needs to be the same name as the file.

#

Though, we can use the name of the file passed through Myriad to infer which type the user wants to generate, if there are multiple possibilities.

#

Hmm... no, actually if it is in a nested module/namespace that still won't work

mossy whale
#

If it is a nested module then you can make a folder to represent that though, right?

open dagger
#

Yeah, that is probably the only way

open dagger
#

Okay, the way I would currently implement creating the c# files would be by just outputting them to a (customizable) folder (with subfolders for modules/namespaces) while/after creating the specific f# files, without having the user to specify where to put each file. Since the name of the file has to match the class anyway and c# does not care about order, I don't think this is a problem, and/or better anyway.

mossy whale
open dagger
mossy whale
open dagger
mossy whale
open dagger
#

Anyway, I'll just do it the way you described, with the user having the possibility to change the folder

mossy whale
#

๐Ÿ‘

there is an issue open about needing C# files so maybe in the future we can remove this stuff.

open dagger
#

I am not 100% sure about putting the c# files besides the f# file. There could be potential conflicts if, for whatever reason, there are types with the same name in the toplevel module/namespace in two different f# files (or 2 namespaces inside one file). With the godot constraints on the name of the c# file this can't be solved with just encoding the top level namespace in the file.
The easiest solution would to create folders for even the top level namespaces/modules of a file. The other might be to detect conflicts... but I am not even sure if that is possible given that the Myriad generators work on a per-file basis.

#

Or we encode the namespace in the c# class name using "_" (and name the c# file accordingly)

#

Which leads to bad file names, though

mossy whale
#

hmm... but shoving the C# files in some random file structure is also not exactly nice because that is how you interact with the engine

What if we adopt the same "1 exposed thing to Godot per file" thing? Would that help reduce conflicts?

open dagger
#

Hmm... if we also have a "(exported) type name must match file name" constraint then it would work. We don't even need a namespace structure then.

#

So basically "If you want a type to be usable in godot you have to make separate file for it (until the godot dotnet loader is changed to allow multiple types)"

mossy whale
#

yep

open dagger
#

Having to only handle one type would also have been easier...

open dagger
#

Eh, I'll keep it as handling lists, but checking for it only being a single item.

#

Hmm... how can I tell Myriad that a certain file has nothing for a given Generator?

open dagger
#

Or rather, how does one work with multiple Generators in Myriad generally?

mossy whale
#

๐Ÿคท

#

normally they look for attributes

but that happens in the generator itself

#

not sure if there is some nicer way of doing this

open dagger
#

Yeah, but in the project one just says that a file is generated, but multiple generators could find attributes, or one could find none... and we can't error that one since that stops building completely

mossy whale
#

hmmm.... no idea

open dagger
#

Hmm... okay that might not be a problem. For multiple all sources are added... so if none generate something that means the input file is wrong anyway

#

So, unless they generate something that conflicts that would not be a problem, I think?

mossy whale
#

sounds like it? ๐Ÿ˜…

open dagger
#

I mean, that could only be if they use the same namespace/module as their first statement

#

And a namespace/module has to be the first statement in a file, and the generators have to work as if they generate for an empty file

#

I mean, we also could write a single generator for everything (with the different parts in separate files), and differentiate by what attribute is given (and error when there are multiple that would lead to a c# file being generated)

#

Actually, that might not be a bad idea - given how dotnet works in godot. I have to think a bit more about that.

open dagger
#

So, what I am thinking is the following:
The generator has a list of sub generators (like one for Scripts and one (or two?) for Unions/Records as resources) that have to implement an interface that we specify.

That interface has two methods, one "Verify" that returns the number of Godot types that would be generated, and one "Generate" that does the generating (and returns the same Output that a Myriad generator does)

The generator will first run "Verify" on each generator and if there are more than one Godot types being generated it will error.
If that checks it it will run "Generate" on the single generator has a type to be generated.

I am unsure whether we want to pass the GeneratorContext from Myriad or the Tast (probably either the FSharpCheckFileResults or the FSharpImplementationFileContents) to the Generators.

mossy whale
#

Yea, sounds reasonable

open dagger
#

And what do you think the generators should work with, the GeneratorContext (from Myriad), the FSharpCheckFileResults , the FSharpImplementationFileContents, or something else?

mossy whale
#

I don't know what is best

We need to be able to walk up the inheritance tree though

open dagger
#

Well, with the GeneratorContext you can choose what to use to parse the file (AST, TAST, something else) but need to do more manually, the FSharpImplementationFileContents has information about the specific file and FSharpCheckFileResults also has related information about the checked file. FSharpImplementationFileContents and FSharpCheckFileResults are TAST.

#

FSharpImplementationFileContents can be obtained through FSharpCheckFileResults, but I am not sure if we need the other fields.

open dagger
#

So, I think for now I use GeneratorContext and write functions to get FSharpCheckFileResults and FSharpImplementationFileContents from it and we see later what is needed

open dagger
#

Okay, I pushed the changes for the main Generator and interface to main in my repo

open dagger
#

Okay, the generator to create resources from unions and records is now finished and merged

mild tiger
#

Hurray : ๐Ÿฅณ

mild tiger
#

In case anyone missed it

solemn plover
#

Hello guys, how is the state of the interop? How can I test in a project? What are the next steps and what can I do to help?

#

I am learning Godot and have some knowledge of F#, it isn't much but I want to help in what I can ๐Ÿ™‚

mild tiger
#

There is also a fork from lenscas, with one commit ahead, and six commits behind.

mossy whale
#

I.. haven't touched the project in too long ๐Ÿ˜… Sorry about that.

IIRC I was working on making it work so actual classes could be generated for your nodes while Beliar worked on doing some resource stuff.

If anyone wants to take a stab on making the classes generate that would be huge

open dagger
#

I am currently working on other godot related things, but after that I might look at it, if no one else did at that point.

mild tiger
#

Thanks ๐Ÿ‘๐Ÿป

solemn plover
#

Great!

This project should be used with the one that you create for your game? Or are you planning to put it with the ones made inside the engine?

open dagger
#

I am not quite sure I understand what you mean

solemn plover
#

To use the Godot.FSharp.SourceGenerator, should I use the project as a project reference (or a NuGet package in the future), or it will be proposed to be added inside the engine itself, in Godot.Sdk (or some other .Net project inside the engine)?

mossy whale
#

F# isn't supported by Godot

#

so, though some changes to Godot might be merged to make this project nicer to work with I highly doubt that this project as a whole would get accepted to be included into Godot

solemn plover
#

Understood

#

so, I should reference the project in my own game?

#

would I still need to create the "C# glue" classes?

mossy whale
#

sadly, for the time being, yes

Until that is solved a stop gap is to generate those C# classes automatically like how one can do for Godot 3.X

open dagger
#

Okay, I have rewritten the generator for nodes so that it generates the F# code for nodes based on the input.

#

The problem with SaveGodotObjectData and RestoreGodotObjectData remains, and I doubt that the PR will be merged, so idk what to do there

mossy whale
#

yea, no way it will be merged for 4.1, so, at best 4.2 I guess....

We can put a forked version of those generators on NuGet that isn't turned off the same way as the ones from Godot itself and then just ask people to turn the Godot source generators off and to use our version.

And maybe poke the devs a bit after 4.1 got released

open dagger
#

I just found a problem with that approach. For the IDE using a different SDK from nuget will work, but the editor and templates will be using the bundled assemblies, which don't have that attribute. So, we either need to provide the whole editor and templates (for dotnet builds) or remove generating these functions - I don't really know where they are even used, they are never hit in a debugger.
Technically it is only needed for the generators, but I am not sure if it is possible to only include it in the generator/analyzer stage of Roslyn.

open dagger
#

The only other way that might work is to have intermediate classes, that are in a separate package/project, for the c# classes to inherit from that only contain code for these special methods. This is technically not difficult but there needs to be one for every class that inherits from GodotObject.

mossy whale
#

you... don't have to replace the entire SDK though? Just turn the source generators off from the SDK in Godot (which can be done in the csproj file) and then import the forked version which just contains the forked source generators?

open dagger
#

I mean, that is possible too if the code to disable the source generators is removed from the forked version.

#

We then just need to name the fork different... which is easy

open dagger
#

Okay, that seems to work. I just need to find where to move the definition of the attribute to, as it currently would be in the GodotSharp project. Luckily the source generator only needs to match the name, not the actual type.

open dagger
mossy whale
open dagger
#

In the csharp project file one would then need to add the following:

    <ItemGroup>
      <Analyzer Remove="@(Analyzer)" Condition="'%(Filename)' == 'Godot.SourceGenerators'" />
    </ItemGroup>
  </Target>```
#

I think for 4.1 they added the other PR to disable generators for the whole project, so then that also should work.

#

What is missing for the object generator is creation of the C# files, and more testing.

open dagger
#

Okay, C# files for the nodes are now also generated

open dagger
#

Okay, after a bit of testing, there are some things that are not working correctly. One is that the generated code for some inputs is not correct and the variant type for objects (like Texture) is not correctly determined.

open dagger
#

Okay, fixed the errors I found and the type for Objects is correctly determined with specialization for Nodes and Resources

mild tiger
mossy whale
mild tiger
#

Thanks

mossy whale
#

hmm... what should be used as the hint path in the fsproj file so it can see godotsharp?

mossy whale
#

I'm an idiot, can just reference the nuget package nowadays laughlc

mild tiger
#

In case someone is interested

#

GDQuest discontinues their lifetime plan

#

It's still available until the 15.Juli

open dagger
#

Seems like nuget does not display the readme the same way that github does

open dagger
#

The Godot.FSharp.SourceGenerators.CSharp package has been updated for 4.1

#

Also while rewriting an old godot fsharp project I have found some bugs in the fsharp generators, which I am going to check out

open dagger
#

Okay, fixed the bugs and uploaded a new package (pending validation).

#

The generated code should work for 4.0 and 4.1 as nothing 4.1 specific is generated at this point

open dagger
#

Hmm, I think there was a problem with updating the package. I have found another bug anyway, so I first fix that.

open dagger
#

Oh, there is still bug in the generated GetGodotMethodList() when there are no methods to add to the list. Though at worst godot prints an error

gilded geode
mossy whale
#

the thing that Beliar_83 worked on basically brings the godot 3 workflow back and ideally better than ever

#

so while the methods described there do work, you should probably check out the thing Beliar worked on :)

gilded geode
#

Sounds good

open dagger
#

I think we basically do the same, but automatically generate the code that does the C# <-> F# stuff

gilded geode
#

Ah. makes sense.

mild tiger
#

It is only for Godot 3

#

The author said he is interested in contributing, once Godot 4 is settled on how it implements C#

#

And I know he is constantly busy

gilded geode
#

Ok. Yes he seems quite active and also very interested in integrating F# into Godot.

mild tiger
#

Yeah. I posted him the repo on his Discord

#

He works at the university and has a very tight schedule, but yeah, he is quite involved in Godot for a couple of years. ๐Ÿ™‚

Welcome @gilded geode by the way ๐Ÿ™‚

#

๐Ÿ‘‹๐Ÿป

gilded geode
mild tiger
#

Lifeguards are here, watching the beach ๐Ÿ–๏ธ ๐Ÿ›Ÿ

open dagger
#

Anyone having a problem with this signature (inside a NodeSript) ?:

        (state, 1) // 1 = Example for return value```
open dagger
#

Interesting. StringName.op_Equality("methodname", &method) works in HasGodotClassMethod but not in InvokeGodotClassMethod

#

I think the Godot generators has the property and method names as static fields for a reason

open dagger
#

Ah, I think that was rather because these two methods were not correctly called

open dagger
#

I have pushed the fixes to the repo and uploaded 0.0.4 to nuget

open dagger
#

I have looked at GlobalClass in 4.1 and while it generally would work if we add that attribute to the C# Class - adding it to F# type does not - it cannot be used for the "Any class inheriting from" constraint in godot hint strings, as the F# types do not (and can't) inherit from the generated C# class.
The main handling of the GlobalClass also seems to be done in C++ code. There is nothing generated in C# when adding that attribute.

mossy whale
#

oh, welp

#

GlobalClass was like, the only reason I wanted to update from 3.5 to 4.1

#

so... fun

open dagger
#

I mean, you can still use it when you want it to appear in the "Create Node" list, but inheritance won't work correctly

mossy whale
#

I need it for resources sweat_smile_lc

open dagger
#

Yeah, that is where I want it too

#

You can, for example, put a hint string of "Component" and it will allow only that specific global class but not any that inherits from the generated F# node, only from the generated C# node.

#

It would also be possible to create another F# and C# project for the base type and inherit from that... but that is suboptimal

mild tiger
mossy whale
mild tiger
#

3.5 got a lot nice things backported and your project seems to fare the easiest this way

#

You are doing something entirely 2D related, so OpenGL and the old graphic stack generally will be enough

mossy whale
#

navigation didn't fully get backported though soblc

mild tiger
#

did they say why?

mossy whale
#

because doing it fully would break everything IIRC

mossy whale
open dagger
#

@mossy whale Sorry, I explained incorrectly.
If you have a GlobalClass class, for example "Component", that inherits from Resource and another class that exports a field with that type in C#, the editor would only allow setting that field to an instance of "Component" or any GlobalClass class that inherits from Component.
For F# that is not working correctly as godot can only see the GlobalClass if that attribute is set on the C# node, and the generated F# types inherit from the base F# type, not the C# node, so they don't inherit from the C# node.

open dagger
#

And it actually might work if we change how the types are generated, though I have to think a bit about that

open dagger
#

Actually, it more likely might not work, but I'll have to try out things to be sure.

open dagger
#

I don't really think there is a good way to do this

mossy whale
open dagger
#

I am off to work, will reply later

mossy whale
#

Ok

open dagger
#

@mossy whale From what I got the CSharpScriptLanguage class in C++ calls a method in GodotPlugin to gather information about a script. GodotPlugin checks, through reflection, if the class has a non inherited GodotClass attribute and sets the "global_class" variable to true or false. When the engine calls "get_class_name" on CSharpScriptLanguage it returns the name of the C# class if it is a global class.
This should also work if the GlobalClass was defined in another assembly/package.

mossy whale
#

hmmm... so.... if the C# part depends on the F# project as a .DLL, that would work then?

open dagger
#

I think so, though it might depend on what you actually mean

#

I have, for example, made a C# Project (did not want to go through F# for a class that is basically empty) with a single global class called "Component" which the F# project references and uses to inherit from.

#

Suboptimal, but it works

#

I wonder if we can write the whole dotnet side of GodotSharp in F# and use that as a replacement. We would not have to touch C++, but are bound the interface that is there. And it is still native interop, just only the dotnet side

mossy whale
#

didn't even really need it for anything Godot related. Just for some logic of what a type is...

open dagger
#

Yeah, probably not, I don't really know too much about native interop

#

It also is not an easy thing to do, even if we just "copy" what is done in GodotSharp

mossy whale
#

I do find it weird that it going through .dll's is fine but through project references is not really?

Because even .dll's that are C# can't really register their classes, can they? As there are no files any more?

So, I find it weird that then works but our F# classes do not?

What happens if you compile the F# to a .dll first and depend on that instead of the fsproj file?

Sorry if I am missing something, just woke up sweat_smile_lc

open dagger
#

Ah, the project also uses the Godot.NET sdk which generates the entry point for that assembly, and I guess godot looks looks for that for in every assembly... not quite sure.

#

Hmm, there is no iterator in gd_mono.cpp

mossy whale
#

so... we also need to somehow create an entry point?

open dagger
#

It is something like this in the generated C#:

    {
        [UnmanagedCallersOnly(EntryPoint = "godotsharp_game_main_init")]
        private static godot_bool InitializeFromGameProject(IntPtr godotDllHandle, IntPtr outManagedCallbacks,
            IntPtr unmanagedCallbacks, int unmanagedCallbacksSize)
        {
            try
            {
                DllImportResolver dllImportResolver = new GodotDllImportResolver(godotDllHandle).OnResolveDllImport;

                var coreApiAssembly = typeof(global::Godot.GodotObject).Assembly;

                NativeLibrary.SetDllImportResolver(coreApiAssembly, dllImportResolver);

                NativeFuncs.Initialize(unmanagedCallbacks, unmanagedCallbacksSize);

                ManagedCallbacks.Create(outManagedCallbacks);

                ScriptManagerBridge.LookupScriptsInAssembly(typeof(global::GodotPlugins.Game.Main).Assembly);

                return godot_bool.True;
            }
            catch (Exception e)
            {
                global::System.Console.Error.WriteLine(e);
                return false.ToGodotBool();
            }
        }
    }```
#

I think UnmanagedCallersOnly does not even work in F#

mossy whale
#

does that matter?

open dagger
#

Not quite sure

mossy whale
#

it sounds like it is just there to prevent C# from calling it?

#

and in our case we probably have to have this file be the last in the fsproj list...

#

so.... kinda solved that way sweat_smile_lc

open dagger
#

I think the compiler uses that to generated different ILASM, or something?

mossy whale
#

oh...

open dagger
#

Or something do to with how the function is found from hostfxr

#

It might work without, but not sure

mossy whale
#

and... .NET has no way to patch DLL's after compiling, right?

Otherwise we could've just made a .DLL with just this piece of code and patch it into the .DLL that F# code got compiled to sweat_smile_lc

open dagger
#

"Any method marked with System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute can be directly called from native code." but "This attribute is currently unsupported by the F# compiler. Applying it will not achieve its intended effect."

#

I don't think so, but not like I'd know

open dagger
#

Okay, I just did a quick test. If you have a Resource Type in another assembly you can't create an instance of that with the editor, but you can have a property that has that class as a Type and create instances of inherited classes defined in the main project.

mossy whale
open dagger
#

I think so

#

You also probably could create an instance through code too

#

This would work for the use case I have in mind, but not sure about others

mossy whale
open dagger
#

There is a 4.1 branch if you want to check it out, but I have not yet added generating the correct hint strings to the generators, but you can change the generated f# code to add that, for now.

#

Something like this properties.Add( Bridge.PropertyInfo( Variant.Type.Object, "Component", // Property name PropertyHint.ResourceType, "Component", // Global class name (LanguagePrimitives.EnumOfValue<_, _> 4102L), true ) )

mossy whale
untold fern
open dagger
#

After having rewrote my old project I noticed some things with Myriad (or is it how we use it?):

  • Editing a single file seems to cause every file to be regenerated
  • Files are generated sequentially which increases the build time greatly on large projects
mild tiger
#

@balmy bison Can you help us?

mossy whale
#

Files are generated sequentially which increases the build time greatly on large projects

Doesn't totally surprise me and I don't think it would be a problem if

Editing a single file seems to cause every file to be regenerated

is fixed

open dagger
#

Yeah

#

Oh, btw, I have found out that the entry point is only needed for the main project, for others they don't seem to be called.

mossy whale
#

oh

open dagger
#

So, I am not entirely sure how the class in another project is found, though maybe the Bridge still can find it because the main project references the project?

#

Also, I wonder if FSharp.CompilerService can be used inside the Bridge to get information about FSharp types

#

That might be easier than rewriting the NativeInterop in F#... but it needs testing for viability

open dagger
#

Hmm... it is also possible that the regeneration is done because of a change in the project structure

mossy whale
#

Hmmm that still doesn't sound great...

#

Can we store cache files somewhere?

#

Would that be faster?

untold fern
#

maybe hash the input and include the hash as a comment in the output F#, then when regenerating check if the output has the hash in it

open dagger
#

Hmm, I guess we can do, probably in some "." folder

mossy whale
open dagger
#

I mean, nothing is stopping us to load files

mossy whale
#

In before it ends up being slower laughlc

open dagger
#

So, I'd say store the hashes in a key/value file

#

I'll create an issue and look into that, unless someone else wants ^^

mossy whale
#

at work now

#

and still haven't managed to port over my game soblc

open dagger
#

Hmm, if I, through an AutoLoad, call ScriptManagerBridge.LookupScriptsInAssembly manually for the assembly of other projects the Classes are correctly registered

#

Well, except F#, but I think this is (also) because there is no loader for .fs files.

mossy whale
#

so... we need to make a loader for our F# files?

#

well, that is going to be fun /s

#

on the plus side, that should allow us to then drop C# file generation as a whole, right?

open dagger
#

Yeah, IF it works

mossy whale
#

yea... and that is a big if

open dagger
#

One thing is, that the Bridge looks for an AssemblyHasScripts attribute, with a list of the types, on the assembly, but this should be doable, I think

open dagger
#

Hmm, the C# loader does not really do anything other than call something on the Bridge (which does things with the already registered Type, or create an empty), set the source text and return the script.

open dagger
#

Hmm, doing some tests with just allowing "fs" for CSharpScripts and then using the Bridge to add a Node defined in F# it seems to be added correctly, displays properties, but they can't be edited. It could be just something how Godot finds methods.

#

I mean, outside of F# specific things the Types should be compatible, anyway.

mossy whale
open dagger
#

I have it checkout out locally

mossy whale
#

ah, nice

open dagger
#

And, Yes I know C++, but No, I'd like to stay away from it, unless necessary.

mossy whale
#

like any sane person :P

open dagger
#

If it works we may just need a single LoadFSharpTypes() in an autoloaded C# script, so I am looking more into that.

mossy whale
#

nice

#

(and we also need to convince the Godot guys to accept your changes to the engine...)

open dagger
#

Or write an FSharp loader (that inherits from the CSharp one) in dotnet, or as an extension (in rust ^^)

mossy whale
#

oh, that is also possible? Nice

open dagger
#

It should be, I am just changing it in the Engine directly as just changing these lines is faster ^^

mossy whale
#

yea, I can imagine sweat_smile_lc

open dagger
#

Ah, i forgot [<Tool>]

open dagger
#

Well, first, we actually can't have a ResourceLoader for fs files, unless we kinda duplicate much of what the dotnet module does... but I have found out that godot does not care what actually is in the file - though it needs to exist for the class to be recognized. What is important is the type that has the ScriptPath attribute with the file.
If, for example an F# type has the attribute [<ScriptPath("res://MyNode.cs")>], and there exists a file - even an empty one - at that path godot will register the F# type as that script. The editor would show the wrong file contents, though, so it could not be edited in there.

#

So, kinda tricking godot into thinking it loaded a CSharpScript, even though it is not. It works because of how dotnet works.

#

That seems to be the only way to be able to use F# with Godot without having to write proxy classes in C#, that does not need us to pretty much reinvent what the current dotnet binding does.

mossy whale
#

so... if Godot doesn't care about the stuff inside of the file

would it then be possible to edit the kinds of files it looks for for C#?

#

to also include .fs files?

#

there are open issues about .NET support as a whole, so if we can demonstrate that we have a way to get F# working with only that one check being in the way of things then I think we have a pretty decent case on why that check should be editable in some way?

#

with editable I mean either in a setting somewhere or perhaps even better, have it be editable by code itself. Then projects that aim to add more language support through the .NET interface (like the F# thing you worked on) can just add their file extension to the check

open dagger
#

Well, as Godot does not actually parse or compile the dotnet files, yes, the loaders are kinda only for the editor to show the source.

Now, for full support you want to have actually classes that inherit ScriptLanguage/ScriptLanguageExtension for the types, as otherwise the editor would try to reformat F# files like C# ones, and that does not go well.

#

Of course the base loading would be the same, so only specific formatting and syntax would have to be in these.

#

Of course, outside of modules, which need to be compiled into the engine, we can't inherit from CSharpScriptLanguage, as that class is not visible to extensions or GodotSharp.

open dagger
#

Anyway, would it be okay for you if the F# generators are rewritten to take advantange of that?

mossy whale
#

sounds good to me

mossy whale
#

you have a lot more knowledge about how everything works, so I am just going to trust you :)

open dagger
#

The other advantage is, that we don't have to care what the C# Generators would do, so it works with the default SDK as is.

mossy whale
open dagger
#

Ah, yeah, that is true

#

Not sure how that can be fixed, though

mossy whale
#

ideally with some way for Godot's generators not being dumb sweat_smile_lc

#

or.. F# just biting the bullet and adding protected

#

actually, why doesn't F# have protected?

open dagger
#

Can't remember

untold fern
#

IIRC F# doesn't have protected because it encourages bad, OO-focused designs

open dagger
#

Well, F# did other things just because it needs to be compatible with C# too

#

Although it is compatible, just not with the Godot generators

untold fern
#

yeah, it can work with protected but not in a C#-F#-C# sandwich

mossy whale
open dagger
#

But that probably is still way off in the future

#

I also still don't know when SaveGodotObjectData and RestoreGodotObjectData are being called, they don't seem to be during normal operation

#

So, not sure if they are even absolutely needed

mossy whale
#

I think you can save some things to disk

#

Like resources

#

So.... I'd assume that that is when that happens?

open dagger
#

Ah, that could make sense

mossy whale
#

Not sure when nodes are though

#

Might be related to the hot reload thing from godot

#

Or... Packed scรจnes?

open dagger
#

Well, those are resources

untold fern
mild tiger
#

the languages can't just directly interact with each other

mossy whale
#

which will still be a pretty big hurdle

mild tiger
#

good so ๐Ÿ˜‚

mossy whale
#

no?

#

not if you want people to use F#

mild tiger
#

I want to use Godot with FSharp

open dagger
#

Okay, yeah, the functions are called when the assembly is reloaded in the editor.

open dagger
#

There seems to be another problem with the assembly reloading. The type seems to not be registered after that, and I have yet to find out how fix that.

#

This would only be a problem in the editor, but still is not good

open dagger
#

I think for this to fully work, godot would have to fully support having godot classes in different projects/assemblies

#

Though, if you don't mind having to remember to close the editor before rebuilding the solution, it would still work

#

And the only reason that it works now is because the autoload script is being called before godot is doing things with the registered types

mild tiger
open dagger
#

You mean the on where it creates actual c# files? then, yes.

#

I mean, it supports reloading the assemblies

#

What also might work is just having the fsharp project as the assembly, with the things I tried today

mild tiger
#

I meant in the text/code editor

#

Ah, sure. You meant the Godot editor

open dagger
#

Yeah, editing the code is another things

mild tiger
#

Do you think this will be possible?

#

I slightly remember how that would have to be implemented

#

Maybe I am capable doing that

#

But this was in Godot 3, no idea if they changed something about it

open dagger
#

Technically yes but I am not sure about the details

#

Though the editor is pretty bad for anything that is not gdscript, or just small fixes, anyway

mild tiger
#

I usually hook VSCode to Godot in the settings, so that it opens, when I click on a script

Then I hover the VSCode window exactly over where the Godot inbuild editor is, usually

#

I just like, to edit the files within the context of Godot, it feels just right to me

open dagger
#

Okay, if I change the project assembly in the godot project to the FSharp assembly it works even after rebuilding and reloading. So, outside of the protected thing, there is no general problem with loading Scripts from F#

#

So, I think what we need is a way to tell godot to also look in other assembly for Scripts, and then whoever wants to use godot with their dotnet language just has to set up the the things that godot is looking for.

#

Also, maybe be able to extend from a base "DotnetScriptLanguage" with a bare minimum of just having to override "getRecognizedExtensions"

#

Because the basic stuff for dotnet languages should be the same

open dagger
balmy bison
mild tiger
#

All fine, yeah thanks ๐Ÿ‘๐Ÿป

#

They figured it out

balmy bison
#

Cool, because I have no idea how any of that works now! :-p

#

abandonware

mild tiger
#

๐Ÿ˜…๐Ÿ˜œ

open dagger
#

Actually I havent, but good to know.

balmy bison
#

I was only joking, its just I havent looked at it, or F# in a good while

mild tiger
#

I just saw, that Rider has specific Godot support, like for Unreal and Unity.

Might be possible to simply use that, once fsharp does comply with Godot itself.

mild tiger
#

Developing? If it ever comes to that

frank apex
#

sorry to off topic a bit here, but any reason why to go with Godot as opposed to something like MonoGame? ๐Ÿค”

curious as I've used Unity before, and was considering either trying to get Godot or Unity to work wwith F# but now I'm just veering towards learning MonoGame

mossy whale
#

monogame is just a framework rather than a fully fledged engine so it has less stuff builtin

frank apex
#

True, I'm kind of oblivious to how much stuff is missing from MonoGame vs something like Unity, guess I'll just take the time and explore a bit ๐Ÿ˜

mossy whale
#

working properly with unity is going to be hard as the csproj file is full with hardcoded paths (a few hundred of them IIRC) and you are not supposed to put it nor the sln file in git.

#

you can get it to work but I doubt it will work with multiple people/multiple machines

frank apex
#

oof, that sucks, MonoGame it is then wish me luck I guess

mossy whale
#

if you can split the fsproj into 2 or more files
One of which containing all the paths to unity's stuff
and the other everything else then it could work

mild tiger
#

@frank apex I did look at the available engines for FSharp, and found Godot personally to be the most balanced

Unreal 4 has a pretty good implementation, but it broke on the way to Unreal 5, and the engine seems to be suited for entire teams, and not single individuals anyway due to it's complexity

#

One thing, that could be interesting to you us FNA.

This is an accurate open source implementation of XNA 4.

MonoGame is it's successor, and some people say it's changes to the architecture are for the better and the worse.

open dagger
#

For me it is mostly that Godot has an editor that also can be extended by plugins.

frank apex
#

So, I just got F# to work with Unity pretty flawlessly following a modified version of @balmy bison's tutorial
problem is no hot reload (https://hotreload.net/) ๐Ÿ˜ข

mossy whale
frank apex
#

With an .fsx script you can create a References.props file with all unity references, then reference it in your .fsproj, then just copy the dlls over to Unity. I have to manually refresh the project folder by right clicking unfortunately for it to detect it, and it is a lot slower vs having hot reload with C#, but at least its F# ๐Ÿ˜„

using dotnet watch build and having vscode save when editor loses focus makes it a lot better as well

#

I'll put it in a repo, give me a sec

mossy whale
#

neat :)

frank apex
balmy bison
frank apex
open dagger
#

What do you think about adding F# Script (fsx) Support to Godot instead/in addition to F#? I did some basic testing and it should work. It would need to be written as an extension, in C++ (which can be ported to Rust gdext, once at least one issue there is resolved), with probably most of the actual code in dotnet (to make calls to the F# compiler service/FSI). It would allow the issues we ran into to be resolved by it being an actual separate language, though we also need to implement the classes needed for that, but we have the F# compiler service/FSI to help with that. It would still use the dotnet version of Godot and the GodotSharp package, so we don't need to reimplement the base setup for dotnet.

mossy whale
#

yea, originally I was hoping that we could just piggybank off C#'s implementation and safe time on our end

but it sounds more and more that that isn't going to work out properly

open dagger
#

Yeah, but since there is already an interpreter for fsx we can use, which can be called from code, it should not be too difficult. I just have only tested whether it could work at all.

wicked flare
#

@open dagger i'd be interested to follow you on this / lend a hand where i can. i'm not actively using godot right now but i've wanted to try and integrate FSI with godot after playing with the FCS APIs. i've done a bit of research on the new extensions system for v4 but wasn't sure how to make that work other than generating some C/C++ bindings on the fly maybe. i was hoping it might be possible to generate C# files from FSX scripts or f# projects and use the FCS API there but i haven't been following the .NET attempts you've already done closely

open dagger
#

@wicked flare What I am currently trying to do is to implement FSX as an actual script language, which means implementing a ScriptLanguage, Script, ResourceSaver and ResourceLoader for it using FCS and FSI. As there does not seem to be really much in Documentation for those I am trying to figure it out by looking at the implementatiosn for GDScript and CSharpScript.
It has 2 parts: A gdextension written in C++ and a plugin written in C#/F#.

wicked flare
# open dagger <@160176014738194432> What I am currently trying to do is to implement FSX as an...

oh nice! i forgot about those layers/integration, i scanned some of the C# integration code a while ago to get an idea of what was going on under the hood and stumbled into some of that. i'm going to follow what you post and i'll share anything i find (tho currently i'm preoccupied with some webrtc stuff, working on some bindings + a little chat app for my resume while i'm between jobs)

what time zone are you if you don't mind me asking? i'm gmt -6/CDT

open dagger
#

GMT + 2 / CEST

open dagger
#

I was hoping I could use the already existing GodotSharp package inside the scripts, but it seems that, probably because of some other others things that I had to do to work around an issue due to how Godot loads assemblies, the calls from dotnet to native are not setup in the context of the script and fail.
I will try to see how difficult it actually is to write something similar (though it can probably be simpler for fsx) in C++, or maybe better in rust (with gdextension).

frank apex
#

Hey! Not sure if interest died down here, but after the Unity debacle I ended up giving Godot another go. I tried Beliar's lib, but had a few issues and ended up just creating a generic script using reflection on _Ready. to load an F# script from an imported assembly that's referenced on the Godot's .csproj.

using System;
using System.Reflection;
using Godot;

static class Utils
{
    public static fs.IActor? GetIActor(string? typeName, Node node)
    {
        try
        {
            if (typeName is null)
            {
                GD.PrintErr("FsFile must be set!");
                return null;
            }

            var assembly = Assembly.Load("fs");
            var type = assembly?.GetType(typeName);

            if (type is null)
            {
                GD.PrintErr($"Failed loading type {typeName}");
                return null;
            }

            object[] args = { node };

            var actor = Activator.CreateInstance(type, args) as fs.IActor;
            if (actor is null)
            {
                GD.PrintErr($"Failed creating instance for type {typeName}");
            }

            return actor;
        }
        catch (Exception e)
        {
            GD.PrintErr($"Error instantiating type {typeName}: {e.Message}");
            return null;
        }
    }
}
using System;
using System.Reflection;
using Godot;

public partial class FChar2D : CharacterBody2D
{
    [Export]
    public string? FsFile { get; set; }

    fs.IActor? actor;

    public override void _Ready()
    {
        actor = Utils.GetIActor(FsFile, this);
    }

    public override void _Process(double delta)
    {
        actor?.Proc(delta);
    }
}
#

With this I can just re-use FNodeName classes everywhere, setting the FsFile to the fully qualified class name.

F# classes simply implement the IActor interface, and take the corresponding node as single constructor arg. Been working without issues for me.

Not sure if I'll encounter any performance penalty later on unless I'm spawning a lot of things at once, but let's see I guess ๐Ÿ˜„

frank apex
#

biggest downside is needing a separate class for each base node type (FNode for Node, FNode2D for Node2D, etc), but after that there is no need to touch C# code at all.

type IActor =
    abstract member Proc: delta: float -> unit

type Player(node: CharacterBody2D) =
    let speed = 200f

    let getMoveVector () =
        let x = Input.GetActionStrength("move_right") - Input.GetActionStrength("move_left")
        let y = Input.GetActionStrength("move_down") - Input.GetActionStrength("move_up")
        let vec = Vector2(x, y)

        if vec.Length() > 0f then vec.Normalized() * speed else vec

    interface IActor with
        member _.Proc _ =
            node.Velocity <- getMoveVector ()
            node.MoveAndSlide() |> ignore

open dagger
#

Hmm, interesting. I don't think you can make global classes using that, though?

open dagger
frank apex
#

one thing I've been considering is just trying the render server directly with Garnet ECS (f# ECS)

open dagger
#

One of the additions of 4.1 was that you can define "global classes", that is, classes that you can, for example, use to constraint lists in the editor to specific types, with the [GlobalClass] attribute.

#

And garnet is nice, yeah.

#

Which is also something I wanted to use together with global classes to just define components in the editor, but that did not work with how the fsharp generator was written.

frank apex
trim fern
#

Think a <@&457404552136622090> could pin the original message? Discord has made it a struggle to get to it without scrolling several eons.

pallid fossil
trim fern
#

Excellent! Thank you @pallid fossil ๐Ÿฅณ

twilit zodiac
twilit zodiac
#

Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Ello", "Ello\Ello\Ello.fsproj", "{61F61CA2-F869-4E8D-8779-94F05E0E1C3C}" EndProject

I noticed that some people's solution files have something along the lines of this

#

But mine doesn't. I have no clue how to get that long alphanumerical string

mossy whale
twilit zodiac
#

And, am using Omnisharp

#

Is there a fix, besides switching IDEs?

twilit zodiac
twilit zodiac
# twilit zodiac Is there a fix, besides switching IDEs?

https://github.com/dotnet/vscode-csharp/issues/1623 ah crap, seems like it's been a longstanding issue. I wonder if it works with the C# devkit extension, but I wouldn't be surprised if it doesn't

GitHub

@DustinCampbell Last I asked about possibilities for autocomplete of F# namespaces and types in omnisharp and the answer was it won't work because roslyn doesn't compile F#. I think you may...

mossy whale
#

If you tell omnisharp to decompile dependencies then it somewhat works (for me at least)

#

You have to reload the editor (or maybe just omnisharp if there is such an option) for it to pick up changes in your F# code. And going to a type brings you to the decompiled version rather than your F# code but.... At least it is something....

twilit zodiac
#

Reload the editor anytime I want to go access new F# code from C#? Yeah that's messed up. If it's that bad, that's reason enough to switch to Jetbrains IDe or VS

mossy whale
twilit zodiac
#

Anyone know of a way to make these error messages not show in Godot? They are mildly annoying

twilit zodiac
# twilit zodiac Reload the editor anytime I want to go access new F# code from C#? Yeah that's m...

Also, my experience with VS was bad too. Not as bad; I could at least get the C# to recognize the F# code whenever I built/compiled it. But, it was still bad because whenever I used f2 to rename my F# code, it doesn't update the C# code correspondingly it seems. Furthermore, if I open the C# code and try to update the names with f2 there, it didn't seem to work. So, I had to have both VS and VSCode open whenever I do refactorings...

lucid steeple
#

A thread this long should be its own channel, or several different threads i think

wicked flare
#

I'd participate in a gamedev channel

hazy swift
#

I don't do gamedev but I find it interesting and would lurk in such a channel.

rain bough
#

bump/necro, "worked for me"
though this is all it returns

mossy whale
#

going to be honest, don't look at that stuff. Look at the repo from Beliar_83

rain bough
#

I'll do that too, I've been just scrambling these past few days for something usable ๐Ÿ˜…

#

so I was kinda hoping that it can be solved with just a simple resourceformatloader/resourceloader

mossy whale
#

yea, sadly that is a no.

Godot is too dependent on source generators for that and source generators are very much a C# only feature.

The repo from Beliar_83 gets as close as a "drop in" you are going to get but it requires fiddling a bit with the existing source generators to get them to play nice with the stuff Myriad generates.

There is a PR open IIRC to make them work out of the box but I don't think there was any progress there for a long time

rain bough
#

Oh shoot there was a PR? Godot repo right?

#

(Also quick question after a lot of Ctrl+F-ing, signals are still iffy even with Beliar_83's generator, right?)

mossy whale
mossy whale
rain bough
rain bough
mossy whale
#

I think there is a .NET bug regarding signals in Godot right now though that prevents them from being properly cleaned up. Not sure about the details though

rain bough
#

sounds even better

also tried the myriad generator quickly, not against the package in any way but I guess I'll stick to some simple libs at the moment
maybe a C# (for resources) -> F# -> C# (nodes+calling fs lib) sandwich will be okayish lmao

#

gotta see if Godot can handle multiple projects in any way

mossy whale
#

nope

#

it can't

rain bough
#

d a m n

mossy whale
#

and somehow I still think that Godot has better .NET integration than Unity.

Really shows how low that bar has gotten >_>

rain bough
#

holy sh*t it works

#

i mean
it's 100% "hand-written" public partial whatev in the sub-project, so that's a pain point

rain bough
#

okay, got a "[importedTypePath] conflicts with imported type [importedTypePath]" warning but no issues
so uhh "update", that can be used for extra glue

rain bough
#

eh, janky enough to set up but it works

#

so
C# project for resources
F# lib that has a node that inherits from said resource
C# lib that has a node that inherits from the F# resource
both the C# and F# lib refer to the Resource project

mossy whale
#

๐Ÿ‘

rain bough
#

only cons: in this A<B<C inheritance, if I have a "top-level" export reference to type A
and give it a type C
I just get the following error:

E 0:00:00:0454   :0 @ System.Object System.Runtime.CompilerServices.CastHelpers.ChkCastAny(System.Void* , System.Object ): System.InvalidCastException: Unable to cast object of type 'SandwichParent' to type 'ResourcesProject.ResourceAttempt'.
  <C++ Error>    System.InvalidCastException
  <C++ Source>   :0 @ System.Object System.Runtime.CompilerServices.CastHelpers.ChkCastAny(System.Void* , System.Object )
  <Stack Trace>  :0 @ System.Object System.Runtime.CompilerServices.CastHelpers.ChkCastAny(System.Void* , System.Object )
                 VariantUtils.generic.cs:385 @ T Godot.NativeInterop.VariantUtils.ConvertTo<T >(Godot.NativeInterop.godot_variant& )
                 OtherProjectCaller_ScriptProperties.generated.cs:13 @ Boolean OtherProjectCaller.SetGodotClassPropertyValue(Godot.NativeInterop.godot_string_name& , Godot.NativeInterop.godot_variant& )
                 CSharpInstanceBridge.cs:57 @ Godot.NativeInterop.godot_bool Godot.Bridge.CSharpInstanceBridge.Set(IntPtr , Godot.NativeInterop.godot_string_name* , Godot.NativeInterop.godot_variant* )
#

sorry for spamming this channel tho ^^"

mossy whale
rain bough
#

also this err's not for "plz troubleshoot", just some quick logging

mossy whale
#

I'm happy to see Godot + F# get some more attention

rain bough
#

and F#'s Array/List/any modules are imo a bit better than resorting to linq in "heavier" areas

mossy whale
#

even if I stopped using both the combination has potential to be good

It just will be too much effort and time for me to do anything with it sweat_smile_lc

rain bough
#

or just accessing RenderingServer parts

rain bough
#

but the error above is a huge bummer

#

the signals and their listeners worked perfectly

#

but any attempt to call method from the parent-parent-... type is no bueno

#

maybe I'll just have to limit the F# usage to not inheriting gd types or doing some sandwich inheritance thing

#

(why am I "wasting" time on this with a 2 week deadline for uni)

rain bough
rain bough
pallid fossil
#

@rain bough I don't know how much effort you are considering to put in experimenting F# -> Rust -> Godot, but any project which highlights how to write a crate in F# or a binding to a rust crate in F# and or rust is of great value to F# community

#

I'm interested in fable to rust for reaper extensibility myself (and due to lack of lua target, as this would be easier for me, than the native C api and the current rust crates for it)

rain bough
#

But it would be definitely a different kind of funny to idk
Rewrite the dotnet/mono bindings of godot (not like I could do it) in rust
Coming from F#
So essentially a dotnet<->native binding written in dotnet
(Thinking about that, is it even possible to do something similar at least with NativeAOT? f# or c# doesn't matter)

pallid fossil
#

there is no pressure to deliver a nuget that covers all of F# to Rust to Godot (which seems to be huge), just more "F# wrappers to Rust crate" code samples out there

#

with fable, it would be F# specific, no C# support for such nuget

#

as for doing AOT stuff that binds to C++, well supported for both C# and F#, if I understand you

rain bough
#

I know, I just got a bit sidetracked by myself with "can Bob build it"

rain bough
#

It would be a few layers of either weird, cool or painful to (using godot as an example) write .NET<->native bindings in pure .NET

#

Practical? Doesn't sound like that
Technically possible? God I'd throw money at some mythbusters-level projects of these sorts

pallid fossil
#

unless we are talking CPPProvider<"""#include "<godot/godot.hpp>" """> if you are aiming for dotnet community support, C# is probably better, but isn't there already this existing? and what is missing for AOT?

rain bough
#

Naah, not a type provider but the other way around

#

Like "hey here's my nativeAOT module that tells you how to just call my stuff xoxo gossip girl"

#

So nothing missing in particular (afaik), just joking about what if "c/cpp glue to call dotnet code" could be written in dotnet

#

To see if there are multiple ways to do this stuff:

  • good ol cpp like they do
  • fable->rust+godot-rust (stability is another question)
  • maybe some nativeaot thing?
#

Honestly wish I had the expertise+time to actually work on stuff like that

pallid fossil
#

fable->rust if the godot rust story is stable, I think would be stable, assuming you do basic F#, the rust fable target seems to be well behaved, just needs more examples of bindings to crates IMO

#

and no managed stuff in the end result, which other AOT path, you still deal with GC and dotnet runtime bloat

#

didn't I see some figure of benchmark comparing F# AOT via rust vs dotnet recently?

mossy whale
rain bough
#

But what if (sorry for these)

#

Instead of a "we'll target godot through gd-rust" path

#

There'd be a way to only implement just the loaders/other gdextension stuff

#

So yeah it'd still need godot-rust
But instead of going with compiling all of our gd-related F# code to work through godot-rust
There would be just the "gd-mono-like" loader that runs through that module

untold fern
rain bough
#

so just a dotnet bootstrapper(? if that's a name that makes sense) instead of "every bit of game-specific logic"

mossy whale
untold fern
#

Yep, it generates C at compile time that bootstraps the .net runtime and native exports based on annotated F# functions

rain bough
mossy whale
rain bough
#

yep, their memory management is the one thing that always stopped me from learning it

#

(not bc i don't like it but bc it kicked me 100 times for good reason)

mossy whale
#

and I do not know what the rules exactly are when you start to mix those languages.

Like, I get that 2 &mut T's to the same object are a no go.

But what counts as "same object" if the object lives in C/C++?

Are 2 nodes that are gotten from a get_node() different objects? Even if they both go to the same node?

I, simply put just don't know

rain bough
mossy whale
#

that actually won't work

#

breaking the rules means UB (or just not compiling, depending if we are talking pure safe code or unsafe code).

UB is both a problem at runtime and compile time

pallid fossil
pallid fossil
mossy whale
#

"I like ub and I know what I am doing" is not a thing that exists. Not even in C/C++ land.

People may think it exists but it doesn't

#

At best it means you are hard stuck with an exact version of the compiler with some exact settings and some prayers that your code will not break when you recompile it

untold fern
#

Just add a unit test that the UB is still doing what you expect with each new compiler

mossy whale
#

Not just compiler changes but also every code change and even hardware change.

And technically, even the existence of that unit test may change the behaviour of the ub

#

Also, UB infects the callstack. As the compiler is allowed to assume that UB never happens it means that any path that would lead to UB also never happens

#

So, measuring the UB in one place doesn't mean that the UB will behave the exact same everywhere

#

It all depends on what the compiler exactly managed to do and how good it was able to analyse your code at any given point

#

Like I said, UB is not just a runtime thing like normal bugs

#

It also exists at compile time

lucid steeple
#

Wait wait. Godot cant have C# nodes that use F# libs?

#

If so aw shit.. i was doing really well in a DDD approach i was doing

open dagger
#

It can, just Nodes in F# are a Problem

lucid steeple
#

Yea, that part i was aware of. But using C# as glue between godot and f# should be fine right?

open dagger
#

Aye

mossy whale
#

Would it? I thought that the F# exports and stuff won't show up then?

open dagger
#

You can call into F# Functions

#

And write Nodes in C#

open dagger
#

Also, Update: After what felt like a year, but apparently was only 4 months, I came to the conclusion that I still don't know enough about Native Interop to do what I wanted - add F#/FSX as a script language to Godot, and I want to do something different for now.

#

I hope that the Godot devs do know that (and their Engine) better and the teased - not sure if work ever started on it - Dotnet (C#) Support through a GDExtension will make that possible.

rain bough
#

Though it's weird that exports from an F# base are not visible even if it's on a C# child (class Cs : Fs case)

#

Also if you want to pass around F# types in signals, a good workaround is using https://github.com/pocketberserker/MessagePack.FSharpExtensions to just serialize those DUs and stuff, pass them to a signal and handle deserialization in signal handlers (I usually just slap an active recognizer on the paramlist of the handler if it's written in F# lol)

GitHub

MessagePack Extensions for F#. / msgpack.org[F#] . Contribute to pocketberserker/MessagePack.FSharpExtensions development by creating an account on GitHub.

mossy whale
rain bough
mossy whale
#

Beliar_83 made an attempt with Myriad, I think it makes some stuff work but not everything (no global class for one)

open dagger
#

Yeah, that is when I started trying to add F# as a script language, but the main Problem is not having a Library that I can use to call into godot. The one for C# requires special initialization from the engine (that is why it is a separate Download) and trying to use GDExtension it seems using Function Pointers through multiple DLLs (from Dotnet to a Shared Library to Godot) does not really work, or at least I don't know how that can work.

open dagger
#

It might also work if someone knows how to get swig to work with classes that have several circular dependencies.

#

Because that was the closest I had it to working, I think.

wicked flare
#

https://github.com/godotengine/godot-proposals/issues/7895

I've been playing with GDExtension's C API a tiny bit and I think this route might be the right way to get better F# support for Godot. As far as I understand the proposal would be to create something akin to rust-godot or swift-godot by writing a C/C++ GDExtension to host dotnet and call into a .net DLL. It's a decent amount of work but I think with that route there wouldn't need to be any awkward C# code generation/C# only entry points for F# to deal with

Alternatively I was poking around the .NET integration code w/ Godot and it seems like right now Godot .NET works by using reflection on a DLL (who's path is specified in the project settings) to call an initialization method that does all of the classdb bindings/setup. From what I could tell there wasn't any reason you couldn't drop in a F# dll, but godot's project builds might get in the way of that. If C# is trying to move to a GDExtension based route anyways though probably better to go with the former option

GitHub

NOTE: This proposal aims to be a public technical discussion regarding the future of C# in Godot. The idea is to get help from the community to reach implementation direction consensus. Describe th...

#

I think whatever route gets used there's a need for a F# => ClassDB registration code generator like the existing C# source generator

untold fern
#

No reason that wouldn't work. There are tools to generate C# from C headers to get all the struct definitions (I can't remember the names now but they are in this discord). C# is probably better at the lowest level anyway for a few language features like fixed buffers in structs and function pointers. As well as just reading more like the original C source.
You can use DNNE https://github.com/AaronRobinsonMSFT/DNNE to generate the C exports that Godot would call and the necessary code to host .NET. That works from F# or C#.
After that you'd want a wrapper to take the unmanaged structs to something more like idiomatic F#.

open dagger
#

Yeah, basically I am waiting if and when that C# GDExtension is being implemented, though if someone with more knowledge of native interop wants to look at it, I won't stop them ^^

wicked flare
#

i was able to load + call GD.Print successfully by using the netcorehost API last night

i'm looking to see what i need to do to register a new class type

i'll post a link to a demo repo soon, gotta fix the hard coded paths first

wicked flare
#

i used Godot 4.2.1 (non-dotnet version), the dotnet version seemed to have issues with me loading another runtime

open dagger
mossy whale
#

@rain viper sorry for ping but I am guessing you might be interested in this thread :)

rain viper
#

Yep, I had already found it ๐Ÿ˜›

mossy whale
#

ah, oops. Sorry sweat_smile_lc

tidal mauve
#

Hello, I'm a mantainer of the Godot .NET integration and would like F# to be added to the list of community-maintained languages. Would you be interested in that? If so, I'd like to help you port your existing packages to the new GDExtension-based bindings. Feel free to ask any questions, and let me know of any issues that may come up during the process.

I have a sample project entirely in F# that you can see here: https://github.com/raulsntos/summator-fsharp

I'm not an F# developer so this is likely not idiomatic. I imagine you'll want to build F#-specific packages on top of the .NET bindings to add convenience, like Myriad generators or other fancy F# features.

wicked flare
#

I know I'd love to see easier integration with Godot ๐Ÿ™‚ I've been keeping an eye on the GDExtension w/ .NET stuff so it's good to hear you expressing interest in f#

i'll try to play around with the sample when i have some time, i know one thing i'd really like to see is a way to integrate the f# interactive repl for hot reloading (although that might not be necessary with the new gdextension setup?)

tidal mauve
#

Just a heads up, since we don't have a module yet to load the .NET runtime with hostfxr, you'll need to build a native library with the [UnmanagedCallersOnly] attribute, but as far as I know F# doesn't support this. Maybe there's some other way to build a native library with F# and expose a C compatible method.

Regarding hot-reloading, I haven't implemented that yet in the new bindings and I'm not sure how it works, but I know there's an API for it.

wicked flare
#

just tried it out with a C# entry point and it all works for me, really exiting stuff. thanks for reaching out to us ๐Ÿ™‚

wicked flare
#

i had a hostfxr project handy and the Summator example works with that, i was also able to verify the F# repl api works with the hostfxr version

tulip meadowBOT
#

@frank apex gave @tidal mauve +1 reputation.

strong cipher
#

What's the current best way to use F# in Godot right now?
There seems to be a lot of conflicting information right now

mossy whale
#

depends on how you actually want to use it.

If you want to use F# as the only language, then Godot 3.X with that extension to automate the boilerplate is probably the "best"

if you just want to write some F# in combination with most of it being C#: then just make an F# library as normal and keep the amount of Godot code in F# minimal

strong cipher
#

With Godot 4.2.x, are there any tools for using F# with boilerplate?

mossy whale
#

sorry, I don't know of any

strong cipher
#

looking through the thread, is there any update on Godot supporting F# nativly like it does with C#?

strong cipher
tidal mauve
#

@strong cipher No, this means that when the new .NET integration is out (which will definitely not be in 4.3), the F# community will likely have an easier path to provide support, and I'd be happy to help in that effort.

open dagger
open dagger
wicked flare
#

so [UnmanagedCallersOnly] is working in F# w/ AOT for you? really cool to know if that's true

untold fern
#

it might be that UnmanagedCallersOnly "just works" provided the arguments were all already unmanaged

open dagger
#

I am not sure how it works. I was looking into the possibility of adjusting the IL code to get the same result as in C#, when I noticed that it actually is the same. It probably is best to look more into and/or at least have it verified as working by someone else.

untold fern
#

I'm using that attribute and ignoring the warnings in F# for a different project and it works. But I'm also generating the C API that gets called from native code using the DNNE library

rain bough
tidal mauve
worthy sphinx
#

Hi, I am looking at godot for ui. I already F# code and want to hook that api to events for the ui. Did this by creating a F# lib and create base F# node types/classes there. The signals are define in the original partials root class (C#).

I couldn't find much info online besides reading this channel. I would like to know if there is a better way atm also should I create my own attribute for F# signals and will those be compatible with records?

rain bough
#

For records and unions you should serialize them to a byte array, since Godot can't deal with those by default (using MessagePack.FSharp for that) and then deserialize on the receiving end
Either this, a dictionary or a wrapper class for these records which is more work than its worth

#

And also looking for a better way, I'm currently defining a base interface for everything I'd use in F#, and use my "API" with those (and native godot types) and implementing them on the C# side

Aaand the signals are exposed as observables in those interfaces since I've ran onto some inconsistent issues with implementing interface events, which results in a ton of boilerplate (not sure if it's a "right way" atm)

worthy sphinx
rain bough
#

Eh, there's a lot of glue but it could be manageable, I'm also thinking of pausing the F# part tho

#

It's essentially a waiting game of "who'll write the codegen for it" atm to skip the boilerplate, but then there should be a generally agreed-upon path to pick for it

#

Since some people would like to possibly go with records and fp-heavy godot code, others would like an "it just works" way even if it results in imperative/oop F# code so uhhhhh
Yeah

#

Personally I'm waiting for the new GodotBridge which has been mentioned above, and some motivation to look into codegen-related topics

jovial granite
#

I had a look at Godot recently and it seems to suffer from:

  • deeply wedded to OO techniques. For example creating a door, attach a script to it that has some behaviour in response to an event. How do I replicate that behavior in a function so that I can share it between other doors.
  • deeply wedded to its own editor. I want my source code an assets to be seperate. I want my game logic to be separate from the game engine.
rain bough
#

For the first one: something like "let openDoor<'T&#Node&#IDoor> (door:'T)" or something similar (plus whatever other params you need) could work in a module, then you'd only have to call the method from your godot C# code, at least that's what I'm doing right now to share common code between some Nodes

rare thistle
#

Is there a post about how would a preferred api look for f# script?

frank apex
#

i dunno tbh im too clueless on godot to have a proper opinion

#

i just cooked an api i like, but its definitely not performant, at least the way its implemented

namespace fs

open Godot

module Systems =
    let setup (world: World) =
        world.on <|
            fun (Process deltaTime) (area: Area2D) (sprite: Sprite2D) ->
                area.GlobalScale |> string |> sprintf "hello! area 2d with scale %s" |> GD.Print
                sprite.Texture.ResourcePath |> sprintf "texture is %s" |> GD.Print

#

i made it so

  1. user registers functions before anything runs
  2. this regsitration creates a function that takes in a custom node type and registers its lifecycle methods if that node has all the required nodes from the function (Area2D and Sprite2D) in this example
  3. this created function is called by any nodes of the Entity type on _EnterTree (its an empty custom node that gathers all functions it should call and just calls them)
#

the Godot Node looks like the following:

using Godot;
using fs;

public partial class Entity : Node
{
    FNode fnode;

    public override void _EnterTree()
    {
        fnode = fs.FNode.Create(this);
        fs.World.Singleton().registerNode(fnode);
    }

    public override void _Ready()
    {
        fnode.On(new fs.Ready());
    }

    public override void _Process(double delta){
        var x  = fs.Process.NewProcess(delta);
        fnode.On(x);
    }
}

theoretically once its expanded enough there would be 0 need to touch any c# code and everything could be done from f# ๐Ÿค”

#

i guess maybe some databags or something like that would be done in c#, but literally just using variables to [Export]

strong cipher
#

I would love to see a full example of this, it looks very interesting

frank apex
#

im probably going to try something with codegen in a bit

frank apex
#

i got something converting

[<Gfs>]
type Converted() =
    inherit Node2D()
    member val Speed = 5.0f with get, set

    override _._Ready() = ()
    override _._Process(delta: double) = ()

to

        using Godot;

        public partial class Converted : Godot.Node2D
        {
            fs.Converted converted = new();
            
            [Export]
            public float Speed
            {
                get => converted.Speed;
                set => converted.Speed = value;
            }
                
            
            public override void _Ready()
            {
                converted._Ready();
            }
                
            public override void _Process(Double delta)
            {
                converted._Process(delta);
            }
                
        }

trivially, but there are two main issues:

  1. you cant reference other generated godot classes from the f# project due to circular dependencies
  2. every converted class now has a heap allocation

using structs could solve this but property ergonomics on structs would be pretty bad

frank apex
#

this also works pretty well:

[<Gfs(typeof<Node2D>); Struct>]
type Converted =
    val mutable Speed: float

    member _._Ready() = ()
    member _._Process(delta: double) = ()

drawback remains:

  1. you can't reference other generated godot classes from the f# project

new drawback:

  1. no type checking on method override -- its being done by convention by the generator on every method starting with _

but, removes the drawback of every f#-converted godot class resulting in a new heap allocation