From b7e06f23bf424a1d1fec6761a79fe8a125a29b14 Mon Sep 17 00:00:00 2001
From: WayofTime <WayofTime@users.noreply.github.com>
Date: Tue, 19 Jan 2021 19:26:23 -0500
Subject: [PATCH] Very WIP implementation of Sigil of Holding

---
 .../java/wayoftime/bloodmagic/BloodMagic.java |   5 +
 .../client/key/BloodMagicKeyHandler.java      |  82 ++++
 .../bloodmagic/client/key/IKeybindable.java   |   9 +
 .../client/key/KeyBindingBloodMagic.java      |  17 +
 .../bloodmagic/client/key/KeyBindings.java    | 110 +++++
 .../common/block/BloodMagicBlocks.java        |   2 +
 .../item/inventory/ContainerHolding.java      | 202 +++++++++
 .../item/inventory/InventoryHolding.java      |  82 ++++
 .../common/item/inventory/ItemInventory.java  | 252 ++++++++++++
 .../common/item/sigil/ItemSigilHolding.java   | 385 ++++++++++++++++++
 .../network/BloodMagicPacketHandler.java      |   2 +
 .../network/KeyProcessorPacket.java           |  71 ++++
 .../java/wayoftime/bloodmagic/util/Utils.java |  29 ++
 13 files changed, 1248 insertions(+)
 create mode 100644 src/main/java/wayoftime/bloodmagic/client/key/BloodMagicKeyHandler.java
 create mode 100644 src/main/java/wayoftime/bloodmagic/client/key/IKeybindable.java
 create mode 100644 src/main/java/wayoftime/bloodmagic/client/key/KeyBindingBloodMagic.java
 create mode 100644 src/main/java/wayoftime/bloodmagic/client/key/KeyBindings.java
 create mode 100644 src/main/java/wayoftime/bloodmagic/common/item/inventory/ContainerHolding.java
 create mode 100644 src/main/java/wayoftime/bloodmagic/common/item/inventory/InventoryHolding.java
 create mode 100644 src/main/java/wayoftime/bloodmagic/common/item/inventory/ItemInventory.java
 create mode 100644 src/main/java/wayoftime/bloodmagic/common/item/sigil/ItemSigilHolding.java
 create mode 100644 src/main/java/wayoftime/bloodmagic/network/KeyProcessorPacket.java

diff --git a/src/main/java/wayoftime/bloodmagic/BloodMagic.java b/src/main/java/wayoftime/bloodmagic/BloodMagic.java
index 46d3432b..e4575033 100644
--- a/src/main/java/wayoftime/bloodmagic/BloodMagic.java
+++ b/src/main/java/wayoftime/bloodmagic/BloodMagic.java
@@ -36,6 +36,8 @@ import net.minecraftforge.fml.event.server.FMLServerStartingEvent;
 import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
 import wayoftime.bloodmagic.client.ClientEvents;
 import wayoftime.bloodmagic.client.hud.Elements;
+import wayoftime.bloodmagic.client.key.BloodMagicKeyHandler;
+import wayoftime.bloodmagic.client.key.KeyBindings;
 import wayoftime.bloodmagic.client.model.MimicModelLoader;
 import wayoftime.bloodmagic.common.block.BloodMagicBlocks;
 import wayoftime.bloodmagic.common.data.GeneratorBaseRecipes;
@@ -249,6 +251,8 @@ public class BloodMagic
 		ClientEvents.initClientEvents(event);
 		Elements.registerElements();
 		MinecraftForge.EVENT_BUS.register(new ClientEvents());
+		KeyBindings.initializeKeys();
+		new BloodMagicKeyHandler();
 //		IEventBus modBus = FMLJavaModLoadingContext.get().getModEventBus();
 //		
 
@@ -306,4 +310,5 @@ public class BloodMagic
 			return new ItemStack(BloodMagicBlocks.BLOOD_ALTAR.get());
 		}
 	};
+	public static final String NAME = "Blood Magic: Alchemical Wizardry";
 }
