Skip to content

Instantly share code, notes, and snippets.

@Cryptite
Last active November 12, 2017 14:35
Show Gist options
  • Save Cryptite/5fe65cd5cb67991f1e0afead35071d5b to your computer and use it in GitHub Desktop.
Save Cryptite/5fe65cd5cb67991f1e0afead35071d5b to your computer and use it in GitHub Desktop.
Hopper Burnout - Proof of Concept for turning off Hoppers when they try to move too many items
import org.bukkit.Location;
import org.bukkit.Particle;
import org.bukkit.Sound;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
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.BlockBreakEvent;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryMoveItemEvent;
import org.bukkit.event.inventory.InventoryPickupItemEvent;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.event.world.ChunkUnloadEvent;
import org.bukkit.inventory.Inventory;
import java.util.*;
import static org.bukkit.ChatColor.*;
public class HopperBurnout implements Listener {
private Map<StringChunk, Map<StringLocation, Hopper>> watchMap = new HashMap<>();
private final Set<InventoryType> inventoryTypes = new HashSet<>(Arrays.asList(InventoryType.BREWING,
InventoryType.CHEST,
InventoryType.DISPENSER,
InventoryType.DROPPER,
InventoryType.FURNACE,
InventoryType.HOPPER));
class Hopper {
private final StringLocation location;
private long firstItem;
private boolean burnedOut;
int itemsMoved;
Hopper(StringLocation location) {
this.location = location;
firstItem = System.currentTimeMillis();
increment();
}
boolean increment() {
itemsMoved++;
//If it's been a 1s since our first item transferred
if (System.currentTimeMillis() - firstItem > 1000) {
//Hopper Burnout tends to be over 50 items for a single chest, 100 for a double-chest.
if (itemsMoved > 50) {
System.out.println(YELLOW + "[Hoppers] " + RED + "BURNOUT" + YELLOW +
" with " + GREEN + itemsMoved + " ips" + YELLOW + " at " + location.getLocation().toString());
setBurnedOut(true);
playBurnout();
} else {
setBurnedOut(false);
}
}
return burnedOut;
}
private void playBurnout() {
Location blockLoc = location.getLocation().add(.5f, 0, .5f);
for (Player p : location.getWorld().getPlayers()) {
p.spawnParticle(Particle.SMOKE_NORMAL, blockLoc, 10, 0, 0, 0, .005f);
}
blockLoc.getWorld().playSound(blockLoc, Sound.BLOCK_FIRE_EXTINGUISH, .5f, 1);
}
void setBurnedOut(boolean burnedOut) {
itemsMoved = 0;
firstItem = System.currentTimeMillis();
this.burnedOut = burnedOut;
}
}
public HopperBurnout() {
}
@EventHandler(priority = EventPriority.MONITOR)
public void onHopperMoveEvent(InventoryMoveItemEvent e) {
if (e.isCancelled()) return;
Inventory destination = e.getDestination();
if (destination.getType().equals(InventoryType.HOPPER)) {
Block sourceBlock = destination.getLocation().getBlock();
if (incrementBlock(sourceBlock.getLocation())) {
e.setCancelled(true);
}
}
}
@EventHandler(priority = EventPriority.MONITOR)
public void onHopperPickUpEvent(InventoryPickupItemEvent e) {
if (e.getInventory().getHolder() instanceof BlockState
&& inventoryTypes.contains(e.getInventory().getType()))
incrementBlock(((BlockState) e.getInventory().getHolder()).getLocation());
}
@EventHandler(priority = EventPriority.MONITOR)
public void onChunkUnload(ChunkUnloadEvent e) {
StringChunk chunk = new StringChunk(e.getChunk());
if (watchMap.remove(chunk) != null) {
System.out.println(YELLOW + "[Hoppers] Chunks: " + watchMap.size());
}
}
@EventHandler(priority = EventPriority.MONITOR)
public void onBlockBreak(BlockBreakEvent e) {
StringChunk chunk = new StringChunk(e.getBlock().getChunk());
if (watchMap.containsKey(chunk)) {
//Breaking a hopper block should remove the hopper from the watch map.
watchMap.get(chunk).remove(new StringLocation(e.getBlock()));
}
}
@EventHandler
public void onInventoryClicked(InventoryClickEvent e) {
if (!(e.getInventory().getHolder() instanceof BlockState)) return;
Location l = ((BlockState) e.getInventory().getHolder()).getLocation();
Hopper hopper = getHopper(l);
if (hopper != null) {
//Doing any modifications to the hopper's inventory should reset it so that it
//can see if it's still burning out
hopper.setBurnedOut(false);
}
}
private Hopper getHopper(Location l) {
StringChunk chunk = new StringChunk(l.getChunk());
if (watchMap.containsKey(chunk)) {
return watchMap.get(chunk).getOrDefault(new StringLocation(l), null);
}
return null;
}
private boolean incrementBlock(Location l) {
StringChunk chunk = new StringChunk(l.getChunk());
StringLocation loc = new StringLocation(l);
Hopper hopper;
Map<StringLocation, Hopper> map;
if (watchMap.containsKey(chunk)) {
map = watchMap.get(chunk);
hopper = map.getOrDefault(loc, new Hopper(loc));
map.put(loc, hopper);
} else {
map = new HashMap<>();
hopper = new Hopper(loc);
System.out.println(YELLOW + "[Hoppers] Registered new chunk: " + chunk.toString());
}
boolean burntOut = hopper.increment();
map.put(loc, hopper);
watchMap.put(chunk, map);
return burntOut;
}
}
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import java.util.Objects;
public class StringChunk {
public String world;
public int x, z;
public StringChunk(Chunk c) {
this.world = c.getWorld().getName();
this.x = c.getX();
this.z = c.getZ();
}
public StringChunk(String args) {
String[] argsSplit = args.split(",");
this.world = argsSplit[0];
this.x = Integer.parseInt(argsSplit[1]);
this.z = Integer.parseInt(argsSplit[2]);
}
public Chunk getChunk() {
return Bukkit.getWorld(world).getChunkAt(x, z);
}
public boolean isLoaded() {
return Bukkit.getWorld(world).isChunkLoaded(x, z);
}
@Override
public int hashCode() {
return Objects.hash(world, x, z);
}
@Override
public boolean equals(Object other) {
if (other instanceof Chunk) {
Chunk o = (Chunk) other;
return x == o.getX() && z == o.getZ() && world.equals(o.getWorld().getName());
} else if (other instanceof StringChunk) {
StringChunk o = (StringChunk) other;
return x == o.x && z == o.z && world.equals(o.world);
}
return false;
}
@Override
public String toString() {
return world + "," + x + "," + z;
}
}
public class StringLocation {
public String world;
public double x, y, z;
private float yaw;
private float pitch;
public StringLocation(Block b) {
this.world = b.getWorld().getName();
this.x = b.getX();
this.y = b.getY();
this.z = b.getZ();
}
public StringLocation(Location l) {
this.world = l.getWorld().getName();
this.x = l.getBlockX();
this.y = l.getBlockY();
this.z = l.getBlockZ();
this.yaw = l.getYaw();
this.pitch = l.getPitch();
}
public StringLocation(String string) {
if (string == null) return;
String[] elems = string.split(",");
world = elems[0];
x = Double.parseDouble(elems[1]);
y = Double.parseDouble(elems[2]);
z = Double.parseDouble(elems[3]);
yaw = elems.length > 4 ? Float.parseFloat(elems[4]) : 0f;
pitch = elems.length > 5 ? Float.parseFloat(elems[5]) : 0f;
}
public Chunk getChunk() {
return Bukkit.getWorld(world).getChunkAt(getLocation());
}
public Location getLocation() {
if (yaw != 0 || pitch != 0) {
return new Location(getWorld(), x, y, z, yaw, pitch);
}
return new Location(getWorld(), x, y, z);
}
public void add(int x, int y, int z) {
this.x += x;
this.y += y;
this.z += z;
}
public void subtract(int x, int y, int z) {
this.x -= x;
this.y -= y;
this.z -= z;
}
public boolean equals(Location l) {
World world = l.getWorld();
return this.world != null
&& world != null
&& this.world.equals(world.getName())
&& Double.doubleToLongBits(this.x) == Double.doubleToLongBits(l.getX())
&& Double.doubleToLongBits(this.y) == Double.doubleToLongBits(l.getY())
&& Double.doubleToLongBits(this.z) == Double.doubleToLongBits(l.getZ())
&& Float.floatToIntBits(this.pitch) == Float.floatToIntBits(l.getPitch())
&& Float.floatToIntBits(this.yaw) == Float.floatToIntBits(l.getYaw());
}
public boolean looseEquals(Location l) {
return getBlockX() == l.getBlockX()
&& getBlockY() == l.getBlockY()
&& getBlockZ() == l.getBlockZ();
}
public Location getCenter() {
return getBlock().getLocation().add(.5, .5, .5);
}
public Block getBlock() {
return world != null
? Bukkit.getWorld(world).getBlockAt((int) x, (int) y, (int) z)
: Bukkit.getWorld("world").getBlockAt(0, 0, 0);
}
Material getType() {
return getBlock().getType();
}
void setType(Material m) {
getBlock().setType(m);
}
public int getBlockX() {
return (int) x;
}
public int getBlockY() {
return (int) y;
}
public int getBlockZ() {
return (int) z;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public void setZ(int z) {
this.z = z;
}
public void setPitch(float pitch) {
this.pitch = pitch;
}
public void setYaw(float yaw) {
this.yaw = yaw;
}
public float getPitch() {
return pitch;
}
public float getYaw() {
return yaw;
}
public double distance(Location location) {
return getLocation().distance(location);
}
public double distance(StringLocation loc) {
return Math.sqrt(square(this.x - loc.x) + square(this.y - loc.y) + square(this.z - loc.z));
}
public org.bukkit.World getWorld() {
return Bukkit.getWorld(world);
}
public Vector toVector() {
return new Vector(getBlockX(), getBlockY(), getBlockZ());
}
public void setLocation(Location l) {
world = l.getWorld().getName();
x = (int) l.getX();
y = (int) l.getY();
z = (int) l.getZ();
yaw = l.getYaw();
pitch = l.getPitch();
}
@Override
public int hashCode() {
return Objects.hash(world, x, y, z, yaw, pitch);
}
@Override
public boolean equals(Object other) {
if (!(other instanceof StringLocation)) {
return false;
}
StringLocation o = (StringLocation) other;
return x == o.x && y == o.y && z == o.z &&
world.equals(o.world);
}
@Override
public String toString() {
return LocationUtil.coordsToString(this);
}
public String toStringBasic() {
return LocationUtil.coordsToStringBasic(getLocation());
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment