diff --git a/NMS/v1_14_R1/pom.xml b/NMS/v1_14_R1/pom.xml
index aca991c4..4efb3895 100644
--- a/NMS/v1_14_R1/pom.xml
+++ b/NMS/v1_14_R1/pom.xml
@@ -45,6 +45,6 @@
1.14-R0.1-SNAPSHOTprovided
-
+
\ No newline at end of file
diff --git a/NMS/v1_15_R1/pom.xml b/NMS/v1_15_R1/pom.xml
new file mode 100644
index 00000000..def5b586
--- /dev/null
+++ b/NMS/v1_15_R1/pom.xml
@@ -0,0 +1,49 @@
+
+
+ 4.0.0
+
+
+ com.gmail.filoghost.holographicdisplays
+ holographicdisplays-parent
+ 2.4.1-SNAPSHOT
+ ../../pom.xml
+
+
+ holographicdisplays-nms-v1_15_r1
+ HolographicDisplays NMS v1_15_R1
+
+
+
+ elmakers-repo
+ http://maven.elmakers.com/repository/
+
+
+
+
+
+ ${project.groupId}
+ holographicdisplays-nms-interfaces
+ 2.4.1-SNAPSHOT
+
+
+
+ ${project.groupId}
+ holographicdisplays-utils
+ 2.4.1-SNAPSHOT
+
+
+
+ ${project.groupId}
+ holographicdisplays-config
+ 2.4.1-SNAPSHOT
+
+
+ org.spigotmc
+ spigot
+ 1.15-R0.1-SNAPSHOT
+ provided
+
+
+
+
\ No newline at end of file
diff --git a/NMS/v1_15_R1/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_15_R1/CraftNMSArmorStand.java b/NMS/v1_15_R1/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_15_R1/CraftNMSArmorStand.java
new file mode 100644
index 00000000..d451719f
--- /dev/null
+++ b/NMS/v1_15_R1/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_15_R1/CraftNMSArmorStand.java
@@ -0,0 +1,96 @@
+/*
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.gmail.filoghost.holographicdisplays.nms.v1_15_R1;
+
+import java.util.Collection;
+
+import org.bukkit.EntityEffect;
+import org.bukkit.Location;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftArmorStand;
+import org.bukkit.entity.Entity;
+import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.potion.PotionEffect;
+import org.bukkit.util.EulerAngle;
+import org.bukkit.util.Vector;
+
+public class CraftNMSArmorStand extends CraftArmorStand {
+
+ public CraftNMSArmorStand(CraftServer server, EntityNMSArmorStand entity) {
+ super(server, entity);
+ }
+
+ // Disallow all the bukkit methods.
+
+ @Override
+ public void remove() {
+ // Cannot be removed, this is the most important to override.
+ }
+
+ // Methods from ArmorStand class
+ @Override public void setArms(boolean arms) { }
+ @Override public void setBasePlate(boolean basePlate) { }
+ @Override public void setBodyPose(EulerAngle pose) { }
+ @Override public void setBoots(ItemStack item) { }
+ @Override public void setChestplate(ItemStack item) { }
+ @Override public void setHeadPose(EulerAngle pose) { }
+ @Override public void setHelmet(ItemStack item) { }
+ @Override public void setItemInHand(ItemStack item) { }
+ @Override public void setLeftArmPose(EulerAngle pose) { }
+ @Override public void setLeftLegPose(EulerAngle pose) { }
+ @Override public void setLeggings(ItemStack item) { }
+ @Override public void setRightArmPose(EulerAngle pose) { }
+ @Override public void setRightLegPose(EulerAngle pose) { }
+ @Override public void setSmall(boolean small) { }
+ @Override public void setVisible(boolean visible) { }
+ @Override public void setMarker(boolean marker) { }
+
+ // Methods from LivingEntity class
+ @Override public boolean addPotionEffect(PotionEffect effect) { return false; }
+ @Override public boolean addPotionEffect(PotionEffect effect, boolean param) { return false; }
+ @Override public boolean addPotionEffects(Collection effects) { return false; }
+ @Override public void setRemoveWhenFarAway(boolean remove) { }
+ @Override public void setAI(boolean ai) { }
+ @Override public void setCanPickupItems(boolean pickup) { }
+ @Override public void setCollidable(boolean collidable) { }
+ @Override public void setGliding(boolean gliding) { }
+ @Override public boolean setLeashHolder(Entity holder) { return false; }
+ @Override public void setSwimming(boolean swimming) { }
+
+ // Methods from Entity class
+ @Override public void setVelocity(Vector vel) { }
+ @Override public boolean teleport(Location loc) { return false; }
+ @Override public boolean teleport(Entity entity) { return false; }
+ @Override public boolean teleport(Location loc, TeleportCause cause) { return false; }
+ @Override public boolean teleport(Entity entity, TeleportCause cause) { return false; }
+ @Override public void setFireTicks(int ticks) { }
+ @Override public boolean setPassenger(Entity entity) { return false; }
+ @Override public boolean eject() { return false; }
+ @Override public boolean leaveVehicle() { return false; }
+ @Override public void playEffect(EntityEffect effect) { }
+ @Override public void setCustomName(String name) { }
+ @Override public void setCustomNameVisible(boolean flag) { }
+ @Override public void setGlowing(boolean flag) { }
+ @Override public void setGravity(boolean gravity) { }
+ @Override public void setInvulnerable(boolean flag) { }
+ @Override public void setMomentum(Vector value) { }
+ @Override public void setSilent(boolean flag) { }
+ @Override public void setTicksLived(int value) { }
+ @Override public void setPersistent(boolean flag) { }
+ @Override public void setRotation(float yaw, float pitch) { }
+
+
+}
diff --git a/NMS/v1_15_R1/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_15_R1/CraftNMSItem.java b/NMS/v1_15_R1/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_15_R1/CraftNMSItem.java
new file mode 100644
index 00000000..5de14f68
--- /dev/null
+++ b/NMS/v1_15_R1/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_15_R1/CraftNMSItem.java
@@ -0,0 +1,65 @@
+/*
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.gmail.filoghost.holographicdisplays.nms.v1_15_R1;
+
+import org.bukkit.EntityEffect;
+import org.bukkit.Location;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftItem;
+import org.bukkit.entity.Entity;
+import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.util.Vector;
+
+public class CraftNMSItem extends CraftItem {
+
+ public CraftNMSItem(CraftServer server, EntityNMSItem entity) {
+ super(server, entity);
+ }
+
+ // Disallow all the bukkit methods.
+
+ @Override
+ public void remove() {
+ // Cannot be removed, this is the most important to override.
+ }
+
+ // Methods from Item class
+ @Override public void setItemStack(ItemStack stack) { }
+ @Override public void setPickupDelay(int delay) { }
+
+ // Methods from Entity class
+ @Override public void setVelocity(Vector vel) { }
+ @Override public boolean teleport(Location loc) { return false; }
+ @Override public boolean teleport(Entity entity) { return false; }
+ @Override public boolean teleport(Location loc, TeleportCause cause) { return false; }
+ @Override public boolean teleport(Entity entity, TeleportCause cause) { return false; }
+ @Override public void setFireTicks(int ticks) { }
+ @Override public boolean setPassenger(Entity entity) { return false; }
+ @Override public boolean eject() { return false; }
+ @Override public boolean leaveVehicle() { return false; }
+ @Override public void playEffect(EntityEffect effect) { }
+ @Override public void setCustomName(String name) { }
+ @Override public void setCustomNameVisible(boolean flag) { }
+ @Override public void setGlowing(boolean flag) { }
+ @Override public void setGravity(boolean gravity) { }
+ @Override public void setInvulnerable(boolean flag) { }
+ @Override public void setMomentum(Vector value) { }
+ @Override public void setSilent(boolean flag) { }
+ @Override public void setTicksLived(int value) { }
+ @Override public void setPersistent(boolean flag) { }
+ @Override public void setRotation(float yaw, float pitch) { }
+
+}
diff --git a/NMS/v1_15_R1/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_15_R1/CraftNMSSlime.java b/NMS/v1_15_R1/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_15_R1/CraftNMSSlime.java
new file mode 100644
index 00000000..ee911db2
--- /dev/null
+++ b/NMS/v1_15_R1/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_15_R1/CraftNMSSlime.java
@@ -0,0 +1,85 @@
+/*
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.gmail.filoghost.holographicdisplays.nms.v1_15_R1;
+
+import java.util.Collection;
+
+import org.bukkit.EntityEffect;
+import org.bukkit.Location;
+import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftSlime;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.LivingEntity;
+import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
+import org.bukkit.loot.LootTable;
+import org.bukkit.potion.PotionEffect;
+import org.bukkit.util.Vector;
+
+public class CraftNMSSlime extends CraftSlime {
+
+ public CraftNMSSlime(CraftServer server, EntityNMSSlime entity) {
+ super(server, entity);
+ }
+
+ // Disallow all the bukkit methods.
+
+ @Override
+ public void remove() {
+ // Cannot be removed, this is the most important to override.
+ }
+
+ // Methods from Slime class
+ @Override public void setSize(int size) { }
+ @Override public void setTarget(LivingEntity target) { }
+
+ // Methods from Mob class
+ @Override public void setLootTable(LootTable table) { }
+ @Override public void setSeed(long seed) { }
+
+ // Methods from LivingEntity class
+ @Override public boolean addPotionEffect(PotionEffect effect) { return false; }
+ @Override public boolean addPotionEffect(PotionEffect effect, boolean param) { return false; }
+ @Override public boolean addPotionEffects(Collection effects) { return false; }
+ @Override public void setRemoveWhenFarAway(boolean remove) { }
+ @Override public void setAI(boolean ai) { }
+ @Override public void setCanPickupItems(boolean pickup) { }
+ @Override public void setCollidable(boolean collidable) { }
+ @Override public void setGliding(boolean gliding) { }
+ @Override public boolean setLeashHolder(Entity holder) { return false; }
+ @Override public void setSwimming(boolean swimming) { }
+
+ // Methods from Entity class
+ @Override public void setVelocity(Vector vel) { }
+ @Override public boolean teleport(Location loc) { return false; }
+ @Override public boolean teleport(Entity entity) { return false; }
+ @Override public boolean teleport(Location loc, TeleportCause cause) { return false; }
+ @Override public boolean teleport(Entity entity, TeleportCause cause) { return false; }
+ @Override public void setFireTicks(int ticks) { }
+ @Override public boolean setPassenger(Entity entity) { return false; }
+ @Override public boolean eject() { return false; }
+ @Override public boolean leaveVehicle() { return false; }
+ @Override public void playEffect(EntityEffect effect) { }
+ @Override public void setCustomName(String name) { }
+ @Override public void setCustomNameVisible(boolean flag) { }
+ @Override public void setGlowing(boolean flag) { }
+ @Override public void setGravity(boolean gravity) { }
+ @Override public void setInvulnerable(boolean flag) { }
+ @Override public void setMomentum(Vector value) { }
+ @Override public void setSilent(boolean flag) { }
+ @Override public void setTicksLived(int value) { }
+ @Override public void setPersistent(boolean flag) { }
+ @Override public void setRotation(float yaw, float pitch) { }
+
+}
diff --git a/NMS/v1_15_R1/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_15_R1/EntityNMSArmorStand.java b/NMS/v1_15_R1/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_15_R1/EntityNMSArmorStand.java
new file mode 100644
index 00000000..c031dba8
--- /dev/null
+++ b/NMS/v1_15_R1/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_15_R1/EntityNMSArmorStand.java
@@ -0,0 +1,240 @@
+/*
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.gmail.filoghost.holographicdisplays.nms.v1_15_R1;
+
+import com.gmail.filoghost.holographicdisplays.api.line.HologramLine;
+import com.gmail.filoghost.holographicdisplays.disk.Configuration;
+import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSArmorStand;
+import com.gmail.filoghost.holographicdisplays.util.Utils;
+import com.gmail.filoghost.holographicdisplays.util.reflection.ReflectionUtils;
+import net.minecraft.server.v1_15_R1.*;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.util.CraftChatMessage;
+
+public class EntityNMSArmorStand extends EntityArmorStand implements NMSArmorStand {
+
+ private boolean lockTick;
+ private HologramLine parentPiece;
+ private CraftEntity customBukkitEntity;
+ private String customName;
+
+ public EntityNMSArmorStand(World world, HologramLine parentPiece) {
+ super(EntityTypes.ARMOR_STAND, world);
+ super.setInvisible(true);
+ super.setSmall(true);
+ super.setArms(false);
+ super.setNoGravity(true);
+ super.setBasePlate(true);
+ super.setMarker(true);
+ super.collides = false;
+ this.parentPiece = parentPiece;
+ forceSetBoundingBox(new NullBoundingBox());
+ }
+
+
+ @Override
+ public void b(NBTTagCompound nbttagcompound) {
+ // Do not save NBT.
+ }
+
+ @Override
+ public boolean c(NBTTagCompound nbttagcompound) {
+ // Do not save NBT.
+ return false;
+ }
+
+ @Override
+ public boolean d(NBTTagCompound nbttagcompound) {
+ // Do not save NBT.
+ return false;
+ }
+
+ @Override
+ public NBTTagCompound save(NBTTagCompound nbttagcompound) {
+ // Do not save NBT.
+ return nbttagcompound;
+ }
+
+ @Override
+ public void f(NBTTagCompound nbttagcompound) {
+ // Do not load NBT.
+ }
+
+ @Override
+ public void a(NBTTagCompound nbttagcompound) {
+ // Do not load NBT.
+ }
+
+ @Override
+ public boolean isInvulnerable(DamageSource source) {
+ /*
+ * The field Entity.invulnerable is private.
+ * It's only used while saving NBTTags, but since the entity would be killed
+ * on chunk unload, we prefer to override isInvulnerable().
+ */
+ return true;
+ }
+
+ @Override
+ public boolean isCollidable() {
+ return false;
+ }
+
+ @Override
+ public void setCustomName(IChatBaseComponent ichatbasecomponent) {
+ // Locks the custom name.
+ }
+
+ @Override
+ public void inactiveTick() {
+ // Check inactive ticks.
+
+ if (!lockTick) {
+ super.inactiveTick();
+ }
+ }
+
+ @Override
+ public void setCustomNameVisible(boolean visible) {
+ // Locks the custom name.
+ }
+
+ @Override
+ public EnumInteractionResult a(EntityHuman human, Vec3D vec3d, EnumHand enumhand) {
+ // Prevent stand being equipped
+ return EnumInteractionResult.PASS;
+ }
+
+ @Override
+ public boolean a_(int i, ItemStack item) {
+ // Prevent stand being equipped
+ return false;
+ }
+
+ @Override
+ public void setSlot(EnumItemSlot enumitemslot, ItemStack itemstack) {
+ // Prevent stand being equipped
+ }
+
+ @Override
+ public void a(AxisAlignedBB boundingBox) {
+ // Do not change it!
+ }
+
+ public void forceSetBoundingBox(AxisAlignedBB boundingBox) {
+ super.a(boundingBox);
+ }
+
+ @Override
+ public int getId() {
+ if (Configuration.preciseHologramMovement) {
+ StackTraceElement element = ReflectionUtils.getStackTraceElement(2);
+ if (element != null && element.getFileName() != null && element.getFileName().equals("EntityTrackerEntry.java") && 128 < element.getLineNumber() && element.getLineNumber() < 151) {
+ // Then this method is being called when creating a new movement packet, we return a fake ID
+ return -1;
+ }
+ }
+
+ return super.getId();
+ }
+
+ @Override
+ public void tick() {
+ if (!lockTick) {
+ super.tick();
+ }
+ }
+
+ @Override
+ public void a(SoundEffect soundeffect, float f, float f1) {
+ // Remove sounds.
+ }
+
+ @Override
+ public void setCustomNameNMS(String name) {
+ this.customName = Utils.limitLength(name, 300);
+ super.setCustomName(CraftChatMessage.fromStringOrNull(customName));
+ super.setCustomNameVisible(customName != null && !customName.isEmpty());
+ }
+
+ @Override
+ public String getCustomNameNMS() {
+ return this.customName;
+ }
+
+ @Override
+ public void setLockTick(boolean lock) {
+ lockTick = lock;
+ }
+
+ @Override
+ public void die() {
+ // Prevent being killed.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (customBukkitEntity == null) {
+ customBukkitEntity = new CraftNMSArmorStand(super.world.getServer(), this);
+ }
+ return customBukkitEntity;
+ }
+
+ @Override
+ public void killEntityNMS() {
+ super.dead = true;
+ }
+
+ @Override
+ public void setLocationNMS(double x, double y, double z) {
+ super.setPosition(x, y, z);
+
+ if (Configuration.preciseHologramMovement) {
+ // Send a packet near to update the position.
+ PacketPlayOutEntityTeleport teleportPacket = new PacketPlayOutEntityTeleport(this);
+
+ for (Object obj : super.world.getPlayers()) {
+ if (obj instanceof EntityPlayer) {
+ EntityPlayer nmsPlayer = (EntityPlayer) obj;
+
+ double distanceSquared = Utils.square(nmsPlayer.locX() - super.locX()) + Utils.square(nmsPlayer.locZ() - super.locZ());
+ if (distanceSquared < 8192 && nmsPlayer.playerConnection != null) {
+ nmsPlayer.playerConnection.sendPacket(teleportPacket);
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean isDeadNMS() {
+ return super.dead;
+ }
+
+ @Override
+ public int getIdNMS() {
+ return super.getId(); // Return the real ID without checking the stack trace.
+ }
+
+ @Override
+ public HologramLine getHologramLine() {
+ return parentPiece;
+ }
+
+ @Override
+ public org.bukkit.entity.Entity getBukkitEntityNMS() {
+ return getBukkitEntity();
+ }
+}
diff --git a/NMS/v1_15_R1/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_15_R1/EntityNMSItem.java b/NMS/v1_15_R1/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_15_R1/EntityNMSItem.java
new file mode 100644
index 00000000..170d9e45
--- /dev/null
+++ b/NMS/v1_15_R1/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_15_R1/EntityNMSItem.java
@@ -0,0 +1,246 @@
+/*
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.gmail.filoghost.holographicdisplays.nms.v1_15_R1;
+
+import com.gmail.filoghost.holographicdisplays.api.line.ItemLine;
+import com.gmail.filoghost.holographicdisplays.nms.interfaces.ItemPickupManager;
+import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSEntityBase;
+import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSItem;
+import com.gmail.filoghost.holographicdisplays.util.ConsoleLogger;
+import com.gmail.filoghost.holographicdisplays.util.ItemUtils;
+import com.gmail.filoghost.holographicdisplays.util.reflection.ReflectField;
+import net.minecraft.server.v1_15_R1.*;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack;
+import org.bukkit.entity.Player;
+
+import java.util.logging.Level;
+
+public class EntityNMSItem extends EntityItem implements NMSItem {
+
+ private static final ReflectField VEHICLE_FIELD = new ReflectField<>(Entity.class, "vehicle");
+
+ private boolean lockTick;
+ private ItemLine parentPiece;
+ private ItemPickupManager itemPickupManager;
+ private CraftEntity customBukkitEntity;
+
+ public EntityNMSItem(World world, ItemLine piece, ItemPickupManager itemPickupManager) {
+ super(EntityTypes.ITEM, world);
+ super.pickupDelay = 32767; // Lock the item pickup delay, also prevents entities from picking up the item
+ this.parentPiece = piece;
+ this.itemPickupManager = itemPickupManager;
+ }
+
+ @Override
+ public void tick() {
+
+ // So it won't get removed.
+ ticksLived = 0;
+
+ if (!lockTick) {
+ super.tick();
+ }
+ }
+
+ // Method called when a player is near.
+ @Override
+ public void pickup(EntityHuman human) {
+
+ if (human.locY() < super.locY() - 1.5 || human.locY() > super.locY() + 1.0) {
+ // Too low or too high, it's a bit weird./
+ return;
+ }
+
+ if (parentPiece.getPickupHandler() != null && human instanceof EntityPlayer) {
+ itemPickupManager.handleItemLinePickup((Player) human.getBukkitEntity(), parentPiece.getPickupHandler(), parentPiece.getParent());
+ // It is never added to the inventory.
+ }
+ }
+
+ @Override
+ public void b(NBTTagCompound nbttagcompound) {
+ // Do not save NBT.
+ }
+
+ @Override
+ public boolean c(NBTTagCompound nbttagcompound) {
+ // Do not save NBT.
+ return false;
+ }
+
+ @Override
+ public boolean d(NBTTagCompound nbttagcompound) {
+ // Do not save NBT.
+ return false;
+ }
+
+ @Override
+ public NBTTagCompound save(NBTTagCompound nbttagcompound) {
+ // Do not save NBT.
+ return nbttagcompound;
+ }
+
+ @Override
+ public void f(NBTTagCompound nbttagcompound) {
+ // Do not load NBT.
+ }
+
+ @Override
+ public void a(NBTTagCompound nbttagcompound) {
+ // Do not load NBT.
+ }
+
+ @Override
+ public boolean isInvulnerable(DamageSource source) {
+ /*
+ * The field Entity.invulnerable is private.
+ * It's only used while saving NBTTags, but since the entity would be killed
+ * on chunk unload, we prefer to override isInvulnerable().
+ */
+ return true;
+ }
+
+ @Override
+ public boolean isCollidable() {
+ return false;
+ }
+
+ @Override
+ public void inactiveTick() {
+ // Check inactive ticks.
+
+ if (!lockTick) {
+ super.inactiveTick();
+ }
+ }
+
+ @Override
+ public void setLockTick(boolean lock) {
+ lockTick = lock;
+ }
+
+ @Override
+ public void die() {
+ // Prevent being killed.
+ }
+
+ @Override
+ public boolean isAlive() {
+ // This override prevents items from being picked up by hoppers.
+ // Should have no side effects.
+ return false;
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (customBukkitEntity == null) {
+ customBukkitEntity = new CraftNMSItem(super.world.getServer(), this);
+ }
+ return customBukkitEntity;
+ }
+
+ @Override
+ public boolean isDeadNMS() {
+ return super.dead;
+ }
+
+ @Override
+ public void killEntityNMS() {
+ super.dead = true;
+ }
+
+ @Override
+ public void setLocationNMS(double x, double y, double z) {
+ super.setPosition(x, y, z);
+ }
+
+ @Override
+ public void setItemStackNMS(org.bukkit.inventory.ItemStack stack) {
+ ItemStack newItem = CraftItemStack.asNMSCopy(stack); // ItemStack.a is returned if the stack is null, invalid or the material is not an Item
+
+ if (newItem == null || newItem == ItemStack.a) {
+ newItem = new ItemStack(Blocks.BEDROCK);
+ }
+
+ if (newItem.getTag() == null) {
+ newItem.setTag(new NBTTagCompound());
+ }
+ NBTTagCompound display = newItem.getTag().getCompound("display"); // Returns a new NBTTagCompound if not existing
+ if (!newItem.getTag().hasKey("display")) {
+ newItem.getTag().set("display", display);
+ }
+
+ NBTTagList tagList = new NBTTagList();
+ tagList.add(NBTTagString.a(ItemUtils.ANTISTACK_LORE)); // Antistack lore
+ display.set("Lore", tagList);
+
+ setItemStack(newItem);
+ }
+
+ @Override
+ public int getIdNMS() {
+ return super.getId();
+ }
+
+ @Override
+ public ItemLine getHologramLine() {
+ return parentPiece;
+ }
+
+ @Override
+ public void allowPickup(boolean pickup) {
+ if (pickup) {
+ super.pickupDelay = 0;
+ } else {
+ super.pickupDelay = 32767;
+ }
+ }
+
+ @Override
+ public org.bukkit.entity.Entity getBukkitEntityNMS() {
+ return getBukkitEntity();
+ }
+
+ @Override
+ public void setPassengerOfNMS(NMSEntityBase vehicleBase) {
+ if (vehicleBase == null || !(vehicleBase instanceof Entity)) {
+ // It should never dismount
+ return;
+ }
+
+ Entity entity = (Entity) vehicleBase;
+
+ try {
+ if (super.getVehicle() != null) {
+ Entity oldVehicle = super.getVehicle();
+ VEHICLE_FIELD.set(this, null);
+ oldVehicle.passengers.remove(this);
+ }
+
+ VEHICLE_FIELD.set(this, entity);
+ entity.passengers.clear();
+ entity.passengers.add(this);
+
+ } catch (Throwable t) {
+ ConsoleLogger.logDebug(Level.SEVERE, "Couldn't set passenger", t);
+ }
+ }
+
+ @Override
+ public Object getRawItemStack() {
+ return super.getItemStack();
+ }
+}
diff --git a/NMS/v1_15_R1/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_15_R1/EntityNMSSlime.java b/NMS/v1_15_R1/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_15_R1/EntityNMSSlime.java
new file mode 100644
index 00000000..02044b20
--- /dev/null
+++ b/NMS/v1_15_R1/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_15_R1/EntityNMSSlime.java
@@ -0,0 +1,235 @@
+/*
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.gmail.filoghost.holographicdisplays.nms.v1_15_R1;
+
+import java.util.logging.Level;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.event.player.PlayerInteractEntityEvent;
+
+import com.gmail.filoghost.holographicdisplays.api.line.HologramLine;
+import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSEntityBase;
+import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSSlime;
+import com.gmail.filoghost.holographicdisplays.util.ConsoleLogger;
+import com.gmail.filoghost.holographicdisplays.util.reflection.ReflectField;
+
+import net.minecraft.server.v1_15_R1.AxisAlignedBB;
+import net.minecraft.server.v1_15_R1.DamageSource;
+import net.minecraft.server.v1_15_R1.Entity;
+import net.minecraft.server.v1_15_R1.EntityDamageSource;
+import net.minecraft.server.v1_15_R1.EntityPlayer;
+import net.minecraft.server.v1_15_R1.EntitySlime;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.IChatBaseComponent;
+import net.minecraft.server.v1_15_R1.NBTTagCompound;
+import net.minecraft.server.v1_15_R1.SoundEffect;
+import net.minecraft.server.v1_15_R1.World;
+
+public class EntityNMSSlime extends EntitySlime implements NMSSlime {
+
+ private static final ReflectField VEHICLE_FIELD = new ReflectField<>(Entity.class, "vehicle");
+
+ private boolean lockTick;
+ private HologramLine parentPiece;
+ private CraftEntity customBukkitEntity;
+
+ public EntityNMSSlime(World world, HologramLine parentPiece) {
+ super(EntityTypes.SLIME, world);
+ super.persistent = true;
+ super.collides = false;
+ a(0.0F, 0.0F);
+ setSize(1, false);
+ setInvisible(true);
+ this.parentPiece = parentPiece;
+ forceSetBoundingBox(new NullBoundingBox());
+ }
+
+ @Override
+ public void a(AxisAlignedBB boundingBox) {
+ // Do not change it!
+ }
+
+ public void forceSetBoundingBox(AxisAlignedBB boundingBox) {
+ super.a(boundingBox);
+ }
+
+ @Override
+ public void tick() {
+
+ // So it won't get removed.
+ ticksLived = 0;
+
+ if (!lockTick) {
+ super.tick();
+ }
+ }
+
+ @Override
+ public void b(NBTTagCompound nbttagcompound) {
+ // Do not save NBT.
+ }
+
+ @Override
+ public boolean c(NBTTagCompound nbttagcompound) {
+ // Do not save NBT.
+ return false;
+ }
+
+ @Override
+ public boolean d(NBTTagCompound nbttagcompound) {
+ // Do not save NBT.
+ return false;
+ }
+
+ @Override
+ public NBTTagCompound save(NBTTagCompound nbttagcompound) {
+ // Do not save NBT.
+ return nbttagcompound;
+ }
+
+ @Override
+ public void f(NBTTagCompound nbttagcompound) {
+ // Do not load NBT.
+ }
+
+ @Override
+ public void a(NBTTagCompound nbttagcompound) {
+ // Do not load NBT.
+ }
+
+ @Override
+ public boolean damageEntity(DamageSource damageSource, float amount) {
+ if (damageSource instanceof EntityDamageSource) {
+ EntityDamageSource entityDamageSource = (EntityDamageSource) damageSource;
+ if (entityDamageSource.getEntity() instanceof EntityPlayer) {
+ Bukkit.getPluginManager().callEvent(new PlayerInteractEntityEvent(((EntityPlayer) entityDamageSource.getEntity()).getBukkitEntity(), getBukkitEntity())); // Bukkit takes care of the exceptions
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isInvulnerable(DamageSource source) {
+ /*
+ * The field Entity.invulnerable is private.
+ * It's only used while saving NBTTags, but since the entity would be killed
+ * on chunk unload, we prefer to override isInvulnerable().
+ */
+ return true;
+ }
+
+ @Override
+ public boolean isCollidable() {
+ return false;
+ }
+
+ @Override
+ public void inactiveTick() {
+ // Check inactive ticks.
+
+ if (!lockTick) {
+ super.inactiveTick();
+ }
+ }
+
+ @Override
+ public void setCustomName(IChatBaseComponent ichatbasecomponent) {
+ // Locks the custom name.
+ }
+
+ @Override
+ public void setCustomNameVisible(boolean visible) {
+ // Locks the custom name.
+ }
+
+ @Override
+ public void a(SoundEffect soundeffect, float f, float f1) {
+ // Remove sounds.
+ }
+
+ @Override
+ public void setLockTick(boolean lock) {
+ lockTick = lock;
+ }
+
+ @Override
+ public void die() {
+ // Prevent being killed.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (customBukkitEntity == null) {
+ customBukkitEntity = new CraftNMSSlime(super.world.getServer(), this);
+ }
+ return customBukkitEntity;
+ }
+
+ @Override
+ public boolean isDeadNMS() {
+ return super.dead;
+ }
+
+ @Override
+ public void killEntityNMS() {
+ super.dead = true;
+ }
+
+ @Override
+ public void setLocationNMS(double x, double y, double z) {
+ super.setPosition(x, y, z);
+ }
+
+ @Override
+ public int getIdNMS() {
+ return super.getId();
+ }
+
+ @Override
+ public HologramLine getHologramLine() {
+ return parentPiece;
+ }
+
+ @Override
+ public org.bukkit.entity.Entity getBukkitEntityNMS() {
+ return getBukkitEntity();
+ }
+
+ @Override
+ public void setPassengerOfNMS(NMSEntityBase vehicleBase) {
+ if (vehicleBase == null || !(vehicleBase instanceof Entity)) {
+ // It should never dismount
+ return;
+ }
+
+ Entity entity = (Entity) vehicleBase;
+
+ try {
+ if (super.getVehicle() != null) {
+ Entity oldVehicle = super.getVehicle();
+ VEHICLE_FIELD.set(this, null);
+ oldVehicle.passengers.remove(this);
+ }
+
+ VEHICLE_FIELD.set(this, entity);
+ entity.passengers.clear();
+ entity.passengers.add(this);
+
+ } catch (Throwable t) {
+ ConsoleLogger.logDebug(Level.SEVERE, "Couldn't set passenger", t);
+ }
+ }
+}
diff --git a/NMS/v1_15_R1/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_15_R1/NmsManagerImpl.java b/NMS/v1_15_R1/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_15_R1/NmsManagerImpl.java
new file mode 100644
index 00000000..4d8a9009
--- /dev/null
+++ b/NMS/v1_15_R1/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_15_R1/NmsManagerImpl.java
@@ -0,0 +1,151 @@
+/*
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.gmail.filoghost.holographicdisplays.nms.v1_15_R1;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_15_R1.CraftWorld;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.inventory.ItemStack;
+
+import com.gmail.filoghost.holographicdisplays.api.line.HologramLine;
+import com.gmail.filoghost.holographicdisplays.api.line.ItemLine;
+import com.gmail.filoghost.holographicdisplays.nms.interfaces.ItemPickupManager;
+import com.gmail.filoghost.holographicdisplays.nms.interfaces.NMSManager;
+import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSArmorStand;
+import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSEntityBase;
+import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSItem;
+import com.gmail.filoghost.holographicdisplays.util.ConsoleLogger;
+import com.gmail.filoghost.holographicdisplays.util.Validator;
+import com.gmail.filoghost.holographicdisplays.util.reflection.ReflectField;
+import com.gmail.filoghost.holographicdisplays.util.reflection.ReflectMethod;
+
+import net.minecraft.server.v1_15_R1.Entity;
+import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.EnumCreatureType;
+import net.minecraft.server.v1_15_R1.IRegistry;
+import net.minecraft.server.v1_15_R1.MathHelper;
+import net.minecraft.server.v1_15_R1.RegistryID;
+import net.minecraft.server.v1_15_R1.RegistryMaterials;
+import net.minecraft.server.v1_15_R1.WorldServer;
+
+public class NmsManagerImpl implements NMSManager {
+
+ private static final ReflectField>> REGISTRY_ID_FIELD = new ReflectField<>(RegistryMaterials.class, "b");
+ private static final ReflectField