diff --git a/Minecraft.Server.FourKit/Material/Attachable.cs b/Minecraft.Server.FourKit/Material/Attachable.cs
new file mode 100644
index 000000000..1ce852f4f
--- /dev/null
+++ b/Minecraft.Server.FourKit/Material/Attachable.cs
@@ -0,0 +1,7 @@
+using Minecraft.Server.FourKit.Block;
+namespace Minecraft.Server.FourKit.material;
+
+public interface Attachable : Directional
+{
+ public BlockFace getAttachedFace();
+}
\ No newline at end of file
diff --git a/Minecraft.Server.FourKit/Material/Directional.cs b/Minecraft.Server.FourKit/Material/Directional.cs
new file mode 100644
index 000000000..b3ce21004
--- /dev/null
+++ b/Minecraft.Server.FourKit/Material/Directional.cs
@@ -0,0 +1,8 @@
+using Minecraft.Server.FourKit.Block;
+namespace Minecraft.Server.FourKit.material;
+
+public interface Directional
+{
+ public void setFacingDirection(BlockFace face);
+ public BlockFace getFacing();
+}
\ No newline at end of file
diff --git a/Minecraft.Server.FourKit/Material/MaterialData.cs b/Minecraft.Server.FourKit/Material/MaterialData.cs
new file mode 100644
index 000000000..54d701480
--- /dev/null
+++ b/Minecraft.Server.FourKit/Material/MaterialData.cs
@@ -0,0 +1,48 @@
+namespace Minecraft.Server.FourKit.material;
+
+public class MaterialData
+{
+ private readonly int _typeId;
+ private byte _data = 0;
+
+ public MaterialData(int typeId) : this(typeId, (byte)0) { }
+
+ public MaterialData(Material type) : this((int)type, (byte)0) { }
+
+ public MaterialData(int typeId, byte data)
+ {
+ _typeId = typeId;
+ _data = data;
+ }
+
+ public MaterialData(Material type, byte data) : this((int)type, data) { }
+
+ ///
+ /// Gets the raw data in this material
+ ///
+ /// raw data
+ public byte getData() => _data;
+
+ ///
+ /// Sets the raw data of this material
+ ///
+ /// New raw data
+ public void setData(byte data) {
+ _data = data;
+ }
+
+ ///
+ /// Gets the Material that this MaterialData represents
+ ///
+ /// Material represented by this MaterialData
+ public Material getItemType() => (Material)_typeId;
+
+ ///
+ /// Gets the Material Id that this MaterialData represents
+ ///
+ /// Material Id represented by this MaterialData
+ public int getItemTypeId() => _typeId;
+
+
+ public override string ToString() => $"{getItemType()}({getData()})";
+}
\ No newline at end of file
diff --git a/Minecraft.Server.FourKit/Material/Sign.cs b/Minecraft.Server.FourKit/Material/Sign.cs
new file mode 100644
index 000000000..05cb0fcf5
--- /dev/null
+++ b/Minecraft.Server.FourKit/Material/Sign.cs
@@ -0,0 +1,123 @@
+using Minecraft.Server.FourKit.Block;
+
+namespace Minecraft.Server.FourKit.material;
+
+public class Sign : MaterialData, Attachable
+{
+ public Sign() : base((int)Material.SIGN_POST) { }
+
+ public Sign(int typeId) : base(typeId) { }
+
+ public Sign(Material type) : base(type) { }
+
+ public Sign(int typeId, byte data) : base(typeId, data) { }
+
+ public Sign(Material type, byte data) : base(type, data) { }
+
+ ///
+ /// Check if this sign is attached to a wall
+ ///
+ /// true if this sign is attached to a wall, false if set on top of
+ /// a block
+ public bool isWallSign()
+ {
+ return getItemType() == Material.WALL_SIGN;
+ }
+
+ ///
+ /// Gets the face that this block is attached on
+ ///
+ /// BlockFace attached to
+ public BlockFace getAttachedFace()
+ {
+ if (!isWallSign())
+ {
+ return BlockFace.DOWN;
+ }
+
+ return getData() switch{
+ 0x2 => BlockFace.SOUTH,
+ 0x3 => BlockFace.NORTH,
+ 0x4 => BlockFace.EAST,
+ 0x5 => BlockFace.WEST,
+ _ => BlockFace.SELF
+ };
+ }
+
+ ///
+ /// Gets the direction that this sign is currently facing
+ ///
+ /// BlockFace indicating where this sign is facing
+ public BlockFace getFacing()
+ {
+ if (isWallSign())
+ {
+ return getAttachedFace().getOppositeFace();
+ }
+
+ return getData() switch
+ {
+ 0x0 => BlockFace.SOUTH,
+ 0x1 => BlockFace.SOUTH_SOUTH_WEST,
+ 0x2 => BlockFace.SOUTH_WEST,
+ 0x3 => BlockFace.WEST_SOUTH_WEST,
+ 0x4 => BlockFace.WEST,
+ 0x5 => BlockFace.WEST_NORTH_WEST,
+ 0x6 => BlockFace.NORTH_WEST,
+ 0x7 => BlockFace.NORTH_NORTH_WEST,
+ 0x8 => BlockFace.NORTH,
+ 0x9 => BlockFace.NORTH_NORTH_EAST,
+ 0xA => BlockFace.NORTH_EAST,
+ 0xB => BlockFace.EAST_NORTH_EAST,
+ 0xC => BlockFace.EAST,
+ 0xD => BlockFace.EAST_SOUTH_EAST,
+ 0xE => BlockFace.SOUTH_EAST,
+ 0xF => BlockFace.SOUTH_SOUTH_EAST,
+ _ => BlockFace.SELF
+ };
+ }
+
+ ///
+ /// Sets the direction that this sign is currently facing
+ ///
+ /// BlockFace Direction to point
+ public void setFacingDirection(BlockFace face)
+ {
+ byte data = (byte)(
+ isWallSign()
+ ? face switch
+ {
+ BlockFace.NORTH => 0x2,
+ BlockFace.SOUTH => 0x3,
+ BlockFace.WEST => 0x4,
+ _ => 0x5 // EAST/default
+ }
+ : face switch
+ {
+ BlockFace.SOUTH => 0x0,
+ BlockFace.SOUTH_SOUTH_WEST => 0x1,
+ BlockFace.SOUTH_WEST => 0x2,
+ BlockFace.WEST_SOUTH_WEST => 0x3,
+ BlockFace.WEST => 0x4,
+ BlockFace.WEST_NORTH_WEST => 0x5,
+ BlockFace.NORTH_WEST => 0x6,
+ BlockFace.NORTH_NORTH_WEST => 0x7,
+ BlockFace.NORTH => 0x8,
+ BlockFace.NORTH_NORTH_EAST => 0x9,
+ BlockFace.NORTH_EAST => 0xA,
+ BlockFace.EAST_NORTH_EAST => 0xB,
+ BlockFace.EAST => 0xC,
+ BlockFace.EAST_SOUTH_EAST => 0xD,
+ BlockFace.SOUTH_SOUTH_EAST => 0xF,
+ _ => 0xE // SOUTH_EAST/default
+ });
+
+ setData(data);
+ }
+
+ public override string ToString()
+ {
+ return $"{base.ToString()} facing {getFacing()}";
+ }
+
+}
\ No newline at end of file