[Solved] Events Rewrite Questions

Hey! So I finally found the time to update my plugin after the big events rewrite (late I know), and I was wondering if anyone could help me figure out what events I should use instead! I’ve attempted to choose the correct new ones, except with there being quite a few events, I wanted to make sure I chose the correct replacements. What replaces the following events?

  • PlayerChatEvent
  • PlayerBreakBlockEvent
  • PlayerChangeBlockEvent
  • ServerStartingEvent
  • GameClientConnectEvent
  • PlayerJoinEvent
  • ServerStoppingEvent
  • PlayerPlaceBlockEvent
  • PlayerInteractBlockEvent

I know it’s a lot, but if anyone could help me I’d really appreciate it. Also, what should I use in the place of EntityInteractionTypes.USE? I haven’t really looked into it much yet, though. Thanks you! :jack_o_lantern:

1 Like

PlayerChatEvent == MessageSinkEvent.Chat
PlayerBreakBlockEvent == BreakBlockEvent Then test the cause for the Player class
PlayerChangeBlockEvent == ChangeBlockEvent Then test the cause for the Player class
ServerStartingEvent == GameStartingServerEvent
GameClientConnectEvent == ClientConnectionEvent.Login
PlayerJoinEvent == ClientConnectionEvent.Join
ServerStoppingEvent == GameStoppingServerEvent
PlayerPlaceBlockEvent == PlaceBlockEvent Then test the cause for the Player class
PlayerInteractBlockEvent == InteractBlockEvent Then test the cause for the Player class

for interaction types, you listen to either the Primary or Secondary variant of an interact event
USE is the Secondary action

4 Likes

Ily @simon816, thank you so much! :heart:

A quick follow up question, how would I get the location of an event? I could get the Player’s location through testing and casting, but it’s not always the same as the event’s location.

Note: I realized getTargetBlock() is a thing for the InteractBlockEvent.Secondary, so maybe a variant of the other Events is what I need as well?

Note 2: I checked what I was thinking in Note 1 and BreakBlockEvent (the specific example I was checking) had neither variants nor a getTargetBlock() method.

For a block change event (e.g. place/break) you can get the block snapshots that have changed using getTransactions() then look at the snapshot’s location

Perfect! And how would I get the player sending a message in a MessageSinkEvent.Chat? I see how I can access the recipients, but I don’t see an option for the sender? @simon816 just making sure you see this :smile:

I would guess you get the cause (maybe getCause().first(CommandSource.class)) but it would be convenient to have a method for that too

I tried it in the context:

@Listener
	public void onPlayerChangeBlockEvent(final ChangeBlockEvent event) {
		if (!(event.getCause().first(CommandSource.class).get() instanceof Player))

and it resulted in an NPE :frowning:

21:27:32] [Server thread/ERROR] [Sponge]: Could not pass NeighborNotifyEvent to SpongePlugin:PixelMasters2Plugin{1.0} java.util.NoSuchElementException: No value present at java.util.Optional.get(Optional.java:135)

I see that the problem is not checking if the optional command source is present, but I feel like this is a sign that I should use something else to get the player source…

@simon816 @FerusGrim @gabizou @zml @varrix (You guys help me the most)

This isn’t, actually, a NullPointerException. Although, it may as well be. Optional are object-wrappers, left as reminders to those using them that the object may not actually exist.

Meaning when you hit that good 'ol .get() method, without properly checking if the object is actually there, you get that exception, informing you that these aren’t the droids you’re looking for. Hilarious movie quotes aside, that’s your issue.

@Listener
public void onPlayerChangeBlock(final ChangeBlockEvent event) {
    Optional<CommandSource> source = event.getCause().first(CommandSource.class);

    if (!source.isPresent()) {
        return;
    }

    // Do stuff here.
}

Now, the paramount question here really is: Why doesn’t this object exist?

The answer to which likely isn’t going to be very pleasing: You’re either doing it wrong, or it’s not implemented yet.

My guess would be that you’re doing it wrong. If I’m correct in thinking this, the ChangeBlockEvent doesn’t only get called when a player is the one doing the modifying. Be it generation, other plugins, or a pesky Enderman. To clear out the rabble, add that check above in there, and see what happens.

2 Likes

Or what you could also do is:

@Listener
public void onPlayerChangeBlock(ChangeBlockEvent event) {
  if (event.getCause().any(Player.class)) {
    final Player player = event.getCause().first(Player.class).get();
    // do stuff
  }
}
2 Likes

Don’t forget .get() babe. :3

1 Like

You guys never fail me, thanks! Those seem to have solved my problem. Also thanks for the thorough explanation and perfect code!

However, I’m having another problem. I’m trying really hard to figure things out on my own and not ask a ton of questions, but a lot of these special api maneuvers seem to be a hit or miss. Two more questions:

  1. How do I get the location of a ChangeBlockEvent? There don’t seem to be any direct method calls for this on the ChangeBlockEvent. The only (clearly very wrong and embarrassing) route I found was the following, which is failing and giving me a NoClassDefFoundError.

    Location blockBreakLocation = event.getTransactions().get(0).getFinalReplacement().getLocation().get();

  2. If I’m trying to prevent players from dying, what would be the best route? Should I just prevent them from taking damage (and hunger of course)? Is there some sort of PlayerDeathEvent I should cancel? I’ve tried to use the DamageEntityEvent, but it’s not being listened to for some reason, despite correct registration.

ChangeBlockEvent is a multi block event. Previously, in Bukkit, all block events were single block change events (practically), and the problem is that Minecraft has evolved much further than that such that multiple blocks can change for any given reason. As such, it was our design to make all block related events multi-block. With the tick capturing system written by blood, we’re able to capture all block changes, even from mods.

So, the ultimate thing you should consider is all of the block transactions taking place and all the locations for the block transactions.

I had a hunch something along those lines was happening… Thanks for the clarification. However, shouldn’t my code still have worked and just missed blocks when there were multiple blocks edited in one tick? What would fix it? And also what about the second question?

For the 1st one:

You should get the location from the original block.

  1. Preventing players from dying? when the DamageEntityEvent is being thrown, cancel if the final damage would deal enough damage to kill a player (or you can write a DamageModifier that will forcibly make the damage just above the last bit of health (preferably down to 1 hp)).
event.getTransactions().get(0).getOriginal().getLocation().get();

is not working. In my current implementation, the only transactions being inspected should be ones with a source of a Player, so I feel like it’s okay to test getting the location of one transaction before figuring out how to iterate well and handle all of them. The following error is being thrown:

Could not pass ChangeBlockEvent$Break$Impl to SpongePlugin:MyPluginNameHere{1.0} java.lang.NoClassDefFoundError:

I will be testing the DamageEntityEvent and DamageModifier soon, and will let you know if I can figure them out, thanks!

The provided Cause applies to the entire event. So, if a Player is in the cause, you can trust that it’s applicable to all of the transactions.

Okay that’s really nice to know! What about the code and the given error though? Any idea what’s happening?

Debug Update:

gradle sponge dependency is up to date :white_check_mark:
sponge up to date with matching forge version :white_check_mark:

I tried writing the line with one method call for each line to isolate which method call was causing the problem. When I tried to edit a block, it worked fine. I moved back to the all method calls in one line strategy, and it still worked. :question: :question: :question: Anyways, it’s working now, so I guess I’m happy?