Autom Posted September 5, 2018 Posted September 5, 2018 (edited) I am attempting to modify some of the vanilla mechanics- currently clouds and difficulty, and if that works well potentially more complicated things like rain and the day/night cycle, but clouds and the difficulty system are the main ones. I think the clouds would be possible if I replaced the CloudRenderer class with a duplicate of my own design and the difficulty if I was able to override the World method getDifficultyForLocation(BlockPos pos). However, the only way I can think to do that is through ASM, which rarely ends well. Is there another way to gain access to the logic I am trying to influence that is less hackish, or an approved/safe way to do it via ASM? (If it matters I am planning to have the contents of a hash table reflect the presence of a block in nearby chunks- then clouds will render differently or the difficulty will be changed in those areas. Since there are only so many chunks loaded at a time this should not have too much of an adverse impact on performance.) Edited September 5, 2018 by Autom Quote
jabelar Posted September 6, 2018 Posted September 6, 2018 So it is possible to replace most vanilla behavior. To do so you can consider these possible approaches, in this order: 1) Use public scope fields and methods directly. For example, entities AI are contained in lists that have public access scope. So you can directly clear them, add to them, etc. Since Minecraft vanilla codes is not properly encapsulated, the scope is pretty random. Some classes are locked down tight with private access, but luckily it is very common to have public stuff. 2) Use JSON asset mechanisms. More and more logic in the game is now being represented in the JSON files. For example, recipes, achievements, and so forth. So you can pretty much just make a resource pack and change a lot of behavior. 3) Use Forge events. Most common modding problems already have a Forge event "hook" which allows you to intercept and even replace the vanilla behavior. These exist for all sorts of things from entity updates, rendering, world generation, and so forth. 4) Use Java reflection. For those things that are not public scope, Java still allows you to override the scope and access them. So for example you can do things like replace the MouseHelper field in the Minecraft class using reflection. The stuff you mentioned like clouds and weather can be done with combination of the above techniques. Quote Check out my tutorials here: http://jabelarminecraft.blogspot.com/
Autom Posted September 6, 2018 Author Posted September 6, 2018 Focusing on clouds for the moment and found the minecraftforge.client.CloudRenderer and the method setCloudRenderer in WorldProvider, but am a little unclear on what to do with them as setCloudRenderer takes an IRenderHandler and CloudRenderer is just implements an interface called IResourceManagerReloadListener with no apparent way to cast or convert them. Monitoring the WorldProvider at runtime it seems the cloud renderer is always null anyway so I don't think this is the right thing. A CloudRenderer is set in FMLClientHandler, but this just uses the WorldProvider method. Quote
jabelar Posted September 6, 2018 Posted September 6, 2018 Okay, yeah it is a bit confusing but I looked through the call hierarchy and here is what is happening. The setCloudRenderer() method is a Forge hook. If no renderer is set by a mod (vanilla never calls this method) then it is null. Then later in FMLClientHandler the renderCloud() method checks for null and if it is null then it creates a new CloudRenderer class. If it is not null then it will use the IRenderHandler class you gave it in setCloudRenderer(). In both cases, it calls a render() method but technically as you noted these are different classes that don't share a type hierarchy. In other words, for mods it will call an IRenderHandler.render() and for vanilla it calls a CloudRenderer.render() function. This means that you cannot just instantiate, copy or extend CloudRenderer class for setCloudRenderer. Instead you need to make your own similar implementation but in a class that implements IRenderHandler. So I think (didn't try it myself) you should just go ahead and create a class that implements IRenderHandler but provides a render() function that copies the functionality of CloudRenderer.render(). Since the cloud rendering code is a bit complicated, I would probably do this by "wrapping" a CloudRenderer instance in an IRenderHandler implementation. So like this: 1) create a custom CloudRenderer class called something like CloudRendererCustom that extends CloudRenderer. You would override the render() method to make any changes you wanted to the way clouds look. 2) create an IRenderHandler implementation called something like CloudRenderHandler. The implementation would do two things: (a) create an instance of a CloudRendererCustom from Step #1, and (b) it would implement a render() function that simply calls the render() function of that CloudRendererCustom instance. 3) call the setCloudRenderer() method on the WorldProvider at the time the world is created, passing an instance of CloudRenderHandler from Step #2. Hope that makes sense. You're just making an IRender implementation that calls the render method of a custom CloudRenderer extended class. Quote Check out my tutorials here: http://jabelarminecraft.blogspot.com/
Recommended Posts
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.