Neos-Metaverse / NeosPublic

A public issue/wiki only repository for the NeosVR project
195 stars 9 forks source link

Generic component instantiation help #827

Open Earthmark opened 4 years ago

Earthmark commented 4 years ago

Right now instantiation of generic components is a very tribal knowledge thing, and I'd like to improve that. Here's some requests that I feel would assist with new and old users of logix (dynamic impulse receiver) and components in using generic components.

These are just features that I feel would be useful, other people may have other lists, but this is a list I've noticed that have been an issue for my own usage of generic components. I think they are incredibly powerful, and the system is very flexible and easy to learn besides the generics. Because I see that as a common pain point that's why I'm proposing these changes.

  1. Allow < and > instead of 1`[], the grave key isn't available ingame without a custom keyboard, and the component header already uses what I always call gator syntax instead of grave array syntax. I get that ToString returns grave array but it's confusing and often a hack (to get the grave) to use it. I think this could be done with a string replace depending how the lookup is done (I don't know of any two arg generic components atm), so it might be a low hanging fruit?
  2. Auto search FrooxEngine for types, I get it may come up with collisions, and in that case default to fully qualified falling back to FrooxEngine prefix. This would better match the component headers which don't include the namespace and would make it less effort to type FrooxEngine all the time. This is to the point Turk's custom keyboard comes with a 'FrooxEngine.' button. This extends to the other types that are commonly referenced, such as the Basex library (although I've yet to see this used successfully, see below). I get that custom component libraries would require full namespaces, but for common types where users don't know the assembly names it's very hard (or currently seemingly impossible in the case of Basex) to create genetics without help from someone already in the know.
  3. Allow referencing structs in Basex like color or vector2, I'm not sure if it is referenceable, but I've yet to see anyone pull off including a reference to a type that has a generic of a type in Basex (The list of people I've seen try includes Cyro, Turk, and Lucas, so it's not a lack of knowledgeable effort). I don't know why it seems impossible so far, but I kind wish I could make a dynamic impulse receiver that accepts an IField, but I can't get it to accept the color (it accepts System.* types freely).
  4. Allow the same shortcut types available in C# and the known types list to be useable in the generics names, for instance auto recognize int to be System.Int32, or float as System.Single. The UI has taught me the name, but in the generic context it's relying on the scuzzy system name that nobody wants to type.
  5. Allow a dynamic impulse server to create a dynamic impulse receiver of the same type. When dealing with complex types going through a receiver it is very difficult to create the receiver for a sender if it's not a primitive type. Another alternative would be the output node converting the receiver, but I get a feeling that might be an odd behavior to implement as that becomes... strange. When I have a receiver I always have a broadcaster I'm going to be sending from or can get it, so being able to generate the problematic one from the easy to use one feels like it hopefully would be not too bad?

Thank you for reading these requests, I think logix and components are incredibly powerful, I just want these stumbling blocks to be smoothed over to it's easier to use!

Frooxius commented 4 years ago

Hello. Currently the type selection is mostly handled by just CLR itself, since the functionality isn't completely finished, so you need to follow its rules. Doing a lot of these is planned, but requires substantial work - writing custom type parser and handler, so it will not happen until then.

Right now it's just passed pretty much verbatim to the type parsing functionality in .NET, so you have to live with the complexities/limitations of it.

1) Naive string replace can't handle all cases correctly, because what you replace it with depends on what's inside. If there are multiple arguments, you'd need to replace with 2[] or 3[]. The types can be nested too, which complicates things a bit.

2) This is done for basic types in BaseX already, but doing it for nested types requires the custom parser to handle it properly.. I could do the quick one for root type for the time being though.

3) Structs are by their nature not directly referencable. However having them as generic arguments of referencable class type works. If it's not being taken by the CLR then there's likely something wrong with the format, whatever you're typing in there is passed off to its type system.

4) Like mentioned in 2, this is done already for the root types, but doing it for nested ones needs the string to be actually parsed, so proper substitutions / lookups can be made. When the type is alone, it's pretty much just trivial equality matching.

5) This is planned as well, by having a proper generic type selection system and UI for LogiX nodes, which will let you select the appropriate types. I'd rather not make a system that's specific just to this particular node, as there are other nodes that could use this functionality.

For the receiver being converted by the sender that's not really feasible and would lead to a lot of weird things. Changing the type changes the whole identity of the node and that is something that needs to happen on construction of the LogiX, rather than execution.

Anyway, thanks for suggestions! A lot of this stuff is planned already, it just needs the upfront chunk of work for working with types and the type system to be done, until then we'll have to piggyback on the CLR type parsing.

Earthmark commented 4 years ago

I'll double check the basex references, maybe we need assembly references as well? I'll put feelers out for anyone who has declared a nested generic with the inner arg being a Basex type, if there's a success story it'd be good to learn from it!

I'll do more searching, this seems like it should be an easier solution... A logix parser can maybe mitigate this then? The main issue I was getting was related to nested generics that seemed to have problems.

Frooxius commented 4 years ago

What exactly do you mean by assembly references in this?

But nested types need to be fully qualified, since it's being passed directly to the Type parsing in .NET (this is not the same as the C# syntax), which doesn't understand the type aliases and has quite strict rules on how you specify and qualify the types.

All Neos does when you use "float2" for example is substitute the type when it matches the string. But when it's inside, e.g. IField it cannot be just a simple substitution, so it would actually need to parse what you typed to know what's what.

The parser wouldn't be specifically for LogiX, but in general for generic types.

Frooxius commented 4 years ago

I've added 2. for FrooxEngine. in 2020.7.31.1099, which should help reduce some typing a bit!

Earthmark commented 4 years ago

Thanks for adding that feature Froox!

I get it's a string substitution, I'm curious if I can help this issue with a logix tool to pre-formant recursive generics, I'll poke at that later maybe.

I do think this is a usability thing for a smaller section of the game, and i don't think these changes should be a priority versus things that help a larger space of people.

My thought about full assembly names may be that C# is possibly not scanning all loaded assemblies for the generic type arguments, so providing a stronger name like the full type name including assembly name is something else to try with the basex reference issue. I haven't tired that yet, but other solutions didn't seem to turn up results yet so I'm going down the list. I plan to do more poking at Basex types in general though.

Thank you for the responses, I hope you have a good day. I'll reply with my findings, which will probably be 'I was dumb".

Frooxius commented 4 years ago

It does scan all loaded assemblies for the root type, but the type fetching is then handled on C#/.NET side, so I don't have control over that. I'm not sure if it fetches all assemblies internally for the generic type parameters. If it does not, then it needs the custom parser, which will construct the generic type itself, rather than having .NET do it.

Earthmark commented 4 years ago

Ok, I started looking into doing that second thing to see what was involved, I do agree it's a bit of a bear. I see what you mean by how the paths need to be constructed now, I was doing some research and realize how the strings will need to be constructed for BaseX nested generic references. Haven't tried them ingame yet but it makes sense why they haven't worked so far.

Thanks for all the thought and feedback Froox, I'll keep poking at this and see if I can get a reusable solution for people wanting to use nested generics.

I'm not sure if you want to add future stretch goals to some of these requests or if they will get deprecated by future systems, would you rather this bug get closed or re-tagged?

TehTurk commented 4 years ago

I just wanted to add this is such a nice Addition, I'm sorry I didn't suggest getting some of these added sooner (Even tho it sadly kills my .FrooxEngine Button xD)

Earthmark commented 4 years ago

The button still helps with nested generics so don't remove it yet!

Earthmark commented 4 years ago

It looks like fully qualified types do work as you said Froox, FrooxEngine.ValueStream`1[[BaseX.int2, BaseX, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] works for referencing int2 in a nested generic! Thanks for the lead. Unsure how well this will work for System types as the assembly versions may get wonky per machine, but one of the more annoying parts has a workaround now.

Frooxius commented 4 years ago

No problem! It should still work, the assembly versions will be same on each machine, because they ship the Neos installation (each Unity build has its own copy of Mono runtime embedded).

Earthmark commented 4 years ago

Do windows builds do that as well? I was under the impression on windows it used framework instead of the mono runtime

Frooxius commented 4 years ago

No, it's all embedded Mono. Unity doesn't support using the actual .NET Framework (I wish it did, because that would help a lot with performance), they have a modified version of it too.

Earthmark commented 3 years ago

This was made using the old format but I think may still help, I remember getting grumpy at the time and made an implementation of a parser to resolve these types. I think the native c# parser might also be available? https://github.com/Earthmark/TypeResolver