-
-
Save Zyrenth/ac379988c4dd36106987a4b5849ab536 to your computer and use it in GitHub Desktop.
Advanced NPC Util [1.19] (Packets)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import com.mojang.authlib.GameProfile; | |
import com.mojang.authlib.properties.Property; | |
import com.mojang.datafixers.util.Pair; | |
import io.netty.buffer.Unpooled; | |
import io.netty.channel.ChannelHandlerContext; | |
import io.netty.channel.ChannelInboundHandlerAdapter; | |
import io.netty.channel.ChannelPipeline; | |
import net.minecraft.EnumChatFormat; | |
import net.minecraft.core.BlockPosition; | |
import net.minecraft.nbt.NBTTagCompound; | |
import net.minecraft.network.PacketDataSerializer; | |
import net.minecraft.network.chat.IChatBaseComponent; | |
import net.minecraft.network.protocol.Packet; | |
import net.minecraft.network.protocol.game.*; | |
import net.minecraft.network.syncher.DataWatcher; | |
import net.minecraft.network.syncher.DataWatcherObject; | |
import net.minecraft.network.syncher.DataWatcherRegistry; | |
import net.minecraft.network.syncher.DataWatcherSerializer; | |
import net.minecraft.world.EnumHand; | |
import net.minecraft.world.entity.Entity; | |
import net.minecraft.world.entity.EntityPose; | |
import net.minecraft.world.entity.EntityTypes; | |
import net.minecraft.world.entity.EnumItemSlot; | |
import net.minecraft.world.entity.animal.EntityParrot; | |
import net.minecraft.world.item.ItemStack; | |
import net.minecraft.world.level.EnumGamemode; | |
import net.minecraft.world.scores.Scoreboard; | |
import net.minecraft.world.scores.ScoreboardTeam; | |
import net.minecraft.world.scores.ScoreboardTeamBase; | |
import org.bukkit.Bukkit; | |
import org.bukkit.Location; | |
import org.bukkit.World; | |
import org.bukkit.craftbukkit.v1_19_R1.CraftServer; | |
import org.bukkit.craftbukkit.v1_19_R1.CraftWorld; | |
import org.bukkit.craftbukkit.v1_19_R1.entity.CraftParrot; | |
import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer; | |
import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack; | |
import org.bukkit.craftbukkit.v1_19_R1.util.CraftChatMessage; | |
import org.bukkit.entity.Parrot; | |
import org.bukkit.entity.Player; | |
import org.bukkit.event.EventHandler; | |
import org.bukkit.event.EventPriority; | |
import org.bukkit.event.Listener; | |
import org.bukkit.event.player.PlayerJoinEvent; | |
import org.bukkit.plugin.Plugin; | |
import org.json.simple.JSONArray; | |
import org.json.simple.JSONObject; | |
import org.json.simple.parser.JSONParser; | |
import java.lang.reflect.Field; | |
import java.lang.reflect.Method; | |
import java.net.HttpURLConnection; | |
import java.net.URI; | |
import java.net.http.HttpClient; | |
import java.net.http.HttpRequest; | |
import java.net.http.HttpResponse; | |
import java.time.Duration; | |
import java.util.*; | |
import java.util.concurrent.atomic.AtomicInteger; | |
import java.util.function.Consumer; | |
public class NPC { | |
private static AtomicInteger atomicInteger; | |
private final int entityID; | |
private final Location location; | |
private GameProfile profile; | |
private MetaData metadata = new MetaData(); | |
static { | |
try { | |
Field field = Entity.class.getDeclaredField("c"); | |
field.setAccessible(true); | |
atomicInteger = (AtomicInteger) field.get(null); | |
} catch (NoSuchFieldException | IllegalAccessException e) { | |
e.printStackTrace(); | |
} | |
} | |
public NPC(UUID uuid, Location location, String displayName) { | |
this.entityID = atomicInteger.incrementAndGet(); | |
this.profile = new GameProfile(uuid, displayName); | |
this.location = location; | |
} | |
public NPC(Location location, String displayName) { | |
this(UUID.randomUUID(), location, displayName); | |
} | |
public void spawnNPC(Collection<? extends Player> receivers) { | |
receivers.forEach(this::spawnNPC); | |
} | |
public void spawnNPC(Player receiver) { | |
this.addToTabList(receiver); | |
this.sendPacket(receiver, this.getEntitySpawnPacket()); | |
this.updateMetadata(receiver); | |
} | |
public void destroyNPC(Collection<? extends Player> receivers) { | |
receivers.forEach(this::destroyNPC); | |
} | |
public void destroyNPC(Player receiver) { | |
this.sendPacket(receiver, this.getPlayerInfoPacket(PlayerInfo.REMOVE_PLAYER)); | |
this.sendPacket(receiver, this.getEntityDestroyPacket()); | |
} | |
public void reloadNPC(Collection<? extends Player> receivers) { | |
receivers.forEach(this::reloadNPC); | |
} | |
public void reloadNPC(Player receiver) { | |
this.destroyNPC(receiver); | |
this.spawnNPC(receiver); | |
} | |
public void teleportNPC(Collection<? extends Player> receivers, Location location, boolean onGround) { | |
receivers.forEach(receiver -> this.teleportNPC(receiver, location, onGround)); | |
} | |
public void teleportNPC(Player receiver, Location location, boolean onGround) { | |
this.location.setX(location.getX()); | |
this.location.setY(location.getY()); | |
this.location.setZ(location.getZ()); | |
this.location.setPitch(location.getPitch()); | |
this.location.setYaw(location.getYaw()); | |
this.rotateHead(receiver, location.getPitch(), location.getYaw(), true); | |
this.sendPacket(receiver, this.getEntityTeleportPacket(onGround)); | |
} | |
public void updateMetadata(Collection<? extends Player> receivers) { | |
receivers.forEach(this::updateMetadata); | |
} | |
public void updateMetadata(Player receiver) { | |
this.sendPacket(receiver, this.getEntityMetadataPacket()); | |
} | |
public void updateGameMode(Collection<? extends Player> receivers) { | |
receivers.forEach(this::updateGameMode); | |
} | |
public void updateGameMode(Player receiver) { | |
this.sendPacket(receiver, this.getPlayerInfoPacket(PlayerInfo.UPDATE_GAME_MODE)); | |
} | |
public void setPing(Collection<? extends Player> receivers, Ping ping) { | |
receivers.forEach(receiver -> this.setPing(receiver, ping)); | |
} | |
public void setPing(Player receiver, Ping ping) { | |
this.sendPacket(receiver, this.getUpdatePlayerInfoPacket(PlayerInfo.UPDATE_LATENCY, ping)); | |
} | |
public void setGameMode(Collection<? extends Player> receivers, GameMode gameMode) { | |
receivers.forEach(receiver -> this.setGameMode(receiver, gameMode)); | |
} | |
public void setGameMode(Player receiver, GameMode gameMode) { | |
this.sendPacket(receiver, this.getUpdatePlayerInfoPacket(PlayerInfo.UPDATE_GAME_MODE, gameMode)); | |
} | |
public void setTabListName(Collection<? extends Player> receivers, String displayName) { | |
receivers.forEach(receiver -> this.setTabListName(receiver, displayName)); | |
} | |
public void setTabListName(Player receiver, String displayName) { | |
this.sendPacket(receiver, this.getUpdatePlayerInfoPacket(PlayerInfo.UPDATE_DISPLAY_NAME, displayName)); | |
} | |
public void removeFromTabList(Collection<? extends Player> receivers) { | |
receivers.forEach(this::removeFromTabList); | |
} | |
public void removeFromTabList(Player receiver) { | |
this.sendPacket(receiver, this.getPlayerInfoPacket(PlayerInfo.REMOVE_PLAYER)); | |
} | |
public void addToTabList(Collection<? extends Player> receivers) { | |
receivers.forEach(this::addToTabList); | |
} | |
public void addToTabList(Player receiver) { | |
this.sendPacket(receiver, this.getPlayerInfoPacket(PlayerInfo.ADD_PLAYER)); | |
} | |
public void playAnimation(Collection<? extends Player> receivers, Animation animation) { | |
receivers.forEach(receiver -> this.playAnimation(receiver, animation)); | |
} | |
public void playAnimation(Player receiver, Animation animation) { | |
this.sendPacket(receiver, this.getEntityAnimationPacket(animation)); | |
} | |
public void lookAtPlayer(Collection<? extends Player> receivers, Player target) { | |
receivers.forEach(receiver -> this.lookAtPlayer(receiver, target)); | |
} | |
public void lookAtPlayer(Player receiver, Player target) { | |
this.lookAtPoint(receiver, target.getEyeLocation()); | |
} | |
public void lookAtPoint(Collection<? extends Player> receivers, Location location) { | |
receivers.forEach(receiver -> this.lookAtPoint(receiver, location)); | |
} | |
public void lookAtPoint(Player receiver, Location location) { | |
Location eyeLocation = this.getEyeLocation(); | |
float yaw = (float) Math.toDegrees(Math.atan2(location.getZ() - eyeLocation.getZ(), location.getX() - eyeLocation.getX())) - 90; | |
yaw = (float) (yaw + Math.ceil(-yaw / 360) * 360); | |
float deltaXZ = (float) Math.sqrt(Math.pow(eyeLocation.getX() - location.getX(), 2) + Math.pow(eyeLocation.getZ() - location.getZ(), 2)); | |
float pitch = (float) Math.toDegrees(Math.atan2(deltaXZ, location.getY() - eyeLocation.getY())) - 90; | |
pitch = (float) (pitch + Math.ceil(-pitch / 360) * 360); | |
this.rotateHead(receiver, pitch, yaw, true); | |
} | |
public void rotateHead(Collection<? extends Player> receivers, float pitch, float yaw, boolean body) { | |
receivers.forEach(receiver -> this.rotateHead(receiver, pitch, yaw, body)); | |
} | |
public void rotateHead(Player receiver, float pitch, float yaw, boolean body) { | |
this.location.setPitch(pitch); | |
this.location.setYaw(yaw); | |
System.out.println(yaw); | |
if(body) this.sendPacket(receiver, this.getEntityLookPacket()); | |
this.sendPacket(receiver, this.getEntityHeadRotatePacket()); | |
} | |
public void setEquipment(Collection<? extends Player> receivers, ItemSlot slot, org.bukkit.inventory.ItemStack itemStack) { | |
receivers.forEach(receiver -> this.setEquipment(receiver, slot, itemStack)); | |
} | |
public void setEquipment(Player receiver, ItemSlot slot, org.bukkit.inventory.ItemStack itemStack) { | |
this.sendPacket(receiver, this.getEntityEquipmentPacket(slot.getNMSItemSlot(), CraftItemStack.asNMSCopy(itemStack))); | |
} | |
public void setPassenger(Collection<? extends Player> receivers, int... entityIDs) { | |
receivers.forEach(receiver -> this.setPassenger(receiver, entityIDs)); | |
} | |
public void setPassenger(Player receiver, int... entityIDs) { | |
this.sendPacket(receiver, getEntityAttachPacket(entityIDs)); | |
} | |
public void setLeash(Player receiver, org.bukkit.entity.Entity entity) { | |
this.sendPacket(receiver, getAttachEntityPacket(entity)); | |
} | |
public void setLeash(Collection<? extends Player> receivers, org.bukkit.entity.Entity entity) { | |
receivers.forEach(p->this.setLeash(p, entity)); | |
} | |
public void moveRelative(Player receiver, double relX, double relY, double relZ, boolean onGround) { | |
this.sendPacket(receiver, getEntityMovePacket(relX, relY, relZ, onGround)); | |
} | |
public void moveRelative(Collection<? extends Player> receivers, double relX, double relY, double relZ, boolean onGround) { | |
receivers.forEach(p->this.moveRelative(p, relX, relY, relZ, onGround)); | |
} | |
public BukkitCompletable<NPC> setSkinByUsername(Plugin plugin, String username) { | |
return BukkitCompletable.supplyASync(plugin, ()->{ | |
this.setSkin(NPC.SkinTextures.getByUsername(plugin, username).getSync()); | |
return NPC.this; | |
}); | |
} | |
public BukkitCompletable<NPC> setSkinByUUID(Plugin plugin, UUID uuid) { | |
return BukkitCompletable.supplyASync(plugin, ()->{ | |
this.setSkin(NPC.SkinTextures.getByUUID(plugin, uuid).getSync()); | |
return NPC.this; | |
}); | |
} | |
private void sendPacket(Player receiver, Packet<?> packet) { | |
((CraftPlayer) (receiver)).getHandle().b.a(packet); | |
} | |
public void setTeamFeatures(Player receiver, TeamFeatures teamFeatures) { | |
ScoreboardTeam team = new ScoreboardTeam(new Scoreboard(), this.profile.getName()); | |
team.a(teamFeatures.nameTagVisible() ? ScoreboardTeamBase.EnumNameTagVisibility.a : ScoreboardTeamBase.EnumNameTagVisibility.b); | |
team.a(teamFeatures.glowColor().getNMSColor()); | |
team.a(teamFeatures.collision() ? ScoreboardTeamBase.EnumTeamPush.a : ScoreboardTeamBase.EnumTeamPush.b); | |
PacketPlayOutScoreboardTeam createPacket = PacketPlayOutScoreboardTeam.a(team, true); | |
PacketPlayOutScoreboardTeam joinPacket = PacketPlayOutScoreboardTeam.a(team, this.profile.getName(), PacketPlayOutScoreboardTeam.a.a); | |
this.sendPacket(receiver, createPacket); | |
this.sendPacket(receiver, joinPacket); | |
} | |
public NBTTagCompound createParrot(Consumer<Parrot> callback, World world) { | |
EntityParrot entityParrot = new EntityParrot(EntityTypes.ao, ((CraftWorld) world).getHandle()); | |
CraftParrot parrot = new CraftParrot((CraftServer) Bukkit.getServer(), entityParrot); | |
callback.accept(parrot); | |
NBTTagCompound nbtTagCompound = new NBTTagCompound(); | |
entityParrot.d(nbtTagCompound); | |
return nbtTagCompound; | |
} | |
public void setParrotLeftShoulder(Consumer<Parrot> callback, World world) { | |
this.metadata.setLeftShoulder(this.createParrot(callback, world)); | |
} | |
public void setParrotRightShoulder(Consumer<Parrot> callback, World world) { | |
this.metadata.setRightShoulder(this.createParrot(callback, world)); | |
} | |
public int getEntityID() { | |
return entityID; | |
} | |
public GameProfile getProfile() { | |
return profile; | |
} | |
public MetaData getMetadata() { | |
return metadata; | |
} | |
public Location getLocation() { | |
return location; | |
} | |
public Location getEyeLocation() { | |
return this.location.clone().add(0, EntityTypes.bn.m().b * 0.85F, 0); | |
} | |
public MetaData getMetaData() { | |
return this.metadata; | |
} | |
public void setMetaData(MetaData metaData) { | |
this.metadata = metaData; | |
} | |
public void setSkin(SkinTextures skinTextures) { | |
this.profile.getProperties().put("textures", new Property("textures", skinTextures.getTexture(), skinTextures.getSignature())); | |
} | |
public void setDisplayName(String displayName) { | |
GameProfile swapProfile = new GameProfile(this.profile.getId(), displayName); | |
swapProfile.getProperties().putAll(this.profile.getProperties()); | |
this.profile = swapProfile; | |
} | |
public void registerListener(NPCListener listener) { | |
if (EventManager.isInitialized()) { | |
EventManager.INSTANCE.listenNPC(this, listener); | |
} | |
} | |
public void unregisterListener() { | |
if (EventManager.isInitialized()) { | |
EventManager.INSTANCE.unlistenNPC(this); | |
} | |
} | |
private PacketPlayOutMount getEntityAttachPacket(int[] entityIDs) { | |
return this.createDataSerializer(data -> { | |
data.d(this.entityID); | |
data.a(entityIDs); | |
return new PacketPlayOutMount(data); | |
}); | |
} | |
private PacketPlayOutEntity.PacketPlayOutEntityLook getEntityLookPacket() { | |
return new PacketPlayOutEntity.PacketPlayOutEntityLook(this.entityID, (byte) ((int) (this.location.getYaw() * 256.0F / 360.0F)), (byte) ((int) (this.location.getPitch() * 256.0F / 360.0F)), true); | |
} | |
private PacketPlayOutEntityHeadRotation getEntityHeadRotatePacket() { | |
return this.createDataSerializer(data -> { | |
data.d(this.entityID); | |
data.writeByte((byte) ((int) (this.location.getYaw() * 256.0F / 360.0F))); | |
return new PacketPlayOutEntityHeadRotation(data); | |
}); | |
} | |
private PacketPlayOutEntityTeleport getEntityTeleportPacket(boolean onGround) { | |
return this.createDataSerializer(data -> { | |
data.d(this.entityID); | |
data.writeDouble(this.location.getX()); | |
data.writeDouble(this.location.getY()); | |
data.writeDouble(this.location.getZ()); | |
data.writeByte((byte) ((int) (this.location.getYaw() * 256.0F / 360.0F))); | |
data.writeByte((byte) ((int) (this.location.getPitch() * 256.0F / 360.0F))); | |
data.writeBoolean(onGround); | |
return new PacketPlayOutEntityTeleport(data); | |
}); | |
} | |
private PacketPlayOutEntity.PacketPlayOutRelEntityMove getEntityMovePacket(double x, double y, double z, boolean onGround) { | |
return new PacketPlayOutEntity.PacketPlayOutRelEntityMove(this.entityID, (short)(x * 4096), (short)(y * 4096), (short)(z * 4096), onGround); | |
} | |
private PacketPlayOutAttachEntity getAttachEntityPacket(org.bukkit.entity.Entity entity) { | |
return createDataSerializer(data -> { | |
data.writeInt(entity.getEntityId()); | |
data.writeInt(this.entityID); | |
return new PacketPlayOutAttachEntity(data); | |
}); | |
} | |
private PacketPlayOutEntityEquipment getEntityEquipmentPacket(EnumItemSlot slot, ItemStack itemStack) { | |
return new PacketPlayOutEntityEquipment(this.entityID, Arrays.asList(new Pair<>(slot, itemStack))); | |
} | |
private PacketPlayOutAnimation getEntityAnimationPacket(Animation animation) { | |
return this.createDataSerializer((data) -> { | |
data.d(this.entityID); | |
data.writeByte((byte) animation.getType()); | |
return new PacketPlayOutAnimation(data); | |
}); | |
} | |
private PacketPlayOutEntityDestroy getEntityDestroyPacket() { | |
return new PacketPlayOutEntityDestroy(this.entityID); | |
} | |
private PacketPlayOutEntityMetadata getEntityMetadataPacket() { | |
return this.createDataSerializer((data) -> { | |
data.d(this.entityID); | |
DataWatcher.a(this.metadata.getList(), data); | |
return new PacketPlayOutEntityMetadata(data); | |
}); | |
} | |
private PacketPlayOutNamedEntitySpawn getEntitySpawnPacket() { | |
return this.createDataSerializer((data) -> { | |
data.d(this.entityID); | |
data.a(this.profile.getId()); | |
data.writeDouble(this.location.getX()); | |
data.writeDouble(this.location.getY()); | |
data.writeDouble(this.location.getZ()); | |
data.writeByte((byte) ((int) (this.location.getYaw() * 256.0F / 360.0F))); | |
data.writeByte((byte) ((int) (this.location.getPitch() * 256.0F / 360.0F))); | |
return new PacketPlayOutNamedEntitySpawn(data); | |
}); | |
} | |
private PacketPlayOutPlayerInfo getUpdatePlayerInfoPacket(PlayerInfo playerInfo, Object obj) { | |
return this.createDataSerializer((data) -> { | |
PacketPlayOutPlayerInfo.EnumPlayerInfoAction action = playerInfo.getNMSAction(); | |
PacketPlayOutPlayerInfo.PlayerInfoData playerInfoData; | |
net.minecraft.world.entity.player.ProfilePublicKey.a playerPublicKey = null; | |
if (playerInfo == PlayerInfo.UPDATE_LATENCY) { | |
playerInfoData = new PacketPlayOutPlayerInfo.PlayerInfoData(this.profile, ((Ping) obj).getMilliseconds(), null, null, playerPublicKey); | |
} else if (playerInfo == PlayerInfo.UPDATE_GAME_MODE) { | |
playerInfoData = new PacketPlayOutPlayerInfo.PlayerInfoData(this.profile, 0, ((GameMode) obj).getNMSGameMode(), null, playerPublicKey); | |
} else if (playerInfo == PlayerInfo.UPDATE_DISPLAY_NAME) { | |
playerInfoData = new PacketPlayOutPlayerInfo.PlayerInfoData(this.profile, 0, null, CraftChatMessage.fromString(((String) obj))[0], playerPublicKey); | |
} else { | |
throw new NullPointerException(); | |
} | |
List<PacketPlayOutPlayerInfo.PlayerInfoData> list = Arrays.asList(playerInfoData); | |
Method method = playerInfo.getNMSAction().getDeclaringClass().getDeclaredMethod("a", PacketDataSerializer.class, PacketPlayOutPlayerInfo.PlayerInfoData.class); | |
method.setAccessible(true); | |
data.a(playerInfo.getNMSAction()); | |
data.a(list, (PacketDataSerializer.b<PacketPlayOutPlayerInfo.PlayerInfoData>) (a, b) -> this.unsafe(() -> method.invoke(action, a, b))); | |
return new PacketPlayOutPlayerInfo(data); | |
}); | |
} | |
private PacketPlayOutPlayerInfo getPlayerInfoPacket(PlayerInfo playerInfo) { | |
return this.createDataSerializer((data) -> { | |
PacketPlayOutPlayerInfo.EnumPlayerInfoAction action = playerInfo.getNMSAction(); | |
net.minecraft.world.entity.player.ProfilePublicKey.a playerPublicKey = null; | |
PacketPlayOutPlayerInfo.PlayerInfoData playerInfoData = new PacketPlayOutPlayerInfo.PlayerInfoData(this.profile, Ping.FIVE_BARS.getMilliseconds(), GameMode.CREATIVE.getNMSGameMode(), CraftChatMessage.fromString(this.getProfile().getName())[0], playerPublicKey); | |
List<PacketPlayOutPlayerInfo.PlayerInfoData> list = Arrays.asList(playerInfoData); | |
data.a(playerInfo.getNMSAction()); | |
Method method = playerInfo.getNMSAction().getDeclaringClass().getDeclaredMethod("a", PacketDataSerializer.class, PacketPlayOutPlayerInfo.PlayerInfoData.class); | |
method.setAccessible(true); | |
data.a(list, (PacketDataSerializer.b<PacketPlayOutPlayerInfo.PlayerInfoData>) (a, b) -> this.unsafe(() -> method.invoke(action, a, b))); | |
return new PacketPlayOutPlayerInfo(data); | |
}); | |
} | |
public static void initEventHandler(Plugin plugin) { | |
EventManager.init(plugin); | |
} | |
public enum EntityState implements EnumUtil.Maskable<EntityState> { | |
DEFAULT(0x00), | |
ON_FIRE(0x01), | |
@Deprecated CROUCHING(0x02), | |
@Deprecated UNUSED(0x04), | |
SPRINTING(0x08), | |
SWIMMING(0x10), | |
INVISIBLE(0x20), | |
GLOWING(0x40), | |
FLYING(0x80); | |
private final int mask; | |
EntityState(int mask) { | |
this.mask = mask; | |
} | |
public int getMask() { | |
return mask; | |
} | |
} | |
public enum SkinStatus implements EnumUtil.Maskable<SkinStatus> { | |
CAPE_ENABLED(0x01), | |
JACKET_ENABLED(0x02), | |
LEFT_SLEEVE_ENABLED(0x04), | |
RIGHT_SLEEVE_ENABLED(0x08), | |
LEFT_PANTS_LEG_ENABLED(0x10), | |
RIGHT_PANTS_LEG_ENABLED(0x20), | |
HAT_ENABLED(0x40), | |
@Deprecated UNUSED(0x80); | |
private static final SkinStatus[] ALL = {CAPE_ENABLED, JACKET_ENABLED, LEFT_SLEEVE_ENABLED, RIGHT_SLEEVE_ENABLED, LEFT_PANTS_LEG_ENABLED, RIGHT_PANTS_LEG_ENABLED, HAT_ENABLED}; | |
private final int mask; | |
SkinStatus(int mask) { | |
this.mask = mask; | |
} | |
public int getMask() { | |
return mask; | |
} | |
} | |
public enum Pose implements EnumUtil.Identifiable<EntityPose> { | |
STANDING(EntityPose.a), | |
FALL_FLYING(EntityPose.b), | |
SLEEPING(EntityPose.c), | |
SWIMMING(EntityPose.d), | |
SPIN_ATTACK(EntityPose.e), | |
CROUCHING(EntityPose.f), | |
LONG_JUMPING(EntityPose.g), | |
DYING(EntityPose.h), | |
CROAKING(EntityPose.i), | |
ROARING(EntityPose.j), | |
SNIFFING(EntityPose.k), | |
EMERGING(EntityPose.l), | |
DIGGING(EntityPose.m); | |
private final EntityPose nmsPose; | |
Pose(EntityPose nmsPose) { | |
this.nmsPose = nmsPose; | |
} | |
public EntityPose getID() { | |
return nmsPose; | |
} | |
} | |
public enum HandStatus implements EnumUtil.Maskable<HandStatus> { | |
MAIN_HAND(0x00), | |
HAND_ACTIVE(0x01), | |
OFF_HAND(0x02), | |
RIPTIDE_SPIN_ATTACK(0x04); | |
private final int mask; | |
HandStatus(int mask) { | |
this.mask = mask; | |
} | |
public int getMask() { | |
return mask; | |
} | |
} | |
public enum Hand implements EnumUtil.BiIdentifiable<EnumHand, Integer> { | |
OFF_HAND(EnumHand.b, 1), | |
MAIN_HAND(EnumHand.a, 0); | |
private final EnumHand id; | |
private final int type; | |
Hand(EnumHand id, int type) { | |
this.id = id; | |
this.type = type; | |
} | |
public Integer getSecondID() { | |
return type; | |
} | |
public EnumHand getID() { | |
return id; | |
} | |
} | |
public enum InteractType implements EnumUtil.Identifiable<String> { | |
RIGHT_CLICK("INTERACT"), | |
LEFT_CLICK("ATTACK"), | |
RIGHT_CLICK_AT("INTERACT_AT"); | |
private final String id; | |
InteractType(String id) { | |
this.id = id; | |
} | |
public String getID() { | |
return id; | |
} | |
} | |
public enum PlayerInfo { | |
ADD_PLAYER(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.a), | |
UPDATE_GAME_MODE(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.b), | |
UPDATE_LATENCY(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.c), | |
UPDATE_DISPLAY_NAME(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.d), | |
REMOVE_PLAYER(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.e); | |
private final PacketPlayOutPlayerInfo.EnumPlayerInfoAction nmsAction; | |
PlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction nmsAction) { | |
this.nmsAction = nmsAction; | |
} | |
public PacketPlayOutPlayerInfo.EnumPlayerInfoAction getNMSAction() { | |
return nmsAction; | |
} | |
} | |
public enum Ping { | |
NO_CONNECTION(-1), | |
ONE_BAR(1000), | |
TWO_BARS(999), | |
THREE_BARS(599), | |
FOUR_BARS(299), | |
FIVE_BARS(149); | |
private final int milliseconds; | |
Ping(int milliseconds) { | |
this.milliseconds = milliseconds; | |
} | |
public int getMilliseconds() { | |
return milliseconds; | |
} | |
} | |
public enum ItemSlot { | |
MAIN_HAND(EnumItemSlot.a), | |
OFF_HAND(EnumItemSlot.b), | |
BOOTS(EnumItemSlot.c), | |
LEGGINGS(EnumItemSlot.d), | |
CHESTPLATE(EnumItemSlot.e), | |
HELMET(EnumItemSlot.f); | |
private final EnumItemSlot nmsItemSlot; | |
ItemSlot(EnumItemSlot nmsItemSlot) { | |
this.nmsItemSlot = nmsItemSlot; | |
} | |
public EnumItemSlot getNMSItemSlot() { | |
return nmsItemSlot; | |
} | |
} | |
public enum GlowColor { | |
BLACK(EnumChatFormat.a), | |
DARK_BLUE(EnumChatFormat.b), | |
DARK_GREEN(EnumChatFormat.c), | |
DARK_AQUA(EnumChatFormat.d), | |
DARK_RED(EnumChatFormat.e), | |
DARK_PURPLE(EnumChatFormat.f), | |
GOLD(EnumChatFormat.g), | |
GRAY(EnumChatFormat.h), | |
DARK_GRAY(EnumChatFormat.i), | |
BLUE(EnumChatFormat.j), | |
GREEN(EnumChatFormat.k), | |
AQUA(EnumChatFormat.l), | |
RED(EnumChatFormat.m), | |
LIGHT_PURPLE(EnumChatFormat.n), | |
YELLOW(EnumChatFormat.o), | |
WHITE(EnumChatFormat.p), | |
NONE(EnumChatFormat.v); | |
private final EnumChatFormat nmsColor; | |
GlowColor(EnumChatFormat nmsColor) { | |
this.nmsColor = nmsColor; | |
} | |
public EnumChatFormat getNMSColor() { | |
return nmsColor; | |
} | |
} | |
public enum GameMode { | |
SURVIVAL(EnumGamemode.a), | |
CREATIVE(EnumGamemode.b), | |
ADVENTURE(EnumGamemode.c), | |
SPECTATOR(EnumGamemode.d); | |
private final EnumGamemode nmsGameMode; | |
GameMode(EnumGamemode nmsGameMode) { | |
this.nmsGameMode = nmsGameMode; | |
} | |
public EnumGamemode getNMSGameMode() { | |
return nmsGameMode; | |
} | |
} | |
public enum Animation { | |
SWING_MAIN_HAND(0), | |
TAKE_DAMAGE(1), | |
LEAVE_BED(2), | |
SWING_OFFHAND(3), | |
CRITICAL_EFFECT(4), | |
MAGIC_CRITICAL_EFFECT(5); | |
private final int type; | |
Animation(int type) { | |
this.type = type; | |
} | |
public int getType() { | |
return type; | |
} | |
} | |
public class MetaData { | |
//Entity metadata | |
private final DataWatcher.Item<Byte> entityState = a(0, (byte) EnumUtil.createMask(EntityState.DEFAULT)); | |
private final DataWatcher.Item<Integer> airTicks = a(1, 300); | |
private final DataWatcher.Item<Optional<IChatBaseComponent>> customName = a(2, Optional.empty(), DataWatcherRegistry.f); | |
private final DataWatcher.Item<Boolean> customNameVisible = a(3, false); | |
private final DataWatcher.Item<Boolean> silent = a(4, false); | |
private final DataWatcher.Item<Boolean> noGravity = a(5, false); | |
private final DataWatcher.Item<EntityPose> pose = a(6, Pose.STANDING.getID()); | |
private final DataWatcher.Item<Integer> frozenTicks = a(7, 0); //shaking at tick 140 | |
//LivingEntity metadata | |
private final DataWatcher.Item<Byte> handStatus = a(8, (byte) EnumUtil.createMask(HandStatus.MAIN_HAND)); | |
private final DataWatcher.Item<Float> health = a(9, 1.0F); | |
private final DataWatcher.Item<Integer> potionEffectColor = a(10, 0); | |
private final DataWatcher.Item<Boolean> isPotionEffectAmbient = a(11, false); | |
private final DataWatcher.Item<Integer> arrowsInEntity = a(12, 0); | |
private final DataWatcher.Item<Integer> beeStingers = a(13, 0); | |
private final DataWatcher.Item<Optional<BlockPosition>> sleepingBedLocation = a(14, Optional.empty(), DataWatcherRegistry.m); | |
//Player metadata | |
private final DataWatcher.Item<Float> additionalHearts = a(15, 0.0F); | |
private final DataWatcher.Item<Integer> score = a(16, 0); | |
private final DataWatcher.Item<Byte> skinStatus = a(17, (byte) EnumUtil.createMask(SkinStatus.ALL)); | |
private final DataWatcher.Item<Byte> hand = a(18, (byte) ((int) Hand.MAIN_HAND.getSecondID())); | |
private final DataWatcher.Item<NBTTagCompound> leftShoulder = a(19, new NBTTagCompound()); | |
private final DataWatcher.Item<NBTTagCompound> rightShoulder = a(20, new NBTTagCompound()); | |
private final List<DataWatcher.Item<?>> list; | |
public MetaData() { | |
this.list = new ArrayList<>(Arrays.asList( | |
this.entityState, | |
this.airTicks, | |
this.customName, | |
this.customNameVisible, | |
this.silent, | |
this.noGravity, | |
this.pose, | |
this.frozenTicks, | |
this.handStatus, | |
this.health, | |
this.potionEffectColor, | |
this.isPotionEffectAmbient, | |
this.arrowsInEntity, | |
this.beeStingers, | |
this.sleepingBedLocation, | |
this.additionalHearts, | |
this.score, | |
this.skinStatus, | |
this.hand, | |
this.leftShoulder, | |
this.rightShoulder)); | |
} | |
public List<EntityState> getEntityState() { | |
return EnumUtil.fromMask(entityState.b(), EntityState.class); | |
} | |
public Integer getAirTicks() { | |
return airTicks.b(); | |
} | |
public Optional<IChatBaseComponent> getCustomName() { | |
return customName.b(); | |
} | |
public Boolean isCustomNameVisible() { | |
return customNameVisible.b(); | |
} | |
public Boolean isSilent() { | |
return silent.b(); | |
} | |
public Boolean hasNoGravity() { | |
return noGravity.b(); | |
} | |
public Pose getPose() { | |
return EnumUtil.getByID(this.pose.b(), Pose.class); | |
} | |
public Integer getFrozenTicks() { | |
return frozenTicks.b(); | |
} | |
public List<HandStatus> getHandStatus() { | |
return EnumUtil.fromMask(handStatus.b(), HandStatus.class); | |
} | |
public Float getHealth() { | |
return health.b(); | |
} | |
public Integer getPotionEffectColor() { | |
return potionEffectColor.b(); | |
} | |
public Boolean isPotionEffectAmbient() { | |
return isPotionEffectAmbient.b(); | |
} | |
public Integer getArrowsInEntity() { | |
return arrowsInEntity.b(); | |
} | |
public Integer getBeeStingers() { | |
return beeStingers.b(); | |
} | |
public Optional<BlockPosition> getSleepingBedLocation() { | |
return sleepingBedLocation.b(); | |
} | |
public Float getAdditionalHearts() { | |
return additionalHearts.b(); | |
} | |
public Integer getScore() { | |
return score.b(); | |
} | |
public List<SkinStatus> getSkinStatus() { | |
return EnumUtil.fromMask(skinStatus.b(), SkinStatus.class); | |
} | |
public Hand getHand() { | |
return EnumUtil.getBySecondID((int) hand.b(), Hand.class); | |
} | |
public NBTTagCompound getLeftShoulder() { | |
return leftShoulder.b(); | |
} | |
public NBTTagCompound getRightShoulder() { | |
return rightShoulder.b(); | |
} | |
public void setEntityState(EntityState... entityState) { | |
this.entityState.a((byte) EnumUtil.createMask(entityState)); | |
} | |
public void setAirTicks(Integer airTicks) { | |
this.airTicks.a(airTicks); | |
} | |
public void setCustomName(String customName) { | |
this.customName.a(Optional.ofNullable(IChatBaseComponent.a(customName))); | |
} | |
public void setCustomNameVisible(Boolean customNameVisible) { | |
this.customNameVisible.a(customNameVisible); | |
} | |
public void setSilent(Boolean silent) { | |
this.silent.a(silent); | |
} | |
public void setNoGravity(Boolean gravity) { | |
this.noGravity.a(gravity); | |
} | |
public void setPose(Pose pose) { | |
this.pose.a(pose.getID()); | |
} | |
public void setFrozenTicks(Integer frozenTicks) { | |
this.frozenTicks.a(frozenTicks); | |
} | |
public void setShaking(boolean shaking) { | |
this.setFrozenTicks(shaking ? 140 : 0); | |
} | |
public void setHandStatus(HandStatus handStatus) { | |
this.handStatus.a((byte) EnumUtil.createMask(handStatus)); | |
} | |
public void setHealth(Float health) { | |
this.health.a(health); | |
} | |
public void setPotionEffectColor(Integer potionEffectColor) { | |
this.potionEffectColor.a(potionEffectColor); | |
} | |
public void setIsPotionEffectAmbient(Boolean isPotionEffectAmbient) { | |
this.isPotionEffectAmbient.a(isPotionEffectAmbient); | |
} | |
public void setArrowsInEntity(Integer arrowsInEntity) { | |
this.arrowsInEntity.a(arrowsInEntity); | |
} | |
public void setBeeStingers(Integer beeStingers) { | |
this.beeStingers.a(beeStingers); | |
} | |
public void setSleepingBedLocation(BlockPosition sleepingBedLocation) { | |
this.sleepingBedLocation.a(Optional.ofNullable(sleepingBedLocation)); | |
} | |
public void setAdditionalHearts(Float additionalHearts) { | |
this.additionalHearts.a(additionalHearts); | |
} | |
public void setScore(Integer score) { | |
this.score.a(score); | |
} | |
public void setSkinStatus(SkinStatus... skinStatus) { | |
this.skinStatus.a((byte) EnumUtil.createMask(skinStatus)); | |
} | |
public void setHand(Hand hand) { | |
this.hand.a((byte) ((int) hand.getSecondID())); | |
} | |
public void setLeftShoulder(NBTTagCompound leftShoulder) { | |
this.leftShoulder.a(leftShoulder); | |
} | |
public void setRightShoulder(NBTTagCompound rightShoulder) { | |
this.rightShoulder.a(rightShoulder); | |
} | |
public List<DataWatcher.Item<?>> getList() { | |
return list; | |
} | |
private static <T> DataWatcher.Item<T> a(int index, T value) { | |
DataWatcherSerializer<?> serializer = null; | |
if (value instanceof Byte) { | |
serializer = DataWatcherRegistry.a; | |
} else if (value instanceof Float) { | |
serializer = DataWatcherRegistry.c; | |
} else if (value instanceof Integer) { | |
serializer = DataWatcherRegistry.b; | |
} else if (value instanceof String) { | |
serializer = DataWatcherRegistry.d; | |
} else if (value instanceof Boolean) { | |
serializer = DataWatcherRegistry.i; | |
} else if (value instanceof NBTTagCompound) { | |
serializer = DataWatcherRegistry.q; | |
} else if (value instanceof BlockPosition) { | |
serializer = DataWatcherRegistry.m; | |
} else if (value instanceof IChatBaseComponent) { | |
serializer = DataWatcherRegistry.e; | |
} else if (value instanceof EntityPose) { | |
serializer = DataWatcherRegistry.t; | |
} | |
return a(index, value, (DataWatcherSerializer<T>) serializer); | |
} | |
private static <T> DataWatcher.Item<T> a(int index, T value, DataWatcherSerializer<T> serializer) { | |
return new DataWatcher.Item<T>(new DataWatcherObject<T>(index, serializer), value); | |
} | |
} | |
public static class EnumUtil { | |
private interface Maskable<M> { | |
int getMask(); | |
} | |
private interface Identifiable<I> { | |
I getID(); | |
} | |
private interface BiIdentifiable<I, J> extends Identifiable<I> { | |
J getSecondID(); | |
} | |
@SafeVarargs | |
private static <M> int createMask(Maskable<M>... maskables) { | |
int mask = 0; | |
for (Maskable m : maskables) { | |
mask |= m.getMask(); | |
} | |
return mask; | |
} | |
private static <M extends Maskable<M>> List<M> fromMask(int mask, Class<M> enumClass) { | |
List<M> list = new ArrayList<M>(); | |
for (M maskable : enumClass.getEnumConstants()) { | |
if ((maskable.getMask() & mask) != 0) { | |
list.add(maskable); | |
} | |
} | |
return list; | |
} | |
private static <I, M extends Identifiable<I>> M getByID(I id, Class<M> enumClass) { | |
for (M identifiable : enumClass.getEnumConstants()) { | |
if (id == identifiable.getID()) { | |
return identifiable; | |
} | |
} | |
return null; | |
} | |
private static <I, J, M extends BiIdentifiable<I, J>> M getBySecondID(J id, Class<M> enumClass) { | |
for (M identifiable : enumClass.getEnumConstants()) { | |
if (id == identifiable.getID()) { | |
return identifiable; | |
} | |
} | |
return null; | |
} | |
} | |
public static class SkinTextures { | |
private static final String TEXTURE_URL = "https://sessionserver.mojang.com/session/minecraft/profile/%s?unsigned=false"; | |
private static final String UUID_URL = "https://api.mojang.com/profiles/minecraft"; | |
private final String texture; | |
private final String signature; | |
public SkinTextures(String textures, String signature) { | |
this.texture = textures; | |
this.signature = signature; | |
} | |
public String getTexture() { | |
return texture; | |
} | |
public String getSignature() { | |
return signature; | |
} | |
public static BukkitCompletable<SkinTextures> getByUsername(Plugin plugin, String username) { | |
return BukkitCompletable.supplyASync(plugin, ()->{ | |
JSONArray array = new JSONArray(); | |
array.add(username); | |
UUID uuid = null; | |
HttpRequest request = HttpRequest.newBuilder(new URI(UUID_URL)) | |
.setHeader("Content-Type", "application/json") | |
.POST(HttpRequest.BodyPublishers.ofString(array.toString())) | |
.timeout(Duration.ofSeconds(5)) | |
.build(); | |
HttpResponse<String> response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString()); | |
if (response.statusCode() == HttpURLConnection.HTTP_OK) { | |
JSONArray uuidArray = (JSONArray) new JSONParser().parse(response.body()); | |
if (uuidArray.size() > 0) { | |
String uuidStr = (String) ((JSONObject) uuidArray.get(0)).get("id"); | |
uuid = UUID.fromString(uuidStr.replaceAll("(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})", "$1-$2-$3-$4-$5")); | |
} | |
} | |
if (uuid == null) throw new NullPointerException("uuid is null"); | |
return getByUUID(plugin, uuid).getSync(); | |
}); | |
} | |
public static BukkitCompletable<SkinTextures> getByUUID(Plugin plugin, UUID uuid) { | |
return BukkitCompletable.supplyASync(plugin, ()->{ | |
SkinTextures result = null; | |
HttpRequest request = HttpRequest.newBuilder(new URI(String.format(TEXTURE_URL, uuid.toString().replace("-", "")))) | |
.timeout(Duration.ofSeconds(5)) | |
.GET() | |
.build(); | |
HttpResponse<String> response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString()); | |
if (response.statusCode() == HttpURLConnection.HTTP_OK) { | |
JSONArray properties = (JSONArray) ((JSONObject) new JSONParser().parse(response.body())).get("properties"); | |
for (int t = 0; t < properties.size(); t++) { | |
JSONObject obj = (JSONObject) properties.get(t); | |
if (obj.containsKey("name") && obj.get("name").equals("textures")) { | |
result = new SkinTextures((String) obj.get("value"), (String) obj.get("signature")); | |
} | |
} | |
} | |
return result; | |
}); | |
} | |
} | |
public static class BukkitCompletable<T> { | |
private final Plugin plugin; | |
private final UnsafeSupplier<T> runnable; | |
private Consumer<Throwable> errorHandler; | |
private Consumer<T> callbackHandler; | |
private Runnable emptyCallbackHandler; | |
private BukkitCompletable(Plugin plugin, UnsafeSupplier<T> runnable) { | |
this.plugin = plugin; | |
this.runnable = runnable; | |
} | |
public static <T> BukkitCompletable<T> supplyASync(Plugin plugin, UnsafeSupplier<T> runnable) { | |
return new BukkitCompletable<T>(plugin, runnable); | |
} | |
public BukkitCompletable<T> onFinish(Consumer<T> callbackHandler) { | |
this.callbackHandler = callbackHandler; | |
return this; | |
} | |
public BukkitCompletable<T> onEmptyFinish(Runnable emptyCallbackHandler) { | |
this.emptyCallbackHandler = emptyCallbackHandler; | |
return this; | |
} | |
public BukkitCompletable<T> onException(Consumer<Throwable> errorHandler) { | |
this.errorHandler = errorHandler; | |
return this; | |
} | |
public void getSafe() { | |
this.get(true); | |
} | |
public void getUnsafe() { | |
this.get(false); | |
} | |
public T getSync() throws Exception { | |
return runnable.get(); | |
} | |
private void get(boolean safe) { | |
async(plugin, ()->{ | |
try { | |
T t = this.runnable.get(); | |
if(!(this.callbackHandler == null && this.emptyCallbackHandler == null)) { | |
if(safe) { | |
sync(plugin, () -> { | |
if (callbackHandler != null) this.callbackHandler.accept(t); | |
if (emptyCallbackHandler != null) this.emptyCallbackHandler.run(); | |
}); | |
} else { | |
if(callbackHandler != null) this.callbackHandler.accept(t); | |
if(emptyCallbackHandler != null) this.emptyCallbackHandler.run(); | |
} | |
} | |
} catch (Throwable e) { | |
if(this.errorHandler != null) { | |
if(safe) sync(plugin, ()->this.errorHandler.accept(e)); | |
else this.errorHandler.accept(e); | |
} | |
} | |
}); | |
} | |
} | |
private static class EventManager implements Listener { | |
private static final String CHANNEL_NAME = "npc_manager"; | |
private static EventManager INSTANCE; | |
private final Plugin plugin; | |
private final Map<Integer, NPCListener> listenerMap = new HashMap<>(); | |
private EventManager(Plugin plugin) { | |
this.plugin = plugin; | |
Bukkit.getPluginManager().registerEvents(this, plugin); | |
Bukkit.getOnlinePlayers().forEach(this::unregisterPlayer); | |
Bukkit.getOnlinePlayers().forEach(this::registerPlayer); | |
} | |
private void listenNPC(NPC npc, NPCListener listener) { | |
if (listenerMap.containsKey(npc.getEntityID())) | |
throw new UnsupportedOperationException("cannot register same npc twice"); | |
this.listenerMap.put(npc.getEntityID(), listener); | |
} | |
private void unlistenNPC(NPC npc) { | |
if (!listenerMap.containsKey(npc.getEntityID())) throw new NullPointerException("listener does not exist"); | |
this.listenerMap.remove(npc.getEntityID()); | |
} | |
private void unregisterPlayer(Player player) { | |
ChannelPipeline pipeline = ((CraftPlayer) player).getHandle().b.b.m.pipeline(); | |
if(pipeline.names().contains(CHANNEL_NAME)) { | |
pipeline.remove(CHANNEL_NAME); | |
} | |
} | |
private void registerPlayer(Player player) { | |
ChannelPipeline pipeline = ((CraftPlayer) player).getHandle().b.b.m.pipeline(); | |
if(!pipeline.names().contains(CHANNEL_NAME)) { | |
pipeline.addBefore("packet_handler", CHANNEL_NAME, new PlayerInboundHandlerAdapter(player)); | |
} | |
} | |
@EventHandler(priority = EventPriority.LOWEST) | |
public void onPlayerJoinEvent(PlayerJoinEvent e) { | |
this.registerPlayer(e.getPlayer()); | |
} | |
public static boolean isInitialized() { | |
return INSTANCE != null; | |
} | |
public static void init(Plugin plugin) { | |
if(INSTANCE != null) INSTANCE.listenerMap.clear(); | |
INSTANCE = new EventManager(plugin); | |
} | |
private class PlayerInboundHandlerAdapter extends ChannelInboundHandlerAdapter { | |
private final Player player; | |
public PlayerInboundHandlerAdapter(Player player) { | |
this.player = player; | |
} | |
@Override | |
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { | |
super.channelRead(ctx, msg); | |
try { | |
if (msg instanceof PacketPlayInUseEntity packet) { | |
Field entityIdField = packet.getClass().getDeclaredField("a"); | |
entityIdField.setAccessible(true); | |
int entityId = (int) entityIdField.get(packet); | |
final NPCListener listener = listenerMap.getOrDefault(entityId, null); | |
if (listener != null) { | |
Field typeField = packet.getClass().getDeclaredField("b"); | |
typeField.setAccessible(true); | |
Object type = typeField.get(packet); | |
Method typeMethod = type.getClass().getDeclaredMethod("a"); | |
typeMethod.setAccessible(true); | |
InteractType interactType = EnumUtil.getByID(((Enum) typeMethod.invoke(type)).name(), InteractType.class); | |
Hand hand = Hand.MAIN_HAND; | |
if (interactType != InteractType.RIGHT_CLICK_AT) { | |
if (interactType == InteractType.RIGHT_CLICK) { | |
Field handField = type.getClass().getDeclaredField("a"); | |
handField.setAccessible(true); | |
hand = EnumUtil.getByID((EnumHand) handField.get(type), Hand.class); | |
} | |
boolean sneaking = packet.b(); | |
final NPCInteractEvent event = new NPCInteractEvent(this.player, interactType, hand, sneaking); | |
Bukkit.getScheduler().runTask(plugin, () -> listener.accept(event)); | |
} | |
} | |
} | |
} catch (Exception e) { | |
e.printStackTrace(); | |
} | |
; | |
} | |
} | |
} | |
public record TeamFeatures(boolean nameTagVisible, boolean collision, GlowColor glowColor) {} | |
public record NPCInteractEvent(Player player, InteractType interactType, Hand hand, boolean sneaking) { | |
} | |
public static void sync(Plugin plugin, Runnable runnable) { | |
Bukkit.getScheduler().runTask(plugin, runnable); | |
} | |
public static void async(Plugin plugin, Runnable runnable) { | |
Bukkit.getScheduler().runTaskAsynchronously(plugin, runnable); | |
} | |
public static void sleep(int milliseconds) { | |
try { | |
Thread.sleep(milliseconds); | |
} catch (InterruptedException e) {} | |
} | |
private static void unsafe(UnsafeRunnable run) { | |
try { | |
run.run(); | |
} catch (Exception e) { | |
e.printStackTrace(); | |
} | |
} | |
private static <T> T createDataSerializer(UnsafeFunction<PacketDataSerializer, T> callback) { | |
PacketDataSerializer data = new PacketDataSerializer(Unpooled.buffer()); | |
T result = null; | |
try { | |
result = callback.apply(data); | |
} catch (Exception e) { | |
e.printStackTrace(); | |
} finally { | |
data.release(); | |
} | |
return result; | |
} | |
@FunctionalInterface | |
private interface UnsafeSupplier<T> { | |
T get() throws Exception; | |
} | |
@FunctionalInterface | |
private interface UnsafeRunnable { | |
void run() throws Exception; | |
} | |
@FunctionalInterface | |
private interface UnsafeFunction<K, T> { | |
T apply(K k) throws Exception; | |
} | |
@FunctionalInterface | |
public interface NPCListener extends Consumer<NPCInteractEvent> { | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment