Intercepting WorldServer.addWeatherEffect Argument

24 posts in this topic

I am creating a mod that intercepts the strike location of a lightning bolt. The only way I could get the lightning strikes is with WorldTickEvent and even then the client renders lightning before I can move it. The best, and most efficient, way I can see to combat this is to intercept the argument for WorldServer.addWeatherEffect(Entity). All I would need to do is set the position and one of the variables in the Object.

What is the best way to get the instance of the Entity being passed in before the method code is executed?

Here is the code for the method if it would help. I want to get entityIn before the method starts executing.

Share this post

Link to post

Share on other sites

What are you trying to do, exactly? Move the lightning? You will need to use the tick event to detect the lightning in the weather effects list and move it. Potentially you might also need to update the lightning's position on the clients with a custom packet, I am not sure whether the game keeps the lightning's position in sync or not.

I left out most of the method because it's pretty long. There are some more conditions before what's posted but nothing that looks to me like it would be easy to alter, but you can look at it in your IDE if you want (line 392 in WorldServer class).

I just wanted to point out that since the lightning entities are being newly created within the method, and being pushed directly to the method where they are sent to (nearby) clients, you won't be able to do an easy reflection hack to change a field for this. In order to literally change the course of lightning bolts, you would probably have to replace either the entire World class or the entire WorldServer class, neither of which I really recommend.

I can also think of a byte code edit that would do it, but would also very likely make your mod incompatible with many other mods, so that's not a good approach either.

All that being said, I JUST thought up a possible solution. This method in WorldProvider is part of the check which allows or disallows normal lightning behavior:

If you're ok with just redirecting lightning bolts in the overworld (and any modded dimension that uses DimensionType.OVERWORLD / WorldProviderSurface), you can create your own edited version of WorldProviderSurface which contains all the original fields and methods from the original WorldProviderSurface (there is actually only 1 field and 1 method; the rest are simply redirected to the WorldProvider superclass), and add in an overridden method for canDoLightning that returns "false" instead of "true". This will disable normal lightning completely.

After all that, you'll have to add your own system to the game to reintroduce lightning. I would just copy the existing system in WorldServer.updateBlocks() as much as possible but add a special condition for your redirected bolts.

If you decide to overwrite WorldProviderSurface with your own version of it, you'll need to replace the enum field for it in the DimensionType enum class, like this:

And if all this looks too complicated, you might want to reconsider redirecting lightning bolts!

Edit 1 ==========================

Almost forgot to say that Voidwalker is the one who familiarized me with the internal structure of enum classes, and how to properly reflect into them, so if you use this method you can thank him for that.

Link to post

Share on other sites

Doesn't fire for lightnings since they are weather effects and not normal entities - the World#spawnEntity is never called for them.

My suggestion still stands

1 hour ago, V0idWa1k3r said:

You will need to use the tick event to detect the lightning in the weather effects list and move it. Potentially you might also need to update the lightning's position on the clients with a custom packe

10 minutes ago, Laike_Endaril said:

e.printStackTrace() crashes the game after printing.

What? Throwable#printStackTrace just dumps the stacktrace to the console, it doesn't crash anything(in fact I would assume it can't)

Share this post

Link to post

Share on other sites

No, we don’t talk about ASM here. Also it’s less likely to make your mod incomparable than replacing the class with reflection if done right.

Also, don’t catch & print, crash the game!

You could also create a PR to forge to add a hook? What’s wrong with the onEntityJoinWorld event?

I do want to point out that I have tried the onEntityJoinWorld event and it does not include EntityLightningBolt.

2 hours ago, V0idWa1k3r said:

What are you trying to do, exactly? Move the lightning? You will need to use the tick event to detect the lightning in the weather effects list and move it. Potentially you might also need to update the lightning's position on the clients with a custom packet, I am not sure whether the game keeps the lightning's position in sync or not.

I was initially using WorldTickEvent to just get the latest Entity in World.weatherEffects. It could work, but it is fairly roundabout. The position of lightning is not kept in sync, and I have to send a packet to the client, or directly access the lightning from the client side to change the position.

The main issue I had with WorldTickEvent is that it only calls from the server side. I can modify lightning and everything there, but the client side gets the lightning a couple ticks later. By the time ClientTickEvent calls, the lightning has already been rendered client side and gets a glitchy rubber band effect or just jumps.

Edit: I did also try EntityConstructingEvent and lightning is called for it. The problem there is that it is called at the end of the Entity constructor. EntityLightningBolt calls super then afterwards sets its position and other variables. Also, by that point Entity.getEntityWorld() is null.

Share this post

Link to post

Share on other sites

I'd like to inform everyone that I've figured out how to redirect the lightning. Basically, I noticed that in WorldServer.addWeatherEffect(), before sending a packet, super.addWeatherEffect() is called. So I used reflection to set World.weatherEffects to an ArrayList with the add() overridden to set the position of an Entity when it is added. I just did this on the WorldEvent.Load when the world is not remote.

I essentially added in a listener to when a lightning bolt is added to weatherEffects.

Share this post

Link to post

Share on other sites

Nice! That's a far cleaner solution than what I had suggested. I'm pretty curious what you'll make using that system.

8 hours ago, Cadiboo said:

you catch the exception, and print it. The key word there is catch - the exception doesn’t get thrown & doesn’t crash the game.﻿

Sorry about that. It does, in fact, crash when I test it (with a bad field name), but not directly. It crashes because I'm using that code in the constructor of my main mod class, so even though that particular error was caught, it ends up crashing (because the mod didn't load properly and not because of the exception I had caught).

Share this post

Link to post

Share on other sites

Nice! That's a far cleaner solution than what I had suggested. I'm pretty curious what you'll make using that system.

Sorry about that. It does, in fact, crash when I test it (with a bad field name), but not directly. It crashes because I'm using that code in the constructor of my main mod class, so even though that particular error was caught, it ends up crashing (because the mod didn't load properly and not because of the exception I had caught).

I don't really see the harm in doing this, so I'll give a brief description. The majority of the mod is causing lightning to act better. By better I mean it will strike with a preference to taller objects, things made of metal, less cubelike blocks like iron bars, and entities. I'm also going to have a block that, when placed near anything that acts as a lightning rod, will absorb the lightning that hits it and convert that to RF.

Share this post

Link to post

Share on other sites

That sounds pretty cool. I've been working on a magic-themed pack so the first thing I thought of was some kind of spell that made an entity more prone to being struck by lightning or something, but I hadn't considered the idea of making the lightning more realistic.

Edit: If you finish the mod I'd be interested in using it. Already have a mod name in mind for me to look for? (That is...if you're ok with others using it in a modpack?)

Edited November 11, 2018 by Laike_Endaril

Share this post

Link to post

Share on other sites

That sounds pretty cool. I've been working on a magic-themed pack so the first thing I thought of was some kind of spell that made an entity more prone to being struck by lightning or something, but I hadn't considered the idea of making the lightning more realistic.

Edit: If you finish the mod I'd be interested in using it. Already have a mod name in mind for me to look for? (That is...if you're ok with others using it in a modpack?)

The name of the mod is "Lightning Tweaks". I do not have a release page for it yet, but I plan to make one in the near future. As far as I have been able to test (without any other mods), the lightning is correctly attracted to the blocks I specified before and entities. Most of the values used in this math is customizable through the in game config UI. I have yet to add the block to convert lightning to energy, but some placeholder values are in the config file.

I put a work-in-progress build of the mod below along with its source. Every method and variable is public and every method and class has documentation. You may use it in a modpack.

This is WIP build and has not been extensively tested with or without other mods.

Share this post

Link to post

Share on other sites

Nice! I won't be looking through your source though (or at least not anytime soon if I do); just wanted a mod name to look out for so I can try it out once you release. I just stopped working on mods for a while to test out my modpack as it stands right now and see what I need to remove/tweak. I'll put your mod name down on my modpack TODO list as something to try out when it releases.

Edit: I built a version of it and stuck it into a minecraft mods folder. It works so far.

Edit 2: I can say for certain that the Local Weather, Storms & Tornadoes mod is not compatible with this. This is because they replaced EntityLightningBolt with their own version, among other things. I'll be working on making the two compatible.

Edit 3: Done. Found out also that Minecraft spawns fires around the strike point during the construction of the lightning, so I had to remove them and then readd them in new places after.

Recently Browsing

Posts

Yeah I did.
Im asking for a solution to this in just a quick way. When I open the gui i have no rendered cursor, it only shows up when i move my mouse outside of the minecraft window and move it back in. Fix for this @diesieben07 problem.

Difficult to describe without screenshots, but I have too many options to put easily on one screen, so in all of my previous versions I split them up into 3 screens.
So when you click on Config the first thing you see is the choice of 3: "Coordinates Options" "Timer Options" or "Infopanel Options".
Clicking on "Coordinates Options" brings up a page of just those options, etc.
I implemented all this in 1.12 using the @config annotation system, which allowed this kind of structure (outer-level config, 3x inner-level configs)
I just don't see whether the new ForgeConfigSpec system permits you to do this.