Jump to content

[1.12.1] IRenderFactory and generics


jabelar

Recommended Posts

Okay, I'm admittedly fairly weak in using generics and it is causing me some trouble with my render factory. for my custom entities.

 

IRenderFactory only takes a RenderManager as a parameter. But it also is <T extends Entity>. I assume that it gets the T from the registration method where you associate the entity class with the factory. However, I'm having trouble using the T to figure out which render class to return.

 

Previously I worked around the limitation by creating a separate factory for each entity. So I'd have RenderFactoryTiger that returns the RenderTiger and RenderFactoryLion that returns the RenderLion. That works, but I feel certain that I'm not using the factory idea properly -- ideally you have one factory that returns the different renders based on the type T, right?

 

I was hoping I could just check if T was instanceof and return the render based on that. In other words if T instanceof EntityTiger then return RenderTiger. But it doesn't seem that generics like that type of thing, or maybe I'm not properly associating the T from the interface to the T in my code?

 

Anyway, I'd like to learn how to do this. Does someone have an example of a single factory returning different render classes based on the T registered with the factory?

 

 

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Link to comment
Share on other sites

14 minutes ago, diesieben07 said:

Generics are, technically, just syntactic sugar for runtime casts. They are erased, so at runtime there is no T and you cannot (usually) know what "value" T has.

Thanks for that explanation. That makes a lot of sense and explains better why I can't use the type at runtime.

 

14 minutes ago, diesieben07 said:

The easiest way to create this factory without much verbosity at all is to use a constructor reference. In this case a constructor in your render class has to match the signature of createRenderFor.

Then you can write: registerEntityRenderingHandler(MyEntity.class, MyRenderer::new).

 

In this example, is the MyRenderer the factory class or the Render class? I'm assuming factory class since that is what the register method wants passed, but my factory class currently doesn't have any constructor, just the createRenderFor method.

 

So I think you're saying I should create multiple constructors in the factory class and have each of them return a different Render<T> such as RenderTiger, RenderLion and such? And then that constructor would set a field to be RenderTiger and in the createRenderFor method I'd return that?

 

You can see why I am still getting a bit confused ...

 

Edit: I just started to implement what I just said above and immediately realized the I can't return a Render<T> because I actually have one Render for all big cats (RenderBigCat) that is just constructed with different textures depending on tiger, lion, etc. Also, I don't see how the register method would know to expect a RenderBigCat.

 

I think the main point I'm missing, is how does the factory know which entity is being registered? And how does that translate to expecting a different Render class back. 

 

I'm looking through github and all the examples I find seem to create a different factory per render class, but I'm still thinking I should be able to create a single factory ...

Edited by jabelar

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Link to comment
Share on other sites

Maybe a simpler way to help me is starting with the factory class declaration:

 

If I put: public class RenderFactory implements IRenderFactory it will complain about generic types.

 

If I put public class RenderFactory<T> implements IRenderFactory<T> it complains that IRenderFactory<T> isn't correct since the interface is bounded by <T extends Entity>

 

If I put public class RenderFactory<T> implements IRenderFactory<T>

 

If I put public class RenderFactory implements IRenderFactory<EntityTiger> it is happy, but then that means I just created a factory only for EntityTiger which is what I was trying to avoid.

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Link to comment
Share on other sites

1 minute ago, diesieben07 said:

 

Effectively MyRenderer::new is equivalent to


new IRenderFactory<MyEntity>() {
    public MyEntity createRendererFor(RenderManager manager) {
        return new MyRenderer(manager);
    }
}

 

 

Okay, so then effectively it is creating a new IRenderFactory for each type. I thought a "factory" was meant to be a singleton which handled all the types. I think that is where I went wrong. 

 

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Link to comment
Share on other sites

1 minute ago, diesieben07 said:

Yes, that's not how the factory is intended. You can use it however you want, but ultimately you will need one factory object per entity type.

Thanks that was the root of my problem. I also appreciate the detailed explanations about generics generally.

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Link to comment
Share on other sites

1 hour ago, jabelar said:

Thanks for that explanation. That makes a lot of sense and explains better why I can't use the type at runtime.

Generics (explained generically) are very useful. They're for telling the programmer "I want a thing, but I don't care what kind of thing, as long as that thing has these properties" (expressed as an interface, other class, or hierarchical relationship).

 

For example, the (old way) of registering items was a single method: GameRegistry.register(...) the parameter for that method was defined as <T extends IForgeRegistryEntry>, that interface being applied to blocks, items, potions, enchantments, and a whole host of other things. It allows the program to be clean and elegant, as the single method can handle any kind of object that needed to be registered, as they were all handled identically, even though they were disparate object types.

 

It's not that much different from declaring the parameter as the thing it is (e.g. IForgeRegistryEntry) except you can be more specific. e.g. I do this for one of my helper registration methods. Notice that the parameter is declared as being two things: A Block and IBlockWithMapper (an interface that allows me to extract a custom IStateMapper for determining how to map block states to models) and I can then do so without ever needing to do instanceof or explicitly cast. Or do things like this, where two different parameters relate to each other. In this case, that one type (V) is a child class of another type (T) and that an object of each type will get passed in: IProperty<T> property, V value

 

How generics relate to one another comes down to Covariance (the alterations are altered the same way) and Contravariance (the alterations are mirrored):

https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)#Inheritance_in_object-oriented_languages

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Link to comment
Share on other sites

Thanks for the additional info Draco. I think the main point I was missing about generics was what diesieben07 mentioned -- that the type wasn't available as a runtime field, rather it was more like a flexible casting system. I probably need to challenge myself to look at my code and figure out where generics would have been more useful and try it out. I think registries are an obvious place for it of course.

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.