Prevent snow from falling on certain blocks

Hello everyone,

I’m just trying to stop snow from weather events from accumulating on certain blocks. I’m not sure how I can check for this specific occurrence, though. I imagine a https://jd.spongepowered.org/5.0.0/org/spongepowered/api/event/block/ChangeBlockEvent.Place.html event is the right place to start, but how can I check to see that the cause was from weather?

Thanks.

A good starting point is to filter by whether the block is snow or not, and then print cause.toString() to see what’s in there

Thanks for the tip, here’s what I wound up with. I got lazy and just started checking for certain substrings, works well enough for me.

@Listener
public void onBlockPlaced(ChangeBlockEvent.Place event) {

	// Small cosmetic fix to stop snow appearing on top of player graves.
	List<Transaction<BlockSnapshot>> transactions = event.getTransactions();
	for (int i = 0; i < transactions.size(); i++) {
		Transaction<BlockSnapshot> transaction = transactions.get(i);
		BlockSnapshot snapshot = transaction.getFinal();
		if (snapshot.getState().getId().contains("minecraft:snow_layer")) {
			if (event.getCause().toString()
					.contains("Object={WorldServerMulti{LevelName")) {
				Optional<Location<World>> locOpt = snapshot.getLocation();
				if (locOpt.isPresent()) {
					Location<World> location = locOpt.get();
					location = location.add(new Vector3i(0, -1, 0));
					if (location.getBlock().getType().getName()
							.equals("gravestone:gravestone")) {
						logger.info("Blocking snow on grave.");
						transaction.setValid(false);
					}
				}
			}
		}
	}
}

That’s sliiiiiiiightly extremely bad practices, and could break at any time. Just get the object class that it’s providing and @First it.

I’ll never release something this bad into the wild, don’t worry. Are there any easy resources on understanding this whole @First archetype that Sponge is using? It’s all very different from what I remember of Bukkit and I don’t always have the time to learn it very properly.

So Bukkit hardcoded the things that caused an event (getPlayer(), getWhoClicked(), etc). This was all well and good for a concrete API spec, but it made things hard from a modding perspective (if Applied Energistics’ annihilation planes break a block, who’s the getPlayer()?). In Sponge, everything that the event caused has hardcoded methods (getBlock(), getTargetEntity(), etc) but anything that caused the event is stored in a Cause object. It’s essentially an ordered map of string to object, except objects can also be retrieved by Class. So for instance, for an InteractBlockEvent e, you might call e.getCause().first(Player.class).get() to get the Player that interacted with the block. This allows for completely extensible events, for pretty much any possible purpose.

On to the annotations: Unlike Bukkit, Sponge’s event bus system is more extended; listeners can have more than one argument. The first argument is the event, like normal. All subsequent arguments are retrieved from the Cause, and each one must have a filter annotation, corresponding to the methods in Cause, or they can be annotated with @Getter to use a getter method in the event itself. These can be an Optional, like the original source; they can also be an unwrapped value, or even a subclass of the expected value (e.g. an entity event, where I specifically require a Player). Arguments that already have @Getter or cause filter annotations on them can also be constrained with @Has or @Supports for use with the Data API. So, the example I used before to fetch the first player would be written thus: @Listener public void interact(InteractBlockEvent e, @First Player p) { If a Player is available, it’s placed in the p variable; if no player is available, the listener is not called.

1 Like

Wow, that clarifies things! Thanks for the explanation, I’ll try using this shortly with my next bunch of mod integration.