diff --git a/src/main/java/wayoftime/bloodmagic/client/key/BloodMagicKeyHandler.java b/src/main/java/wayoftime/bloodmagic/client/key/BloodMagicKeyHandler.java
new file mode 100644
index 00000000..f3b2ab92
--- /dev/null
+++ b/src/main/java/wayoftime/bloodmagic/client/key/BloodMagicKeyHandler.java
@@ -0,0 +1,82 @@
+package wayoftime.bloodmagic.client.key;
+
+import java.util.BitSet;
+
+import org.lwjgl.glfw.GLFW;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.settings.KeyBinding;
+import net.minecraft.client.util.InputMappings;
+import net.minecraftforge.client.event.InputEvent;
+import net.minecraftforge.common.MinecraftForge;
+
+public class BloodMagicKeyHandler
+{
+
+	private final BitSet keyDown;
+
+	private final BitSet repeatings;
+
+	public BloodMagicKeyHandler()
+	{
+		this.keyDown = new BitSet();
+		this.repeatings = new BitSet();
+		MinecraftForge.EVENT_BUS.addListener(this::keyTick);
+	}
+
+	public static boolean isKeyDown(KeyBinding keyBinding)
+	{
+		InputMappings.Input key = keyBinding.getKey();
+		int keyCode = key.getKeyCode();
+		if (keyCode != InputMappings.INPUT_INVALID.getKeyCode())
+		{
+			long windowHandle = Minecraft.getInstance().getMainWindow().getHandle();
+			try
+			{
+				if (key.getType() == InputMappings.Type.KEYSYM)
+				{
+					return InputMappings.isKeyDown(windowHandle, keyCode);
+				} else if (key.getType() == InputMappings.Type.MOUSE)
+				{
+					return GLFW.glfwGetMouseButton(windowHandle, keyCode) == GLFW.GLFW_PRESS;
+				}
+			} catch (Exception ignored)
+			{
+			}
+		}
+		return false;
+	}
+
+	public void keyTick(InputEvent.KeyInputEvent event)
+	{
+//		System.out.println("Pressing the key handlers");
+		for (int i = 0; i < KeyBindings.values().length; i++)
+		{
+			KeyBindings keyBindings = KeyBindings.values()[i];
+			KeyBinding keyBinding = keyBindings.getKey();
+			boolean state = keyBinding.isKeyDown();
+			boolean lastState = keyDown.get(i);
+			if (state != lastState || (state && repeatings.get(i)))
+			{
+				if (state)
+				{
+					keyDown(keyBindings, lastState);
+				} else
+				{
+					keyUp(keyBindings);
+				}
+				keyDown.set(i, state);
+			}
+		}
+	}
+
+	public void keyDown(KeyBindings kb, boolean isRepeat)
+	{
+		kb.handleKeybind();
+	}
+
+	public void keyUp(KeyBindings kb)
+	{
+
+	}
+}
diff --git a/src/main/java/wayoftime/bloodmagic/client/key/IKeybindable.java b/src/main/java/wayoftime/bloodmagic/client/key/IKeybindable.java
new file mode 100644
index 00000000..35eddcd0
--- /dev/null
+++ b/src/main/java/wayoftime/bloodmagic/client/key/IKeybindable.java
@@ -0,0 +1,9 @@
+package wayoftime.bloodmagic.client.key;
+
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.item.ItemStack;
+
+public interface IKeybindable
+{
+	void onKeyPressed(ItemStack stack, PlayerEntity player, KeyBindings key, boolean showInChat);
+}
diff --git a/src/main/java/wayoftime/bloodmagic/client/key/KeyBindingBloodMagic.java b/src/main/java/wayoftime/bloodmagic/client/key/KeyBindingBloodMagic.java
new file mode 100644
index 00000000..b48666a1
--- /dev/null
+++ b/src/main/java/wayoftime/bloodmagic/client/key/KeyBindingBloodMagic.java
@@ -0,0 +1,17 @@
+package wayoftime.bloodmagic.client.key;
+
+import net.minecraft.client.settings.KeyBinding;
+import net.minecraftforge.api.distmarker.Dist;
+import net.minecraftforge.api.distmarker.OnlyIn;
+import net.minecraftforge.fml.client.registry.ClientRegistry;
+
+@OnlyIn(Dist.CLIENT)
+public class KeyBindingBloodMagic extends KeyBinding
+{
+	public KeyBindingBloodMagic(KeyBindings key)
+	{
+		super(key.getDescription(), -1, "key.bloodmagic.category");
+
+		ClientRegistry.registerKeyBinding(this);
+	}
+}
diff --git a/src/main/java/wayoftime/bloodmagic/client/key/KeyBindings.java b/src/main/java/wayoftime/bloodmagic/client/key/KeyBindings.java
new file mode 100644
index 00000000..fe6c0224
--- /dev/null
+++ b/src/main/java/wayoftime/bloodmagic/client/key/KeyBindings.java
@@ -0,0 +1,110 @@
+package wayoftime.bloodmagic.client.key;
+
+import java.util.Locale;
+
+import net.minecraft.client.settings.KeyBinding;
+import net.minecraftforge.api.distmarker.Dist;
+import net.minecraftforge.api.distmarker.OnlyIn;
+import net.minecraftforge.client.settings.IKeyConflictContext;
+import net.minecraftforge.client.settings.KeyConflictContext;
+import net.minecraftforge.client.settings.KeyModifier;
+import wayoftime.bloodmagic.BloodMagic;
+import wayoftime.bloodmagic.network.KeyProcessorPacket;
+
+public enum KeyBindings
+{
+	// @formatter:off
+	OPEN_HOLDING(KeyConflictContext.IN_GAME, KeyModifier.NONE, -1)
+	{
+		@OnlyIn(Dist.CLIENT)
+		@Override
+		public void handleKeybind()
+		{
+			BloodMagic.packetHandler.sendToServer(new KeyProcessorPacket(this.ordinal(), false));
+			System.out.println("I is on the client.");
+//			ItemStack itemStack = ClientHandler.minecraft.player.getHeldItemMainhand();
+//			if (itemStack.getItem() instanceof IKeybindable)
+//				BloodMagicPacketHandler.INSTANCE.sendToServer(new KeyProcessorPacket(this, false));
+		}
+	},
+	CYCLE_HOLDING_POS(KeyConflictContext.IN_GAME, KeyModifier.SHIFT, -1)
+	{
+		@OnlyIn(Dist.CLIENT)
+		@Override
+		public void handleKeybind()
+		{
+//			ClientPlayerEntity player = Minecraft.getInstance().player;
+//			if (player.getHeldItemMainhand().getItem() instanceof ItemSigilHolding)
+//				ClientHandler.cycleSigil(player.getHeldItemMainhand(), player, -1);
+		}
+	},
+	CYCLE_HOLDING_NEG(KeyConflictContext.IN_GAME, KeyModifier.SHIFT, -1)
+	{
+		@OnlyIn(Dist.CLIENT)
+		@Override
+		public void handleKeybind()
+		{
+//			ClientPlayerEntity player = Minecraft.getInstance().player;
+//			if (player.getHeldItemMainhand().getItem() instanceof ItemSigilHolding)
+//				ClientHandler.cycleSigil(player.getHeldItemMainhand(), player, 1);
+		}
+	},;
+	// @formatter:on
+
+	private final IKeyConflictContext keyConflictContext;
+	private final KeyModifier keyModifier;
+	private final int keyCode;
+
+	@OnlyIn(Dist.CLIENT)
+	private KeyBinding key;
+
+	KeyBindings(IKeyConflictContext keyConflictContext, KeyModifier keyModifier, int keyCode)
+	{
+		this.keyConflictContext = keyConflictContext;
+		this.keyModifier = keyModifier;
+		this.keyCode = keyCode;
+	}
+
+	@OnlyIn(Dist.CLIENT)
+	public abstract void handleKeybind();
+
+	public IKeyConflictContext getKeyConflictContext()
+	{
+		return keyConflictContext;
+	}
+
+	public KeyModifier getKeyModifier()
+	{
+		return keyModifier;
+	}
+
+	public int getKeyCode()
+	{
+		return keyCode;
+	}
+
+	@OnlyIn(Dist.CLIENT)
+	public KeyBinding getKey()
+	{
+		if (key == null)
+			key = new KeyBindingBloodMagic(this);
+
+		return key;
+	}
+
+	@OnlyIn(Dist.CLIENT)
+	public void setKey(KeyBinding key)
+	{
+		this.key = key;
+	}
+
+	public String getDescription()
+	{
+		return BloodMagic.MODID + ".keybind." + name().toLowerCase(Locale.ENGLISH);
+	}
+
+	public static void initializeKeys()
+	{
+		OPEN_HOLDING.getKey();
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/wayoftime/bloodmagic/common/block/BloodMagicBlocks.java b/src/main/java/wayoftime/bloodmagic/common/block/BloodMagicBlocks.java
index 120ebb55..5c9101e0 100644
--- a/src/main/java/wayoftime/bloodmagic/common/block/BloodMagicBlocks.java
+++ b/src/main/java/wayoftime/bloodmagic/common/block/BloodMagicBlocks.java
@@ -33,6 +33,7 @@ import wayoftime.bloodmagic.api.compat.EnumDemonWillType;
 import wayoftime.bloodmagic.block.enums.BloodRuneType;
 import wayoftime.bloodmagic.common.block.base.BlockPillarCap;
 import wayoftime.bloodmagic.common.item.BloodMagicItems;
+import wayoftime.bloodmagic.common.item.inventory.ContainerHolding;
 import wayoftime.bloodmagic.ritual.EnumRuneType;
 import wayoftime.bloodmagic.tile.container.ContainerAlchemicalReactionChamber;
 import wayoftime.bloodmagic.tile.container.ContainerAlchemyTable;
@@ -131,6 +132,7 @@ public class BloodMagicBlocks
 	public static final RegistryObject<ContainerType<ContainerSoulForge>> SOUL_FORGE_CONTAINER = CONTAINERS.register("soul_forge_container", () -> IForgeContainerType.create(ContainerSoulForge::new));
 	public static final RegistryObject<ContainerType<ContainerAlchemicalReactionChamber>> ARC_CONTAINER = CONTAINERS.register("arc_container", () -> IForgeContainerType.create(ContainerAlchemicalReactionChamber::new));
 	public static final RegistryObject<ContainerType<ContainerAlchemyTable>> ALCHEMY_TABLE_CONTAINER = CONTAINERS.register("alchemy_table_container", () -> IForgeContainerType.create(ContainerAlchemyTable::new));
+	public static final RegistryObject<ContainerType<ContainerHolding>> HOLDING_CONTAINER = CONTAINERS.register("holding_container", () -> IForgeContainerType.create(ContainerHolding::new));
 
 	// Dungeon Blocks
 	public static final RegistryObject<Block> DUNGEON_BRICK_1 = DUNGEONBLOCKS.register("dungeon_brick1", () -> new Block(Properties.create(Material.ROCK).hardnessAndResistance(2.0F, 5.0F).sound(SoundType.STONE).harvestTool(ToolType.PICKAXE).harvestLevel(2).setRequiresTool()));
diff --git a/src/main/java/wayoftime/bloodmagic/common/item/inventory/ContainerHolding.java b/src/main/java/wayoftime/bloodmagic/common/item/inventory/ContainerHolding.java
new file mode 100644
index 00000000..3e207e89
--- /dev/null
+++ b/src/main/java/wayoftime/bloodmagic/common/item/inventory/ContainerHolding.java
@@ -0,0 +1,202 @@
+package wayoftime.bloodmagic.common.item.inventory;
+
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.entity.player.PlayerInventory;
+import net.minecraft.inventory.IInventory;
+import net.minecraft.inventory.container.Container;
+import net.minecraft.inventory.container.Slot;
+import net.minecraft.item.ItemStack;
+import net.minecraft.network.PacketBuffer;
+import net.minecraftforge.fml.common.thread.EffectiveSide;
+import wayoftime.bloodmagic.common.block.BloodMagicBlocks;
+import wayoftime.bloodmagic.common.item.sigil.ISigil;
+import wayoftime.bloodmagic.common.item.sigil.ItemSigilHolding;
+
+public class ContainerHolding extends Container
+{
+	public final InventoryHolding inventoryHolding;
+	private final int PLAYER_INVENTORY_ROWS = 3;
+	private final int PLAYER_INVENTORY_COLUMNS = 9;
+	private final PlayerEntity player;
+
+	public ContainerHolding(int windowId, PlayerInventory playerInventory, PacketBuffer extraData)
+	{
+		this(windowId, playerInventory.player, playerInventory, new InventoryHolding(extraData.readItemStack()));
+	}
+
+	public ContainerHolding(int windowId, PlayerEntity player, PlayerInventory playerInventory, InventoryHolding inventoryHolding)
+	{
+		super(BloodMagicBlocks.HOLDING_CONTAINER.get(), windowId);
+		this.player = player;
+		this.inventoryHolding = inventoryHolding;
+		int currentSlotHeldIn = player.inventory.currentItem;
+		this.setup(playerInventory, currentSlotHeldIn);
+	}
+
+	public void setup(PlayerInventory inventory, int currentSlotHeldIn)
+	{
+		for (int columnIndex = 0; columnIndex < ItemSigilHolding.inventorySize; ++columnIndex)
+		{
+			this.addSlot(new SlotHolding(this, inventoryHolding, player, columnIndex, 8 + columnIndex * 36, 17));
+		}
+
+		for (int rowIndex = 0; rowIndex < PLAYER_INVENTORY_ROWS; ++rowIndex)
+		{
+			for (int columnIndex = 0; columnIndex < PLAYER_INVENTORY_COLUMNS; ++columnIndex)
+			{
+				this.addSlot(new Slot(player.inventory, columnIndex + rowIndex * 9 + 9, 8 + columnIndex * 18, 41 + rowIndex * 18));
+			}
+		}
+
+		for (int actionBarIndex = 0; actionBarIndex < PLAYER_INVENTORY_COLUMNS; ++actionBarIndex)
+		{
+			if (actionBarIndex == currentSlotHeldIn)
+			{
+				this.addSlot(new SlotDisabled(player.inventory, actionBarIndex, 8 + actionBarIndex * 18, 99));
+			} else
+			{
+				this.addSlot(new Slot(player.inventory, actionBarIndex, 8 + actionBarIndex * 18, 99));
+			}
+		}
+	}
+
+	@Override
+	public boolean canInteractWith(PlayerEntity entityPlayer)
+	{
+		return true;
+	}
+
+	@Override
+	public void onContainerClosed(PlayerEntity entityPlayer)
+	{
+		super.onContainerClosed(entityPlayer);
+
+		if (!entityPlayer.getEntityWorld().isRemote)
+		{
+			saveInventory(entityPlayer);
+		}
+	}
+
+	@Override
+	public void detectAndSendChanges()
+	{
+		super.detectAndSendChanges();
+
+		if (!player.getEntityWorld().isRemote)
+		{
+			saveInventory(player);
+		}
+	}
+
+	@Override
+	public ItemStack transferStackInSlot(PlayerEntity entityPlayer, int slotIndex)
+	{
+		ItemStack stack = ItemStack.EMPTY;
+		Slot slotObject = inventorySlots.get(slotIndex);
+		int slots = inventorySlots.size();
+
+		if (slotObject != null && slotObject.getHasStack())
+		{
+			ItemStack stackInSlot = slotObject.getStack();
+			stack = stackInSlot.copy();
+
+			if (stack.getItem() instanceof ISigil)
+			{
+				if (slotIndex < ItemSigilHolding.inventorySize)
+				{
+					if (!this.mergeItemStack(stackInSlot, ItemSigilHolding.inventorySize, slots, false))
+					{
+						return ItemStack.EMPTY;
+					}
+				} else if (!this.mergeItemStack(stackInSlot, 0, ItemSigilHolding.inventorySize, false))
+				{
+					return ItemStack.EMPTY;
+				}
+			} else if (stack.getItem() instanceof ItemSigilHolding)
+			{
+				if (slotIndex < ItemSigilHolding.inventorySize + (PLAYER_INVENTORY_ROWS * PLAYER_INVENTORY_COLUMNS))
+				{
+					if (!this.mergeItemStack(stackInSlot, ItemSigilHolding.inventorySize + (PLAYER_INVENTORY_ROWS * PLAYER_INVENTORY_COLUMNS), inventorySlots.size(), false))
+					{
+						return ItemStack.EMPTY;
+					}
+				} else if (!this.mergeItemStack(stackInSlot, ItemSigilHolding.inventorySize, ItemSigilHolding.inventorySize + (PLAYER_INVENTORY_ROWS * PLAYER_INVENTORY_COLUMNS), false))
+				{
+					return ItemStack.EMPTY;
+				}
+			}
+
+			if (stackInSlot.isEmpty())
+			{
+				slotObject.putStack(ItemStack.EMPTY);
+			} else
+			{
+				slotObject.onSlotChanged();
+			}
+
+			if (stackInSlot.getCount() == stack.getCount())
+			{
+				return ItemStack.EMPTY;
+			}
+
+			slotObject.onTake(player, stackInSlot);
+		}
+
+		return stack;
+	}
+
+	public void saveInventory(PlayerEntity entityPlayer)
+	{
+		inventoryHolding.onGuiSaved(entityPlayer);
+	}
+
+	private class SlotHolding extends Slot
+	{
+		private final PlayerEntity player;
+		private ContainerHolding containerHolding;
+
+		public SlotHolding(ContainerHolding containerHolding, IInventory inventory, PlayerEntity player, int slotIndex, int x, int y)
+		{
+			super(inventory, slotIndex, x, y);
+			this.player = player;
+			this.containerHolding = containerHolding;
+		}
+
+		@Override
+		public void onSlotChanged()
+		{
+			super.onSlotChanged();
+
+			if (EffectiveSide.get().isServer())
+			{
+				containerHolding.saveInventory(player);
+			}
+		}
+
+		@Override
+		public boolean isItemValid(ItemStack itemStack)
+		{
+			return itemStack.getItem() instanceof ISigil && !(itemStack.getItem() instanceof ItemSigilHolding);
+		}
+	}
+
+	private class SlotDisabled extends Slot
+	{
+		public SlotDisabled(IInventory inventory, int slotIndex, int x, int y)
+		{
+			super(inventory, slotIndex, x, y);
+		}
+
+		@Override
+		public boolean isItemValid(ItemStack itemStack)
+		{
+			return false;
+		}
+
+		@Override
+		public boolean canTakeStack(PlayerEntity player)
+		{
+			return false;
+		}
+	}
+}
diff --git a/src/main/java/wayoftime/bloodmagic/common/item/inventory/InventoryHolding.java b/src/main/java/wayoftime/bloodmagic/common/item/inventory/InventoryHolding.java
new file mode 100644
index 00000000..218cd152
--- /dev/null
+++ b/src/main/java/wayoftime/bloodmagic/common/item/inventory/InventoryHolding.java
@@ -0,0 +1,82 @@
+package wayoftime.bloodmagic.common.item.inventory;
+
+import java.util.UUID;
+
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.CompoundNBT;
+import wayoftime.bloodmagic.common.item.sigil.ISigil;
+import wayoftime.bloodmagic.common.item.sigil.ItemSigilHolding;
+import wayoftime.bloodmagic.util.Constants;
+import wayoftime.bloodmagic.util.Utils;
+
+public class InventoryHolding extends ItemInventory
+{
+//	protected ItemStack[] inventory;
+
+	public InventoryHolding(ItemStack itemStack)
+	{
+		super(itemStack, ItemSigilHolding.inventorySize, "SigilOfHolding");
+	}
+
+	public void onGuiSaved(PlayerEntity entityPlayer)
+	{
+		masterStack = findParentStack(entityPlayer);
+
+		if (!masterStack.isEmpty())
+		{
+			save();
+		}
+	}
+
+	public ItemStack findParentStack(PlayerEntity entityPlayer)
+	{
+		if (Utils.hasUUID(masterStack))
+		{
+			UUID parentStackUUID = new UUID(masterStack.getTag().getLong(Constants.NBT.MOST_SIG), masterStack.getTag().getLong(Constants.NBT.LEAST_SIG));
+			for (int i = 0; i < entityPlayer.inventory.getSizeInventory(); i++)
+			{
+				ItemStack itemStack = entityPlayer.inventory.getStackInSlot(i);
+
+				if (!itemStack.isEmpty() && Utils.hasUUID(itemStack))
+				{
+					if (itemStack.getTag().getLong(Constants.NBT.MOST_SIG) == parentStackUUID.getMostSignificantBits() && itemStack.getTag().getLong(Constants.NBT.LEAST_SIG) == parentStackUUID.getLeastSignificantBits())
+					{
+						return itemStack;
+					}
+				}
+			}
+		}
+
+		return ItemStack.EMPTY;
+	}
+
+	public void save()
+	{
+		CompoundNBT nbtTagCompound = masterStack.getTag();
+
+		if (nbtTagCompound == null)
+		{
+			nbtTagCompound = new CompoundNBT();
+
+			UUID uuid = UUID.randomUUID();
+			nbtTagCompound.putLong(Constants.NBT.MOST_SIG, uuid.getMostSignificantBits());
+			nbtTagCompound.putLong(Constants.NBT.LEAST_SIG, uuid.getLeastSignificantBits());
+		}
+
+		writeToNBT(nbtTagCompound);
+		masterStack.setTag(nbtTagCompound);
+	}
+
+	@Override
+	public boolean isItemValidForSlot(int slotIndex, ItemStack itemStack)
+	{
+		return itemStack.getItem() instanceof ISigil && !(itemStack.getItem() instanceof ItemSigilHolding);
+	}
+
+	@Override
+	public int getInventoryStackLimit()
+	{
+		return 1;
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/wayoftime/bloodmagic/common/item/inventory/ItemInventory.java b/src/main/java/wayoftime/bloodmagic/common/item/inventory/ItemInventory.java
new file mode 100644
index 00000000..2b739f8a
--- /dev/null
+++ b/src/main/java/wayoftime/bloodmagic/common/item/inventory/ItemInventory.java
@@ -0,0 +1,252 @@
+package wayoftime.bloodmagic.common.item.inventory;
+
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.inventory.IInventory;
+import net.minecraft.inventory.ItemStackHelper;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.CompoundNBT;
+import net.minecraft.util.NonNullList;
+import wayoftime.bloodmagic.util.Constants;
+import wayoftime.bloodmagic.util.helper.NBTHelper;
+
+public class ItemInventory implements IInventory
+{
+	protected int[] syncedSlots = new int[0];
+	protected ItemStack masterStack;
+	private NonNullList<ItemStack> inventory;
+	private int size;
+	private String name;
+
+	public ItemInventory(ItemStack masterStack, int size, String name)
+	{
+		this.inventory = NonNullList.withSize(size, ItemStack.EMPTY);
+		this.size = size;
+		this.name = name;
+		this.masterStack = masterStack;
+
+		if (!masterStack.isEmpty())
+			this.readFromStack(masterStack);
+	}
+
+	public void initializeInventory(ItemStack masterStack)
+	{
+		this.masterStack = masterStack;
+		this.clear();
+		this.readFromStack(masterStack);
+	}
+
+	private boolean isSyncedSlot(int slot)
+	{
+		for (int s : this.syncedSlots)
+		{
+			if (s == slot)
+			{
+				return true;
+			}
+		}
+		return false;
+	}
+
+	public void readFromNBT(CompoundNBT tagCompound)
+	{
+		this.inventory = NonNullList.withSize(this.getSizeInventory(), ItemStack.EMPTY);
+
+		ItemStackHelper.loadAllItems(tagCompound, this.inventory);
+	}
+
+	public void writeToNBT(CompoundNBT tagCompound)
+	{
+		ItemStackHelper.saveAllItems(tagCompound, this.inventory);
+//		ListNBT tags = new ListNBT();
+//
+//		for (int i = 0; i < inventory.size(); i++)
+//		{
+//			if ((!inventory.get(i).isEmpty()) && !isSyncedSlot(i))
+//			{
+//				CompoundNBT data = new CompoundNBT();
+//				data.putByte(Constants.NBT.SLOT, (byte) i);
+//				inventory.get(i).write(data);
+//				tags.add(data);
+////				tags.appendTag(data);
+//			}
+//		}
+//
+//		tagCompound.put(Constants.NBT.ITEMS, tags);
+	}
+
+	public void readFromStack(ItemStack masterStack)
+	{
+		if (masterStack != null)
+		{
+			NBTHelper.checkNBT(masterStack);
+			CompoundNBT tag = masterStack.getTag();
+			readFromNBT(tag.getCompound(Constants.NBT.ITEM_INVENTORY));
+		}
+	}
+
+	public void writeToStack(ItemStack masterStack)
+	{
+		if (masterStack != null)
+		{
+			NBTHelper.checkNBT(masterStack);
+			CompoundNBT tag = masterStack.getTag();
+			CompoundNBT invTag = new CompoundNBT();
+			writeToNBT(invTag);
+			tag.put(Constants.NBT.ITEM_INVENTORY, invTag);
+		}
+	}
+
+	@Override
+	public int getSizeInventory()
+	{
+		return size;
+	}
+
+	@Override
+	public ItemStack getStackInSlot(int index)
+	{
+		return inventory.get(index);
+	}
+
+	@Override
+	public ItemStack decrStackSize(int index, int count)
+	{
+		if (!inventory.get(index).isEmpty())
+		{
+//            if (!worldObj.isRemote)
+//                worldObj.markBlockForUpdate(this.pos);
+
+			if (inventory.get(index).getCount() <= count)
+			{
+				ItemStack itemStack = inventory.get(index);
+				inventory.set(index, ItemStack.EMPTY);
+				markDirty();
+				return itemStack;
+			}
+
+			ItemStack itemStack = inventory.get(index).split(count);
+			if (inventory.get(index).isEmpty())
+				inventory.set(index, ItemStack.EMPTY);
+
+			markDirty();
+			return itemStack;
+		}
+
+		return null;
+	}
+
+	@Override
+	public ItemStack removeStackFromSlot(int slot)
+	{
+		if (!inventory.get(slot).isEmpty())
+		{
+			ItemStack itemStack = inventory.get(slot);
+			setInventorySlotContents(slot, ItemStack.EMPTY);
+			return itemStack;
+		}
+		return ItemStack.EMPTY;
+	}
+
+	@Override
+	public void setInventorySlotContents(int slot, ItemStack stack)
+	{
+		inventory.set(slot, stack);
+		if (stack.getCount() > getInventoryStackLimit())
+			stack.setCount(getInventoryStackLimit());
+		markDirty();
+//        if (!worldObj.isRemote)
+//            worldObj.markBlockForUpdate(this.pos);
+	}
+
+	@Override
+	public int getInventoryStackLimit()
+	{
+		return 64;
+	}
+
+	@Override
+	public boolean isUsableByPlayer(PlayerEntity player)
+	{
+		return true;
+	}
+
+	@Override
+	public void openInventory(PlayerEntity player)
+	{
+
+	}
+
+	@Override
+	public void closeInventory(PlayerEntity player)
+	{
+
+	}
+
+	@Override
+	public boolean isItemValidForSlot(int index, ItemStack stack)
+	{
+		return true;
+	}
+
+//	@Override
+//	public int getField(int id)
+//	{
+//		return 0;
+//	}
+//
+//	@Override
+//	public void setField(int id, int value)
+//	{
+//
+//	}
+//
+//	@Override
+//	public int getFieldCount()
+//	{
+//		return 0;
+//	}
+
+	@Override
+	public void clear()
+	{
+		this.inventory = NonNullList.withSize(getSizeInventory(), ItemStack.EMPTY);
+	}
+
+//	@Override
+//	public String getName()
+//	{
+//		return name;
+//	}
+//
+//	@Override
+//	public boolean hasCustomName()
+//	{
+//		return false;
+//	}
+//
+//	@Override
+//	public ITextComponent getDisplayName()
+//	{
+//		return new StringTextComponent(getName());
+//	}
+
+	@Override
+	public void markDirty()
+	{
+		if (masterStack != null)
+		{
+			this.writeToStack(masterStack);
+		}
+	}
+
+	@Override
+	public boolean isEmpty()
+	{
+		return false;
+	}
+
+	public boolean canInventoryBeManipulated()
+	{
+		return masterStack != null;
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/wayoftime/bloodmagic/common/item/sigil/ItemSigilHolding.java b/src/main/java/wayoftime/bloodmagic/common/item/sigil/ItemSigilHolding.java
new file mode 100644
index 00000000..4d48ada4
--- /dev/null
+++ b/src/main/java/wayoftime/bloodmagic/common/item/sigil/ItemSigilHolding.java
@@ -0,0 +1,385 @@
+package wayoftime.bloodmagic.common.item.sigil;
+
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+import net.minecraft.client.util.ITooltipFlag;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.entity.player.PlayerInventory;
+import net.minecraft.entity.player.ServerPlayerEntity;
+import net.minecraft.inventory.ItemStackHelper;
+import net.minecraft.inventory.container.Container;
+import net.minecraft.inventory.container.INamedContainerProvider;
+import net.minecraft.item.ItemStack;
+import net.minecraft.item.ItemUseContext;
+import net.minecraft.nbt.CompoundNBT;
+import net.minecraft.util.ActionResult;
+import net.minecraft.util.ActionResultType;
+import net.minecraft.util.Hand;
+import net.minecraft.util.NonNullList;
+import net.minecraft.util.math.MathHelper;
+import net.minecraft.util.text.ITextComponent;
+import net.minecraft.util.text.StringTextComponent;
+import net.minecraft.util.text.TranslationTextComponent;
+import net.minecraft.world.World;
+import net.minecraftforge.api.distmarker.Dist;
+import net.minecraftforge.api.distmarker.OnlyIn;
+import net.minecraftforge.fml.network.NetworkHooks;
+import wayoftime.bloodmagic.api.compat.IAltarReader;
+import wayoftime.bloodmagic.client.key.IKeybindable;
+import wayoftime.bloodmagic.client.key.KeyBindings;
+import wayoftime.bloodmagic.common.item.IBindable;
+import wayoftime.bloodmagic.common.item.inventory.ContainerHolding;
+import wayoftime.bloodmagic.common.item.inventory.InventoryHolding;
+import wayoftime.bloodmagic.core.data.Binding;
+import wayoftime.bloodmagic.util.Constants;
+import wayoftime.bloodmagic.util.Utils;
+import wayoftime.bloodmagic.util.helper.NBTHelper;
+import wayoftime.bloodmagic.util.helper.PlayerHelper;
+
+public class ItemSigilHolding extends ItemSigilBase implements IKeybindable, IAltarReader, ISigil.Holding, INamedContainerProvider
+{
+	public static final int inventorySize = 5;
+
+	public ItemSigilHolding()
+	{
+		super("holding");
+	}
+
+	@Override
+	public void onKeyPressed(ItemStack stack, PlayerEntity player, KeyBindings key, boolean showInChat)
+	{
+		if (stack == player.getHeldItemMainhand() && stack.getItem() instanceof ItemSigilHolding && key.equals(KeyBindings.OPEN_HOLDING))
+		{
+			Utils.setUUID(stack);
+
+			if (player instanceof ServerPlayerEntity)
+			{
+				NetworkHooks.openGui((ServerPlayerEntity) player, this, buf -> buf.writeItemStack(stack, false));
+			}
+//			player.openGui(BloodMagic.instance, Constants.Gui.SIGIL_HOLDING_GUI, player.getEntityWorld(), (int) player.posX, (int) player.posY, (int) player.posZ);
+		}
+	}
+
+//	@Override
+//	public String getHighlightTip(ItemStack stack, String displayName)
+//	{
+//		List<ItemStack> inv = getInternalInventory(stack);
+//		int currentSlot = getCurrentItemOrdinal(stack);
+//		ItemStack item = inv.get(currentSlot);
+//
+//		if (item.isEmpty())
+//			return displayName;
+//		else
+//			return TextHelper.localizeEffect("item.bloodmagic.sigil.holding.display", displayName, item.getDisplayName());
+//	}
+
+	@Override
+	@OnlyIn(Dist.CLIENT)
+	public void addInformation(ItemStack stack, World world, List<ITextComponent> tooltip, ITooltipFlag flag)
+	{
+		super.addInformation(stack, world, tooltip, flag);
+		tooltip.add(new TranslationTextComponent("tooltip.bloodmagic.sigil.holding.press", KeyBindings.OPEN_HOLDING.getKey()));
+
+		if (!stack.hasTag())
+			return;
+
+		List<ItemStack> inv = getInternalInventory(stack);
+		int currentSlot = getCurrentItemOrdinal(stack);
+		ItemStack item = inv.get(currentSlot);
+
+		for (int i = 0; i < inventorySize; i++)
+		{
+			ItemStack invStack = inv.get(i);
+			if (!invStack.isEmpty())
+				if (!item.isEmpty() && invStack == item)
+					tooltip.add(new TranslationTextComponent("tooltip.bloodmagic.sigil.holding.sigilInSlot", i + 1, "&o&n" + invStack.getDisplayName()));
+				else
+					tooltip.add(new TranslationTextComponent("tooltip.bloodmagic.sigil.holding.sigilInSlot", i + 1, invStack.getDisplayName()));
+		}
+	}
+
+	@Override
+	public ActionResultType onItemUse(ItemUseContext context)
+	{
+//		BlockPos pos = context.getPos();
+//		Direction facing = context.getFace();
+//		pos = pos.offset(facing);
+		PlayerEntity player = context.getPlayer();
+		Hand hand = context.getHand();
+		ItemStack stack = player.getHeldItem(hand);
+
+//		ItemStack stack = player.getHeldItem(hand);
+		if (PlayerHelper.isFakePlayer(player))
+			return ActionResultType.FAIL;
+
+		int currentSlot = getCurrentItemOrdinal(stack);
+		NonNullList<ItemStack> inv = getInternalInventory(stack);
+		ItemStack itemUsing = inv.get(currentSlot);
+
+		if (itemUsing.isEmpty() || ((IBindable) itemUsing.getItem()).getBinding(itemUsing) == null)
+			return ActionResultType.PASS;
+
+		ActionResultType result = itemUsing.getItem().onItemUse(context);
+		saveInventory(stack, inv);
+
+		return result;
+	}
+
+	@Override
+	public ActionResult<ItemStack> onItemRightClick(World world, PlayerEntity player, Hand hand)
+	{
+		ItemStack stack = player.getHeldItem(hand);
+		if (PlayerHelper.isFakePlayer(player))
+			return ActionResult.resultFail(stack);
+
+		int currentSlot = getCurrentItemOrdinal(stack);
+		NonNullList<ItemStack> inv = getInternalInventory(stack);
+		ItemStack itemUsing = inv.get(currentSlot);
+
+		if (itemUsing.isEmpty() || ((IBindable) itemUsing.getItem()).getBinding(itemUsing) == null)
+			return ActionResult.resultPass(stack);
+
+		itemUsing.getItem().onItemRightClick(world, player, hand);
+
+		saveInventory(stack, inv);
+
+		return ActionResult.resultPass(stack);
+	}
+
+	@Nonnull
+	@Override
+	public ItemStack getHeldItem(ItemStack holdingStack, PlayerEntity player)
+	{
+		return getInternalInventory(holdingStack).get(getCurrentItemOrdinal(holdingStack));
+	}
+
+	public void saveInventory(ItemStack itemStack, NonNullList<ItemStack> inventory)
+	{
+		CompoundNBT itemTag = itemStack.getTag();
+
+		if (itemTag == null)
+			itemStack.setTag(itemTag = new CompoundNBT());
+
+		ItemStackHelper.saveAllItems(itemTag, inventory);
+
+//		CompoundNBT inventoryTag = new CompoundNBT();
+//		ListNBT itemList = new ListNBT();
+//
+//		for (int i = 0; i < inventorySize; i++)
+//		{
+//			if (!inventory.get(i).isEmpty())
+//			{
+//				CompoundNBT tag = new CompoundNBT();
+//				tag.putByte(Constants.NBT.SLOT, (byte) i);
+//				inventory.get(i).writeToNBT(tag);
+//				itemList.appendTag(tag);
+//			}
+//		}
+//
+//		inventoryTag.put(Constants.NBT.ITEMS, itemList);
+//		itemTag.put(Constants.NBT.ITEM_INVENTORY, inventoryTag);
+	}
+
+	@Override
+	public void inventoryTick(ItemStack stack, World world, Entity entity, int itemSlot, boolean isSelected)
+	{
+		if (stack.hasTag())
+			tickInternalInventory(stack, world, entity, itemSlot, isSelected);
+	}
+
+	public void tickInternalInventory(ItemStack itemStack, World world, Entity entity, int itemSlot, boolean isSelected)
+	{
+		for (ItemStack stack : getInternalInventory(itemStack))
+		{
+			if (stack.isEmpty() || !(stack.getItem() instanceof IBindable) || !(stack.getItem() instanceof ISigil))
+				continue;
+
+			Binding binding = ((IBindable) stack.getItem()).getBinding(stack);
+			if (binding == null)
+				continue;
+
+			stack.getItem().inventoryTick(stack, world, entity, itemSlot, isSelected);
+		}
+	}
+
+//	@Override
+//	public void gatherVariants(@Nonnull Int2ObjectMap<String> variants)
+//	{
+//		// No-op - Just here to stop the super from running since we're using a mesh
+//		// provider
+//	}
+
+//	@Override
+//	public ItemMeshDefinition getMeshDefinition()
+//	{
+//		return stack -> {
+//			if (stack.hasTag() && stack.getTag().hasKey("color"))
+//				return new ModelResourceLocation(getRegistryName(), "type=color");
+//			return new ModelResourceLocation(getRegistryName(), "type=normal");
+//		};
+//	}
+//
+//	@Override
+//	public void gatherVariants(Consumer<String> variants)
+//	{
+//		variants.accept("type=normal");
+//		variants.accept("type=color");
+//	}
+
+	public static int next(int mode)
+	{
+		int index = mode + 1;
+
+		if (index >= inventorySize)
+		{
+			index = 0;
+		}
+
+		return index;
+	}
+
+	public static int prev(int mode)
+	{
+		int index = mode - 1;
+
+		if (index < 0)
+		{
+			index = inventorySize;
+		}
+
+		return index;
+	}
+
+	private static void initModeTag(ItemStack stack)
+	{
+		if (!stack.hasTag())
+		{
+			stack = NBTHelper.checkNBT(stack);
+			stack.getTag().putInt(Constants.NBT.CURRENT_SIGIL, inventorySize);
+		}
+	}
+
+	public static ItemStack getItemStackInSlot(ItemStack itemStack, int slot)
+	{
+		if (itemStack.getItem() instanceof ItemSigilHolding)
+		{
+			List<ItemStack> inv = getInternalInventory(itemStack);
+			if (inv != null)
+				return inv.get(slot == 5 ? 4 : slot);
+			else
+				return ItemStack.EMPTY;
+		}
+
+		return ItemStack.EMPTY;
+	}
+
+	public static int getCurrentItemOrdinal(ItemStack stack)
+	{
+		if (stack.getItem() instanceof ItemSigilHolding)
+		{
+			initModeTag(stack);
+			int currentSigil = stack.getTag().getInt(Constants.NBT.CURRENT_SIGIL);
+			currentSigil = MathHelper.clamp(currentSigil, 0, inventorySize - 1);
+			return currentSigil;
+		}
+
+		return 0;
+	}
+
+	public static NonNullList<ItemStack> getInternalInventory(ItemStack stack)
+	{
+		initModeTag(stack);
+		CompoundNBT tagCompound = stack.getTag();
+
+		if (tagCompound == null)
+		{
+			return NonNullList.withSize(inventorySize, ItemStack.EMPTY);
+		}
+
+		NonNullList<ItemStack> inv = NonNullList.withSize(inventorySize, ItemStack.EMPTY);
+
+		ItemStackHelper.loadAllItems(tagCompound, inv);
+
+//		CompoundNBT inventoryTag = tagCompound.getCompound(Constants.NBT.ITEM_INVENTORY);
+//		ListNBT tagList = inventoryTag.getList(Constants.NBT.ITEMS, 10);
+//
+//		if (tagList.isEmpty())
+//		{
+//			return NonNullList.withSize(inventorySize, ItemStack.EMPTY);
+//		}
+//
+//		List<ItemStack> inv = NonNullList.withSize(inventorySize, ItemStack.EMPTY);
+//
+//		for (int i = 0; i < tagList.tagCount(); i++)
+//		{
+//			CompoundNBT data = tagList.getCompoundTagAt(i);
+//			byte j = data.getByte(Constants.NBT.SLOT);
+//
+//			if (j >= 0 && j < inv.size())
+//			{
+//				inv.set(j, new ItemStack(data));
+//			}
+//		}
+
+		return inv;
+	}
+
+	public static void cycleToNextSigil(ItemStack itemStack, int mode)
+	{
+		if (itemStack.getItem() instanceof ItemSigilHolding)
+		{
+			initModeTag(itemStack);
+
+			int index = mode;
+			if (mode == 120 || mode == -120)
+			{
+				int currentIndex = getCurrentItemOrdinal(itemStack);
+				ItemStack currentItemStack = getItemStackInSlot(itemStack, currentIndex);
+				if (currentItemStack.isEmpty())
+					return;
+				if (mode < 0)
+				{
+					index = next(currentIndex);
+					currentItemStack = getItemStackInSlot(itemStack, index);
+
+					while (currentItemStack.isEmpty())
+					{
+						index = next(index);
+						currentItemStack = getItemStackInSlot(itemStack, index);
+					}
+				} else
+				{
+					index = prev(currentIndex);
+					currentItemStack = getItemStackInSlot(itemStack, index);
+
+					while (currentItemStack.isEmpty())
+					{
+						index = prev(index);
+						currentItemStack = getItemStackInSlot(itemStack, index);
+					}
+				}
+			}
+
+			itemStack.getTag().putInt(Constants.NBT.CURRENT_SIGIL, index);
+		}
+	}
+
+	@Override
+	public Container createMenu(int p_createMenu_1_, PlayerInventory p_createMenu_2_, PlayerEntity player)
+	{
+		// TODO Auto-generated method stub
+		assert player.getEntityWorld() != null;
+		return new ContainerHolding(p_createMenu_1_, player, p_createMenu_2_, new InventoryHolding(player.getHeldItemMainhand()));
+	}
+
+	@Override
+	public ITextComponent getDisplayName()
+	{
+		// TODO Auto-generated method stub
+		return new StringTextComponent("Sigil of Holding");
+	}
+
+}
\ No newline at end of file
diff --git a/src/main/java/wayoftime/bloodmagic/network/BloodMagicPacketHandler.java b/src/main/java/wayoftime/bloodmagic/network/BloodMagicPacketHandler.java
index 089ae21d..d33f0ca2 100644
--- a/src/main/java/wayoftime/bloodmagic/network/BloodMagicPacketHandler.java
+++ b/src/main/java/wayoftime/bloodmagic/network/BloodMagicPacketHandler.java
@@ -17,6 +17,8 @@ public class BloodMagicPacketHandler extends BasePacketHandler
 		registerServerToClient(ARCTanksPacket.class, ARCTanksPacket::encode, ARCTanksPacket::decode, ARCTanksPacket::handle);
 		registerServerToClient(DemonAuraClientPacket.class, DemonAuraClientPacket::encode, DemonAuraClientPacket::decode, DemonAuraClientPacket::handle);
 		registerServerToClient(SetClientHealthPacket.class, SetClientHealthPacket::encode, SetClientHealthPacket::decode, SetClientHealthPacket::handle);
+
+		registerClientToServer(KeyProcessorPacket.class, KeyProcessorPacket::encode, KeyProcessorPacket::decode, KeyProcessorPacket::handle);
 //		INSTANCE.registerMessage(id, messageType, encoder, decoder, messageConsumer);
 //		INSTANCE.registerMessage(ChatUtil.PacketNoSpamChat.Handler.class, ChatUtil.PacketNoSpamChat.class, 0, Side.CLIENT);
 //		INSTANCE.registerMessage(ItemRouterButtonPacketProcessor.class, ItemRouterButtonPacketProcessor.class, 1, Side.SERVER);
diff --git a/src/main/java/wayoftime/bloodmagic/network/KeyProcessorPacket.java b/src/main/java/wayoftime/bloodmagic/network/KeyProcessorPacket.java
new file mode 100644
index 00000000..02f30330
--- /dev/null
+++ b/src/main/java/wayoftime/bloodmagic/network/KeyProcessorPacket.java
@@ -0,0 +1,71 @@
+package wayoftime.bloodmagic.network;
+
+import java.util.function.Supplier;
+
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.item.ItemStack;
+import net.minecraft.network.PacketBuffer;
+import net.minecraftforge.fml.network.NetworkEvent.Context;
+import wayoftime.bloodmagic.client.key.IKeybindable;
+import wayoftime.bloodmagic.client.key.KeyBindings;
+
+public class KeyProcessorPacket
+{
+	public int keyId;
+	public boolean showInChat;
+
+	public KeyProcessorPacket(int keyId, boolean showInChat)
+	{
+		this.keyId = keyId;
+		this.showInChat = showInChat;
+	}
+
+	public KeyProcessorPacket(KeyBindings key, boolean showInChat)
+	{
+		this(key.ordinal(), showInChat);
+	}
+
+	public static void encode(KeyProcessorPacket pkt, PacketBuffer buf)
+	{
+		buf.writeInt(pkt.keyId);
+		buf.writeBoolean(pkt.showInChat);
+	}
+
+	public static KeyProcessorPacket decode(PacketBuffer buf)
+	{
+		KeyProcessorPacket pkt = new KeyProcessorPacket(buf.readInt(), buf.readBoolean());
+
+		return pkt;
+	}
+
+	public static void handle(KeyProcessorPacket message, Supplier<Context> context)
+	{
+		context.get().enqueueWork(() -> sendKeyToServer(message, context.get().getSender()));
+		context.get().setPacketHandled(true);
+	}
+
+	public static void sendKeyToServer(KeyProcessorPacket msg, PlayerEntity playerEntity)
+	{
+		System.out.println("Hoiiiii");
+		if (playerEntity != null)
+		{
+			ItemStack heldStack = playerEntity.getHeldItemMainhand();
+			if (heldStack.getItem() instanceof IKeybindable)
+			{
+				if (msg.keyId < 0 || msg.keyId >= KeyBindings.values().length)
+				{
+					return;
+				}
+				KeyBindings key = KeyBindings.values()[msg.keyId];
+
+				((IKeybindable) heldStack.getItem()).onKeyPressed(heldStack, playerEntity, key, msg.showInChat);
+			}
+		}
+	}
+
+//	@OnlyIn(Dist.CLIENT)
+//	public static void updateClientHolder(DemonWillHolder holder)
+//	{
+//		ClientHandler.currentAura = holder;
+//	}
+}
diff --git a/src/main/java/wayoftime/bloodmagic/util/Utils.java b/src/main/java/wayoftime/bloodmagic/util/Utils.java
index d47d024c..cfcf2328 100644
--- a/src/main/java/wayoftime/bloodmagic/util/Utils.java
+++ b/src/main/java/wayoftime/bloodmagic/util/Utils.java
@@ -2,6 +2,7 @@ package wayoftime.bloodmagic.util;
 
 import java.util.List;
 import java.util.Locale;
+import java.util.UUID;
 
 import javax.annotation.Nullable;
 
@@ -38,6 +39,7 @@ import net.minecraftforge.items.wrapper.PlayerMainInvWrapper;
 import net.minecraftforge.items.wrapper.SidedInvWrapper;
 import wayoftime.bloodmagic.api.compat.IDemonWillViewer;
 import wayoftime.bloodmagic.tile.TileInventory;
+import wayoftime.bloodmagic.util.helper.NBTHelper;
 
 public class Utils
 {
@@ -731,4 +733,31 @@ public class Utils
 //			}
 //		}
 //	}
+
+	public static boolean hasUUID(ItemStack stack)
+	{
+		return stack.hasTag() && stack.getTag().contains(Constants.NBT.MOST_SIG) && stack.getTag().contains(Constants.NBT.LEAST_SIG);
+	}
+
+	public static UUID getUUID(ItemStack stack)
+	{
+		if (!hasUUID(stack))
+		{
+			return null;
+		}
+
+		return new UUID(stack.getTag().getLong(Constants.NBT.MOST_SIG), stack.getTag().getLong(Constants.NBT.LEAST_SIG));
+	}
+
+	public static void setUUID(ItemStack stack)
+	{
+		stack = NBTHelper.checkNBT(stack);
+
+		if (!stack.getTag().contains(Constants.NBT.MOST_SIG) && !stack.getTag().contains(Constants.NBT.LEAST_SIG))
+		{
+			UUID itemUUID = UUID.randomUUID();
+			stack.getTag().putLong(Constants.NBT.MOST_SIG, itemUUID.getMostSignificantBits());
+			stack.getTag().putLong(Constants.NBT.LEAST_SIG, itemUUID.getLeastSignificantBits());
+		}
+	}
 }