EpicHeads/main/java/com/songoda/epicheads/handlers/HeadNamer.java

214 lines
6.3 KiB
Java

package com.songoda.epicheads.handlers;
import com.mojang.authlib.GameProfile;
import com.songoda.epicheads.EpicHeads;
import com.songoda.epicheads.cache.CacheHead;
import com.songoda.epicheads.util.Methods;
import com.songoda.epicheads.volatilecode.ItemNBT;
import com.songoda.epicheads.volatilecode.Items;
import com.songoda.epicheads.volatilecode.TextureGetter;
import com.songoda.epicheads.volatilecode.reflection.nms.BlockPosition;
import com.songoda.epicheads.volatilecode.reflection.nms.TileEntitySkull;
import com.songoda.epicheads.volatilecode.reflection.nms.World;
import net.sothatsit.blockstore.BlockStoreApi;
import org.bukkit.*;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.block.Skull;
import org.bukkit.entity.Player;
import org.bukkit.event.EventException;
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.block.BlockPlaceEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.inventory.meta.SkullMeta;
import org.bukkit.plugin.RegisteredListener;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class HeadNamer implements Listener {
private final EpicHeads instance;
public HeadNamer(EpicHeads instance) {
this.instance = instance;
}
public void registerEvents() {
Bukkit.getPluginManager().registerEvents(this, EpicHeads.getInstance());
}
private boolean shouldUseBlockStore() {
return instance.getMainConfig().shouldUseBlockStore() && instance.isBlockStoreAvailable();
}
@SuppressWarnings("deprecation")
private boolean isHeadsHead(ItemStack item) {
if (!Items.isSkull(item))
return false;
SkullMeta meta = (SkullMeta) item.getItemMeta();
// This needs to be kept too since it will not work on 1.8 if changed.
return meta.hasOwner() && meta.getOwner().equals("SpigotHeadPlugin");
}
@SuppressWarnings("deprecation")
private boolean isHeadsHead(Block block) {
BlockState state = block.getState();
if (!(state instanceof Skull))
return false;
Skull skull = (Skull) state;
// This needs to be kept too since it will not work on 1.8 if changed.
return skull.getOwner() != null && skull.getOwner().equals("SpigotHeadPlugin");
}
private GameProfile getGameProfile(Block block) {
World world = new World(block.getWorld());
BlockPosition pos = new BlockPosition(block.getX(), block.getY(), block.getZ());
TileEntitySkull tile = world.getTileEntity(pos).asSkullEntity();
return tile.getGameProfile();
}
private String findHeadName(Block block) {
if (instance.getMainConfig().shouldUseCacheNames()) {
GameProfile profile = getGameProfile(block);
String texture = TextureGetter.findTexture(profile);
CacheHead head = instance.getCache().findHeadByTexture(texture);
if (head != null)
return ChatColor.GRAY + head.getName();
}
return ChatColor.GRAY + instance.getMainConfig().getDefaultHeadName();
}
@EventHandler(priority = EventPriority.MONITOR)
public void onBlockPlace(BlockPlaceEvent e) {
if (!instance.getMainConfig().isHeadNamesEnabled() || !shouldUseBlockStore() || !isHeadsHead(e.getItemInHand()))
return;
ItemMeta meta = e.getItemInHand().getItemMeta();
if (!meta.hasDisplayName())
return;
BlockStoreApi.setBlockMeta(e.getBlock(), instance, "name", meta.getDisplayName());
}
@EventHandler(priority = EventPriority.LOWEST)
public void onBlockBreak(BlockBreakEvent e) {
if (!instance.getMainConfig().isHeadNamesEnabled())
return;
Block block = e.getBlock();
if (e.getPlayer().getGameMode() == GameMode.CREATIVE || !isHeadsHead(block))
return;
// Stop the head item being dropped by the server
e.setCancelled(true);
if (shouldUseBlockStore()) {
BlockStoreApi.retrieveBlockMeta(instance, block, instance, "name", metaValue -> {
String newName;
if (metaValue instanceof String) {
newName = (String) metaValue;
} else {
newName = findHeadName(block);
}
redropRenamedSkull(block, e.getPlayer(), newName);
});
} else {
redropRenamedSkull(block, e.getPlayer(), findHeadName(block));
}
}
private void redropRenamedSkull(Block block, Player player, String newName) {
BlockBreakEvent event = new BlockBreakEvent(block, player);
List<RegisteredListener> listenersToCall = new ArrayList<>();
for (RegisteredListener listener : BlockBreakEvent.getHandlerList().getRegisteredListeners()) {
if (!listener.getPlugin().isEnabled() || listener.getListener() instanceof HeadNamer)
continue;
listenersToCall.add(listener);
}
CountdownRunnable eventResultHandler = new CountdownRunnable(listenersToCall.size(), () -> {
if (event.isCancelled())
return;
GameProfile profile = getGameProfile(block);
ItemStack drop = ItemNBT.createHead(profile, newName);
Location dropLocation = block.getLocation().add(0.5, 0.5, 0.5);
block.setType(Material.AIR);
block.getWorld().dropItemNaturally(dropLocation, drop);
});
for (RegisteredListener listener : listenersToCall) {
new BlockBreakEventCaller(listener, event, eventResultHandler).scheduleTask();
}
}
private static class BlockBreakEventCaller implements Runnable {
private final RegisteredListener listener;
private final BlockBreakEvent event;
private final CountdownRunnable countdown;
BlockBreakEventCaller(RegisteredListener listener, BlockBreakEvent event, CountdownRunnable countdown) {
this.listener = listener;
this.event = event;
this.countdown = countdown;
}
void scheduleTask() {
Bukkit.getScheduler().scheduleSyncDelayedTask(listener.getPlugin(), this);
}
@Override
public void run() {
try {
listener.callEvent(event);
} catch (EventException exception) {
Methods.formatText("There was an exception calling BlockBreakEvent for " + listener.getPlugin().getName());
exception.printStackTrace();
} finally {
countdown.countdown();
}
}
}
private static class CountdownRunnable {
private final AtomicInteger countdown;
private final Runnable runnable;
CountdownRunnable(int count, Runnable runnable) {
this.countdown = new AtomicInteger(count);
this.runnable = runnable;
}
void countdown() {
if (countdown.decrementAndGet() != 0)
return;
EpicHeads.getInstance().sync(runnable);
}
}
}