Skip to content

Instantly share code, notes, and snippets.

@Jikoo
Last active November 23, 2023 09:27
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Jikoo/c0126ce4ad9cc0f5ef3862180e413d80 to your computer and use it in GitHub Desktop.
Save Jikoo/c0126ce4ad9cc0f5ef3862180e413d80 to your computer and use it in GitHub Desktop.
Block bed placement duplicating crops
package com.github.jikoo.blockbedcropdupe;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState;
import org.bukkit.block.data.Ageable;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.block.BlockMultiPlaceEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
public class BlockBedCropDupe extends JavaPlugin implements Listener {
private final BlockFace[] adjacents = {
BlockFace.NORTH,
BlockFace.SOUTH,
BlockFace.EAST,
BlockFace.WEST
};
private final Map<Material, MultiBlockShape> multiBlocks = new HashMap<>();
@Override
public void onEnable() {
MultiBlockShape bedShape = (block, player) -> Arrays.asList(
block.getState(),
block.getRelative(player.getFacing()).getState()
);
for (Material material : Material.values()) {
if (material.name().endsWith("_BED")) {
multiBlocks.put(material, bedShape);
}
}
getServer().getPluginManager().registerEvents(this, this);
}
@Override
public void onDisable() {
multiBlocks.clear();
}
public void addMultiBlockMat(@NotNull Material material, @NotNull MultiBlockShape shape) {
multiBlocks.put(material, shape);
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGH)
private void preBlockMultiPlace(@NotNull PlayerInteractEvent event) {
if (event.getAction() != Action.RIGHT_CLICK_BLOCK
|| event.getClickedBlock() == null
|| event.getItem() == null) {
// Non-placement.
return;
}
MultiBlockShape shape = multiBlocks.get(event.getItem().getType());
if (shape == null) {
// Non-multiblock placement.
return;
}
// Fast-placing client sends placement packets on ghost blocks. Since the ghost block is air,
// the block is placed inside it instead.
Block placeLocation;
if (event.getClickedBlock().getType() == Material.AIR) {
placeLocation = event.getClickedBlock();
} else {
placeLocation = event.getClickedBlock().getRelative(event.getBlockFace());
}
// Server only erroneously updates blocks adjacent to the origin block, i.e. the bed foot.
boolean adjacentCrop = false;
for (BlockFace adjacentFace : adjacents) {
if (placeLocation.getRelative(adjacentFace).getBlockData() instanceof Ageable) {
// There's an adjacent crop, this is a potential dupe.
adjacentCrop = true;
break;
}
}
if (!adjacentCrop) {
// No adjacent crops, no dupe risk.
return;
}
BlockMultiPlaceEvent fakeEvent = new BlockMultiPlaceEvent(
shape.getBlocks(placeLocation, event.getPlayer()),
placeLocation,
event.getItem(),
event.getPlayer(),
true);
getServer().getPluginManager().callEvent(fakeEvent);
if (fakeEvent.isCancelled()) {
// Protection plugin cancelled our pre-fire event.
event.setCancelled(true);
}
}
public interface MultiBlockShape {
@NotNull List<BlockState> getBlocks(@NotNull Block start, @NotNull Player player);
}
}
main: com.github.jikoo.blockbedcropdupe.BlockBedCropDupe
name: BlockBedCropDupe
version: ${project.version}
api-version: 1.13
author: Jikoo
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment