diff --git a/NMS/v1_13_R2/pom.xml b/NMS/v1_13_R2/pom.xml
new file mode 100644
index 00000000..b3aa2ee2
--- /dev/null
+++ b/NMS/v1_13_R2/pom.xml
@@ -0,0 +1,43 @@
+
+
+ 4.0.0
+
+
+ com.gmail.filoghost.holographicdisplays
+ holographicdisplays-parent
+ 2.3.0-SNAPSHOT
+ ../../pom.xml
+
+
+ holographicdisplays-nms-v1_13_r2
+ HolographicDisplays NMS v1_13_R2
+
+
+
+ codemc-repo
+ https://repo.codemc.org/repository/nms/
+
+
+
+
+
+ ${project.groupId}
+ holographicdisplays-nms-interfaces
+ 2.3.0-SNAPSHOT
+
+
+
+ ${project.groupId}
+ holographicdisplays-utils
+ 2.3.0-SNAPSHOT
+
+
+
+ org.spigotmc
+ spigot
+ 1.13.1-R0.1-20180826.175839-1
+
+
+
+
\ No newline at end of file
diff --git a/NMS/v1_13_R2/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_13_R2/CraftNMSArmorStand.java b/NMS/v1_13_R2/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_13_R2/CraftNMSArmorStand.java
new file mode 100644
index 00000000..4c5fa7c3
--- /dev/null
+++ b/NMS/v1_13_R2/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_13_R2/CraftNMSArmorStand.java
@@ -0,0 +1,82 @@
+package com.gmail.filoghost.holographicdisplays.nms.v1_13_R2;
+
+import java.util.Collection;
+
+import org.bukkit.EntityEffect;
+import org.bukkit.Location;
+import org.bukkit.craftbukkit.v1_13_R2.CraftServer;
+import org.bukkit.craftbukkit.v1_13_R2.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 Armor stand 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
+ @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) { }
+
+
+}
diff --git a/NMS/v1_13_R2/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_13_R2/CraftNMSItem.java b/NMS/v1_13_R2/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_13_R2/CraftNMSItem.java
new file mode 100644
index 00000000..f02aaa58
--- /dev/null
+++ b/NMS/v1_13_R2/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_13_R2/CraftNMSItem.java
@@ -0,0 +1,50 @@
+package com.gmail.filoghost.holographicdisplays.nms.v1_13_R2;
+
+import org.bukkit.EntityEffect;
+import org.bukkit.Location;
+import org.bukkit.craftbukkit.v1_13_R2.CraftServer;
+import org.bukkit.craftbukkit.v1_13_R2.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 Entity
+ @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) { }
+
+ // Methods from Item
+ @Override public void setItemStack(ItemStack stack) { }
+ @Override public void setPickupDelay(int delay) { }
+
+}
diff --git a/NMS/v1_13_R2/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_13_R2/CraftNMSSlime.java b/NMS/v1_13_R2/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_13_R2/CraftNMSSlime.java
new file mode 100644
index 00000000..72be5e37
--- /dev/null
+++ b/NMS/v1_13_R2/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_13_R2/CraftNMSSlime.java
@@ -0,0 +1,70 @@
+package com.gmail.filoghost.holographicdisplays.nms.v1_13_R2;
+
+import java.util.Collection;
+
+import org.bukkit.EntityEffect;
+import org.bukkit.Location;
+import org.bukkit.craftbukkit.v1_13_R2.CraftServer;
+import org.bukkit.craftbukkit.v1_13_R2.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 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
+ @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) { }
+
+ // Methods from Mob
+ @Override public void setLootTable(LootTable table) { }
+ @Override public void setSeed(long seed) { }
+
+ // Methods from Slime
+ @Override public void setSize(int size) { }
+ @Override public void setTarget(LivingEntity target) { }
+
+}
diff --git a/NMS/v1_13_R2/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_13_R2/EntityNMSArmorStand.java b/NMS/v1_13_R2/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_13_R2/EntityNMSArmorStand.java
new file mode 100644
index 00000000..88cd5e1c
--- /dev/null
+++ b/NMS/v1_13_R2/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_13_R2/EntityNMSArmorStand.java
@@ -0,0 +1,237 @@
+package com.gmail.filoghost.holographicdisplays.nms.v1_13_R2;
+
+import org.bukkit.craftbukkit.v1_13_R2.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_13_R2.util.CraftChatMessage;
+
+import com.gmail.filoghost.holographicdisplays.api.line.HologramLine;
+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_13_R2.AxisAlignedBB;
+import net.minecraft.server.v1_13_R2.DamageSource;
+import net.minecraft.server.v1_13_R2.EntityArmorStand;
+import net.minecraft.server.v1_13_R2.EntityHuman;
+import net.minecraft.server.v1_13_R2.EntityPlayer;
+import net.minecraft.server.v1_13_R2.EnumHand;
+import net.minecraft.server.v1_13_R2.EnumInteractionResult;
+import net.minecraft.server.v1_13_R2.EnumItemSlot;
+import net.minecraft.server.v1_13_R2.IChatBaseComponent;
+import net.minecraft.server.v1_13_R2.ItemStack;
+import net.minecraft.server.v1_13_R2.NBTTagCompound;
+import net.minecraft.server.v1_13_R2.PacketPlayOutEntityTeleport;
+import net.minecraft.server.v1_13_R2.SoundEffect;
+import net.minecraft.server.v1_13_R2.Vec3D;
+import net.minecraft.server.v1_13_R2.World;
+
+public class EntityNMSArmorStand extends EntityArmorStand implements NMSArmorStand {
+
+ private boolean lockTick;
+ private HologramLine parentPiece;
+
+ public EntityNMSArmorStand(World world, HologramLine parentPiece) {
+ super(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 c(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() {
+ StackTraceElement element = ReflectionUtils.getStackTraceElement(2);
+ if (element != null && element.getFileName() != null && element.getFileName().equals("EntityTrackerEntry.java") && 158 < element.getLineNumber() && element.getLineNumber() < 168) {
+ // 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) {
+ if (name != null && name.length() > 300) {
+ name = name.substring(0, 300);
+ }
+ super.setCustomName(CraftChatMessage.fromStringOrNull(name));
+ super.setCustomNameVisible(name != null && !name.isEmpty());
+ }
+
+ @Override
+ public String getCustomNameNMS() {
+ return CraftChatMessage.fromComponent(super.getCustomName());
+ }
+
+ @Override
+ public void setLockTick(boolean lock) {
+ lockTick = lock;
+ }
+
+ @Override
+ public void die() {
+ // Prevent being killed.
+ }
+
+ @Override
+ public CraftEntity getBukkitEntity() {
+ if (super.bukkitEntity == null) {
+ super.bukkitEntity = new CraftNMSArmorStand(super.world.getServer(), this);
+ }
+ return super.bukkitEntity;
+ }
+
+ @Override
+ public void killEntityNMS() {
+ super.dead = true;
+ }
+
+ @Override
+ public void setLocationNMS(double x, double y, double z) {
+ super.setPosition(x, y, z);
+
+ // Send a packet near to update the position.
+ PacketPlayOutEntityTeleport teleportPacket = new PacketPlayOutEntityTeleport(this);
+
+ for (Object obj : super.world.players) {
+ 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();
+ }
+}
\ No newline at end of file
diff --git a/NMS/v1_13_R2/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_13_R2/EntityNMSItem.java b/NMS/v1_13_R2/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_13_R2/EntityNMSItem.java
new file mode 100644
index 00000000..bf6a738c
--- /dev/null
+++ b/NMS/v1_13_R2/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_13_R2/EntityNMSItem.java
@@ -0,0 +1,247 @@
+package com.gmail.filoghost.holographicdisplays.nms.v1_13_R2;
+
+import org.bukkit.craftbukkit.v1_13_R2.entity.CraftEntity;
+import org.bukkit.craftbukkit.v1_13_R2.inventory.CraftItemStack;
+import org.bukkit.entity.Player;
+
+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_13_R2.AxisAlignedBB;
+import net.minecraft.server.v1_13_R2.Blocks;
+import net.minecraft.server.v1_13_R2.DamageSource;
+import net.minecraft.server.v1_13_R2.Entity;
+import net.minecraft.server.v1_13_R2.EntityHuman;
+import net.minecraft.server.v1_13_R2.EntityItem;
+import net.minecraft.server.v1_13_R2.EntityPlayer;
+import net.minecraft.server.v1_13_R2.ItemStack;
+import net.minecraft.server.v1_13_R2.NBTTagCompound;
+import net.minecraft.server.v1_13_R2.NBTTagList;
+import net.minecraft.server.v1_13_R2.NBTTagString;
+import net.minecraft.server.v1_13_R2.World;
+
+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;
+
+ public EntityNMSItem(World world, ItemLine piece, ItemPickupManager itemPickupManager) {
+ super(world);
+ super.pickupDelay = 32767; // Lock the item pickup delay, also prevents entities from picking up the item
+ this.parentPiece = piece;
+ this.itemPickupManager = itemPickupManager;
+ forceSetBoundingBox(new NullBoundingBox());
+ }
+
+ @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 d(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 a(AxisAlignedBB boundingBox) {
+ // Do not change it!
+ }
+
+ public void forceSetBoundingBox(AxisAlignedBB boundingBox) {
+ super.a(boundingBox);
+ }
+
+ @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 CraftEntity getBukkitEntity() {
+ if (super.bukkitEntity == null) {
+ super.bukkitEntity = new CraftNMSItem(super.world.getServer(), this);
+ }
+ return super.bukkitEntity;
+ }
+
+ @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(new NBTTagString(ItemUtils.ANTISTACK_LORE)); // Antistack lore
+ display.set("Lore", tagList);
+
+ newItem.setCount(1);
+
+ 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 (Exception ex) {
+ ConsoleLogger.logDebugException(ex);
+ }
+ }
+
+ @Override
+ public Object getRawItemStack() {
+ return super.getItemStack();
+ }
+}
diff --git a/NMS/v1_13_R2/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_13_R2/EntityNMSSlime.java b/NMS/v1_13_R2/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_13_R2/EntityNMSSlime.java
new file mode 100644
index 00000000..d48ffd7c
--- /dev/null
+++ b/NMS/v1_13_R2/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_13_R2/EntityNMSSlime.java
@@ -0,0 +1,217 @@
+package com.gmail.filoghost.holographicdisplays.nms.v1_13_R2;
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_13_R2.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_13_R2.AxisAlignedBB;
+import net.minecraft.server.v1_13_R2.DamageSource;
+import net.minecraft.server.v1_13_R2.Entity;
+import net.minecraft.server.v1_13_R2.EntityDamageSource;
+import net.minecraft.server.v1_13_R2.EntityPlayer;
+import net.minecraft.server.v1_13_R2.EntitySlime;
+import net.minecraft.server.v1_13_R2.IChatBaseComponent;
+import net.minecraft.server.v1_13_R2.NBTTagCompound;
+import net.minecraft.server.v1_13_R2.SoundEffect;
+import net.minecraft.server.v1_13_R2.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;
+
+ public EntityNMSSlime(World world, HologramLine parentPiece) {
+ super(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 (super.bukkitEntity == null) {
+ super.bukkitEntity = new CraftNMSSlime(super.world.getServer(), this);
+ }
+ return super.bukkitEntity;
+ }
+
+ @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 (Exception ex) {
+ ConsoleLogger.logDebugException(ex);
+ }
+ }
+}
diff --git a/NMS/v1_13_R2/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_13_R2/FancyMessageImpl.java b/NMS/v1_13_R2/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_13_R2/FancyMessageImpl.java
new file mode 100644
index 00000000..d14c406d
--- /dev/null
+++ b/NMS/v1_13_R2/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_13_R2/FancyMessageImpl.java
@@ -0,0 +1,170 @@
+package com.gmail.filoghost.holographicdisplays.nms.v1_13_R2;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import net.minecraft.server.v1_13_R2.IChatBaseComponent;
+import net.minecraft.server.v1_13_R2.PacketPlayOutChat;
+
+import org.bukkit.ChatColor;
+
+import com.google.gson.stream.JsonWriter;
+
+import org.bukkit.craftbukkit.v1_13_R2.entity.CraftPlayer;
+import org.bukkit.entity.Player;
+
+import com.gmail.filoghost.holographicdisplays.nms.interfaces.FancyMessage;
+
+public class FancyMessageImpl implements FancyMessage {
+
+ private List messageParts;
+
+ public FancyMessageImpl(String firstPartText) {
+ messageParts = new ArrayList();
+ messageParts.add(new MessagePart(firstPartText));
+ }
+
+ @Override
+ public FancyMessageImpl color(ChatColor color) {
+ if (!color.isColor()) {
+ throw new IllegalArgumentException(color.name() + " is not a color");
+ }
+ latest().color = color;
+ return this;
+ }
+
+ @Override
+ public FancyMessageImpl style(ChatColor... styles) {
+ for (ChatColor style : styles) {
+ if (!style.isFormat()) {
+ throw new IllegalArgumentException(style.name() + " is not a style");
+ }
+ }
+ latest().styles = styles;
+ return this;
+ }
+
+ @Override
+ public FancyMessageImpl file(String path) {
+ onClick("open_file", path);
+ return this;
+ }
+
+ @Override
+ public FancyMessageImpl link(String url) {
+ onClick("open_url", url);
+ return this;
+ }
+
+ @Override
+ public FancyMessageImpl suggest(String command) {
+ onClick("suggest_command", command);
+ return this;
+ }
+
+ @Override
+ public FancyMessageImpl command(String command) {
+ onClick("run_command", command);
+ return this;
+ }
+
+ @Override
+ public FancyMessageImpl tooltip(String text) {
+ onHover("show_text", text);
+ return this;
+ }
+
+ @Override
+ public FancyMessageImpl then(Object obj) {
+ messageParts.add(new MessagePart(obj.toString()));
+ return this;
+ }
+
+ @Override
+ public String toJSONString() {
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter json = new JsonWriter(stringWriter);
+
+ try {
+ if (messageParts.size() == 1) {
+ latest().writeJson(json);
+ } else {
+ json.beginObject().name("text").value("").name("extra").beginArray();
+ for (MessagePart part : messageParts) {
+ part.writeJson(json);
+ }
+ json.endArray().endObject();
+ }
+
+ } catch (IOException e) {
+ throw new RuntimeException("invalid message");
+ }
+ return stringWriter.toString();
+ }
+
+ @Override
+ public void send(Player player) {
+ ((CraftPlayer) player).getHandle().playerConnection.sendPacket(new PacketPlayOutChat(IChatBaseComponent.ChatSerializer.a(toJSONString())));
+ }
+
+ private MessagePart latest() {
+ return messageParts.get(messageParts.size() - 1);
+ }
+
+ private void onClick(String name, String data) {
+ MessagePart latest = latest();
+ latest.clickActionName = name;
+ latest.clickActionData = data;
+ }
+
+ private void onHover(String name, String data) {
+ MessagePart latest = latest();
+ latest.hoverActionName = name;
+ latest.hoverActionData = data;
+ }
+
+ static class MessagePart {
+
+ public ChatColor color = null;
+ public ChatColor[] styles = null;
+ public String clickActionName = null;
+ public String clickActionData = null;
+ public String hoverActionName = null;
+ public String hoverActionData = null;
+ public final String text;
+
+ public MessagePart(final String text) {
+ this.text = text;
+ }
+
+ public JsonWriter writeJson(final JsonWriter json) throws IOException {
+ json.beginObject().name("text").value(text);
+ if (color != null) {
+ json.name("color").value(color.name().toLowerCase());
+ }
+ if (styles != null) {
+ for (final ChatColor style : styles) {
+ json.name(style == ChatColor.UNDERLINE ? "underlined" : style.name().toLowerCase()).value(true);
+ }
+ }
+ if (clickActionName != null && clickActionData != null) {
+ json.name("clickEvent")
+ .beginObject()
+ .name("action").value(clickActionName)
+ .name("value").value(clickActionData)
+ .endObject();
+ }
+ if (hoverActionName != null && hoverActionData != null) {
+ json.name("hoverEvent")
+ .beginObject()
+ .name("action").value(hoverActionName)
+ .name("value").value(hoverActionData)
+ .endObject();
+ }
+ return json.endObject();
+ }
+
+ }
+}
diff --git a/NMS/v1_13_R2/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_13_R2/NmsManagerImpl.java b/NMS/v1_13_R2/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_13_R2/NmsManagerImpl.java
new file mode 100644
index 00000000..9032d65d
--- /dev/null
+++ b/NMS/v1_13_R2/src/main/java/com/gmail/filoghost/holographicdisplays/nms/v1_13_R2/NmsManagerImpl.java
@@ -0,0 +1,167 @@
+package com.gmail.filoghost.holographicdisplays.nms.v1_13_R2;
+
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.function.Function;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Chunk;
+import org.bukkit.craftbukkit.v1_13_R2.CraftWorld;
+import org.bukkit.craftbukkit.v1_13_R2.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.FancyMessage;
+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.VersionUtils;
+import com.gmail.filoghost.holographicdisplays.util.reflection.ReflectField;
+
+import net.minecraft.server.v1_13_R2.Entity;
+import net.minecraft.server.v1_13_R2.EntityTypes;
+import net.minecraft.server.v1_13_R2.IRegistry;
+import net.minecraft.server.v1_13_R2.MathHelper;
+import net.minecraft.server.v1_13_R2.RegistryID;
+import net.minecraft.server.v1_13_R2.RegistryMaterials;
+import net.minecraft.server.v1_13_R2.World;
+import net.minecraft.server.v1_13_R2.WorldServer;
+
+public class NmsManagerImpl implements NMSManager {
+
+ private static final ReflectField>> REGISTRY_ID_FIELD = new ReflectField>>(RegistryMaterials.class, "b");
+ private static final ReflectField