diff --git a/api/.flattened-pom.xml b/api/.flattened-pom.xml
index d5b6fab65772a7b826f65c5fcb0136c0ba524d21..5e652fa075de057d83b65af0230ff16b8325842c 100644
--- a/api/.flattened-pom.xml
+++ b/api/.flattened-pom.xml
@@ -4,12 +4,12 @@
   <modelVersion>4.0.0</modelVersion>
   <groupId>io.lumine</groupId>
   <artifactId>MCCosmetics-API</artifactId>
-  <version>0.8.0-SNAPSHOT</version>
+  <version>0.11.0</version>
   <dependencies>
     <dependency>
       <groupId>io.lumine</groupId>
       <artifactId>LumineUtils</artifactId>
-      <version>1.18-SNAPSHOT</version>
+      <version>1.19-SNAPSHOT</version>
       <scope>provided</scope>
     </dependency>
     <dependency>
@@ -38,10 +38,6 @@
       <id>lumine</id>
       <url>https://mvn.lumine.io/repository/maven/</url>
     </repository>
-    <repository>
-      <id>sk89q-repo</id>
-      <url>https://maven.sk89q.com/repo/</url>
-    </repository>
     <repository>
       <id>md_5-snapshots</id>
       <url>https://repo.md-5.net/content/repositories/snapshots/</url>
@@ -50,10 +46,6 @@
       <id>placeholderapi</id>
       <url>https://repo.extendedclip.com/content/repositories/placeholderapi/</url>
     </repository>
-    <repository>
-      <id>filoghost-repo</id>
-      <url>https://ci.filoghost.me/plugin/repository/everything/</url>
-    </repository>
     <repository>
       <id>papermc</id>
       <url>https://papermc.io/repo/repository/maven-public/</url>
diff --git a/dist/pom.xml b/dist/pom.xml
index b935380b6a9388cd9a41d30e2d726ce58ba1d160..17ee3407051e728dd48ab7f57d0c8078105ea214 100644
--- a/dist/pom.xml
+++ b/dist/pom.xml
@@ -64,6 +64,12 @@
             <version>${project.parent.version}</version>
             <scope>compile</scope>
         </dependency>
+        <dependency>
+            <groupId>io.lumine</groupId>
+            <artifactId>MCCosmetics-v1_19_R2</artifactId>
+            <version>${project.parent.version}</version>
+            <scope>compile</scope>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/plugin/pom.xml b/plugin/pom.xml
index b2d2bc695f94c92c965fe38fccf70305f627acf4..e6e2853b3c6b5a232a0597a408141d3149222b88 100644
--- a/plugin/pom.xml
+++ b/plugin/pom.xml
@@ -82,13 +82,9 @@
     </build>
     <repositories>
         <repository>
-            <id>nexus</id>
+            <id>lumine</id>
             <url>https://mvn.lumine.io/repository/maven/</url>
         </repository>
-        <repository>
-            <id>lumine-public</id>
-            <url>https://mvn.lumine.io/repository/maven-public/</url>
-        </repository>
         <repository>
             <id>spigot-repo</id>
             <url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
@@ -113,10 +109,6 @@
                 <updatePolicy>never</updatePolicy>
             </releases>
         </repository>  -->
-        <repository>
-            <id>sk89q-repo</id>
-            <url>https://maven.sk89q.com/repo/</url>
-        </repository>
         <repository>
 		    <id>md_5-public</id>
 		    <url>https://repo.md-5.net/content/groups/public/</url>
@@ -129,10 +121,6 @@
             <id>placeholderapi</id>
             <url>https://repo.extendedclip.com/content/repositories/placeholderapi/</url>
         </repository>
-        <repository>
-            <id>filoghost-repo</id>
-            <url>https://ci.filoghost.me/plugin/repository/everything/</url>
-        </repository>
         <repository>
             <id>CodeMC</id>
             <url>https://repo.codemc.org/repository/maven-public</url>
@@ -187,7 +175,7 @@
         <dependency>
             <groupId>com.ticxo.playeranimator</groupId>
             <artifactId>PlayerAnimator</artifactId>
-            <version>R1.2.4</version>
+            <version>R1.2.5</version>
         </dependency>
 
         <!-- Spigot API -->
diff --git a/plugin/src/main/java/io/lumine/cosmetics/menus/SelectColorContext.java b/plugin/src/main/java/io/lumine/cosmetics/menus/SelectColorContext.java
new file mode 100644
index 0000000000000000000000000000000000000000..27067c6e70806012f78fef8b24c762abc8a75827
--- /dev/null
+++ b/plugin/src/main/java/io/lumine/cosmetics/menus/SelectColorContext.java
@@ -0,0 +1,17 @@
+package io.lumine.cosmetics.menus;
+
+import io.lumine.cosmetics.api.cosmetics.Cosmetic;
+import io.lumine.cosmetics.managers.MCCosmeticsManager;
+import io.lumine.cosmetics.players.Profile;
+import io.lumine.utils.serialize.Chroma;
+import lombok.Data;
+
+@Data
+public class SelectColorContext {
+
+    private final Profile profile;
+    private final MCCosmeticsManager manager;
+    private final Cosmetic cosmetic;
+    private Chroma chroma;
+
+}
diff --git a/plugin/src/main/java/io/lumine/cosmetics/menus/SelectColorMenu.java b/plugin/src/main/java/io/lumine/cosmetics/menus/SelectColorMenu.java
new file mode 100644
index 0000000000000000000000000000000000000000..5bdc13e0284b7f5fb06086f8e22144558a4d1392
--- /dev/null
+++ b/plugin/src/main/java/io/lumine/cosmetics/menus/SelectColorMenu.java
@@ -0,0 +1,35 @@
+package io.lumine.cosmetics.menus;
+
+import io.lumine.cosmetics.MCCosmeticsPlugin;
+import io.lumine.cosmetics.players.Profile;
+import io.lumine.utils.config.properties.types.MenuProp;
+import io.lumine.utils.menu.EditableMenuBuilder;
+import io.lumine.utils.menu.Icon;
+import io.lumine.utils.menu.MenuData;
+
+public class SelectColorMenu extends CosmeticMenu<SelectColorContext> {
+
+    public SelectColorMenu(MCCosmeticsPlugin core, MenuManager manager, String type) {
+        super(core, manager, new MenuProp(core, "menus/color_picker", "Menu", null));
+    }
+
+    @Override
+    public EditableMenuBuilder<SelectColorContext> build(EditableMenuBuilder<SelectColorContext> builder) {
+        
+
+        return builder;
+    }
+
+    public static class ColorSwatch implements MenuData<SelectColorContext> {
+
+        
+        
+        @Override
+        public Icon<SelectColorContext> getIcon() {
+            // TODO Auto-generated method stub
+            return null;
+        }
+        
+    }
+    
+}
diff --git a/plugin/src/main/resources-filtered/plugin.yml b/plugin/src/main/resources-filtered/plugin.yml
index 4a81064c46289bb1c7c585704d6ce5498787334e..d5b999a1717ca44f4d4a07db42046624a52a7109 100644
--- a/plugin/src/main/resources-filtered/plugin.yml
+++ b/plugin/src/main/resources-filtered/plugin.yml
@@ -12,7 +12,7 @@ softdepend:
 - PlaceholderAPI
 - WorldGuard
 loadbefore: []
-description: The total solution for all things mob-related.
+description: Plugin for adding amazing cosmetics, brought to you by MCModels
 commands:
   mccosmetics:
     description: MCCosmetics admin command
diff --git a/pom.xml b/pom.xml
index 24e5a1388ad12cfa10f10f07ea0349a5bb475ac0..ff8d5867f1466504c1dc3e4cc7e0d7388d6a4a32 100644
--- a/pom.xml
+++ b/pom.xml
@@ -13,7 +13,7 @@
     </organization>
  
 	<properties>  
-	    <mccosmetics.version>0.10.0</mccosmetics.version>
+	    <mccosmetics.version>0.11.0</mccosmetics.version>
 	    <paperapi.version>1.19-R0.1-SNAPSHOT</paperapi.version>
 	    <lumineutils.version>1.19-SNAPSHOT</lumineutils.version>
 	    <lombok.version>1.18.22</lombok.version>
@@ -36,6 +36,7 @@
         <module>v1_18_R2</module>
         <module>v1_19_R1</module>
         <module>v1_19_R1_2</module>
+        <module>v1_19_R2</module>
         <module>dist</module>
         <module>dist-premium</module>
 	</modules>
@@ -91,10 +92,6 @@
             <id>lumine</id>
             <url>https://mvn.lumine.io/repository/maven/</url>
         </repository>
-        <repository>
-            <id>nexus</id>
-            <url>https://mvn.lumine.io/repository/maven-private/</url>
-        </repository>
         <!-- <repository>
 		  	<id>bstats-repo</id>
 		  	<url>http://repo.bstats.org/content/repositories/releases/</url>
@@ -107,10 +104,6 @@
 	            <updatePolicy>never</updatePolicy>
 	        </releases>
 		</repository>  -->
-	    <repository>
-	        <id>sk89q-repo</id>
-	        <url>https://maven.sk89q.com/repo/</url>
-	    </repository>
 	    <repository>
       		<id>md_5-snapshots</id>
       		<url>https://repo.md-5.net/content/repositories/snapshots/</url>
@@ -119,10 +112,6 @@
             <id>placeholderapi</id>
             <url>https://repo.extendedclip.com/content/repositories/placeholderapi/</url>
         </repository>
-        <repository>
-		    <id>filoghost-repo</id>
-		    <url>https://ci.filoghost.me/plugin/repository/everything/</url>
-		</repository>
 	  	<repository>
 		    <id>papermc</id>
 		    <url>https://papermc.io/repo/repository/maven-public/</url>
diff --git a/sql/pom.xml b/sql/pom.xml
index 7df4e48b199f12faea58f4ed2ded85e7895210da..884c500f4d0877b4dd48e6c0457ecbbd9e98c82f 100644
--- a/sql/pom.xml
+++ b/sql/pom.xml
@@ -2,7 +2,7 @@
     <modelVersion>4.0.0</modelVersion>
 
     <artifactId>MCCosmetics-SQL</artifactId>
-    <parent> 
+    <parent>  
         <groupId>io.lumine</groupId>
         <artifactId>MCCosmetics-Plugin</artifactId>
         <version>${mccosmetics.version}</version>
@@ -16,6 +16,12 @@
     </pluginRepositories>
 
     <dependencies>
+        <dependency>
+            <groupId>io.lumine</groupId>
+            <artifactId>MCCosmetics-API</artifactId>
+            <version>${mccosmetics.version}</version>
+            <scope>provided</scope> 
+        </dependency>
         <dependency> 
             <groupId>org.projectlombok</groupId>
             <artifactId>lombok</artifactId>
@@ -90,7 +96,7 @@
              
                     <jdbc>
                         <driver>org.mariadb.jdbc.Driver</driver>
-                        <url>jdbc:mariadb://localhost:3306/mythiccraft_test_mccosmetics</url>
+                        <url>jdbc:mariadb://localhost:3306/mythiccraft_test_achievements</url>
                         <user>mythiccraft_tester</user>
                         <password>62e06d5e-6c48-4c40-a070-00949d2a9c6a</password>
                     </jdbc>
@@ -102,13 +108,13 @@
                             <excludes></excludes>
                             <schemata>
                                 <schema>
-                                    <inputSchema>mythiccraft_test_mccosmetics</inputSchema>
+                                    <inputSchema>mythiccraft_test_achievements</inputSchema>
                                     <outputSchemaToDefault>true</outputSchemaToDefault>
                                 </schema>
                             </schemata>
                         </database>
                         <target>
-                            <packageName>io.lumine.cosmetics.storage.sql.mappings</packageName>
+                            <packageName>io.lumine.achievements.storage.sql.jooq</packageName>
                             <directory>../plugin/src/main/java</directory>
                         </target>
                     </generator>
diff --git a/v1_17_R1/pom.xml b/v1_17_R1/pom.xml
index 5ccf039b1f69a871d3edeb98ce73d58bac57759f..fa47769a67f83391c611c018cc444c3709d355cf 100644
--- a/v1_17_R1/pom.xml
+++ b/v1_17_R1/pom.xml
@@ -77,22 +77,10 @@
 			<id>spigot-repo</id>
 			<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
 		</repository>
-		<repository>
-			<id>nexus</id>
-			<url>https://mvn.lumine.io/repository/maven/</url>
-		</repository>
-		<repository>
-			<id>lumine</id>
-			<url>https://mvn.lumine.io/repository/maven-private/</url>
-		</repository>
-		<repository>
-			<id>lumine-snapshots</id>
-			<url>https://mvn.lumine.io/repository/maven-snapshots/</url>
-		</repository>
-		<repository>
-			<id>lumine-test</id>
-			<url>https://mvn.lumine.io/repository/maven-test/</url>
-		</repository>
+        <repository>
+            <id>lumine</id>
+            <url>https://mvn.lumine.io/repository/maven/</url>
+        </repository>
 		<repository>
 			<id>minecraft-libraries</id>
 			<name>Minecraft Libraries</name>
diff --git a/v1_18_R1/pom.xml b/v1_18_R1/pom.xml
index b4efc549a82b76a6ba33db25e7ee079316d33000..78e8fea7fb3b60bb5d4966824ee2d5fa1a5c882b 100644
--- a/v1_18_R1/pom.xml
+++ b/v1_18_R1/pom.xml
@@ -77,22 +77,10 @@
 			<id>spigot-repo</id>
 			<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
 		</repository>
-		<repository>
-			<id>nexus</id>
-			<url>https://mvn.lumine.io/repository/maven/</url>
-		</repository>
-		<repository>
-			<id>lumine</id>
-			<url>https://mvn.lumine.io/repository/maven-private/</url>
-		</repository>
-		<repository>
-			<id>lumine-snapshots</id>
-			<url>https://mvn.lumine.io/repository/maven-snapshots/</url>
-		</repository>
-		<repository>
-			<id>lumine-test</id>
-			<url>https://mvn.lumine.io/repository/maven-test/</url>
-		</repository>
+        <repository>
+            <id>lumine</id>
+            <url>https://mvn.lumine.io/repository/maven/</url>
+        </repository>
         <repository>
            <id>paper-repo</id>
            <url>https://papermc.io/repo/repository/maven-public/</url>
diff --git a/v1_18_R2/pom.xml b/v1_18_R2/pom.xml
index bf90647c026e9799f36b9be87d020142ff0b0d25..40197012b5f38036a684a7ce3cdf62b228f3de87 100644
--- a/v1_18_R2/pom.xml
+++ b/v1_18_R2/pom.xml
@@ -74,21 +74,9 @@
             <id>spigot-repo</id> 
             <url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
         </repository>
-        <repository>
-            <id>nexus</id>
-            <url>https://mvn.lumine.io/repository/maven/</url>
-        </repository>
         <repository>
             <id>lumine</id>
-            <url>https://mvn.lumine.io/repository/maven-private/</url>
-        </repository>
-        <repository>
-            <id>lumine-snapshots</id>
-            <url>https://mvn.lumine.io/repository/maven-snapshots/</url>
-        </repository> 
-        <repository>
-            <id>lumine-test</id>
-            <url>https://mvn.lumine.io/repository/maven-test/</url>
+            <url>https://mvn.lumine.io/repository/maven/</url>
         </repository>
         <repository>
            <id>paper-repo</id>
diff --git a/v1_19_R1/pom.xml b/v1_19_R1/pom.xml
index 2ea0b225095bef87d715154e7e1c9bd8904c9ff4..328286e09a9a4a21b0f6819c829ac5ff8dabe630 100644
--- a/v1_19_R1/pom.xml
+++ b/v1_19_R1/pom.xml
@@ -74,21 +74,9 @@
             <id>spigot-repo</id> 
             <url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
         </repository>
-        <repository>
-            <id>nexus</id>
-            <url>https://mvn.lumine.io/repository/maven/</url>
-        </repository>
         <repository>
             <id>lumine</id>
-            <url>https://mvn.lumine.io/repository/maven-private/</url>
-        </repository>
-        <repository>
-            <id>lumine-snapshots</id>
-            <url>https://mvn.lumine.io/repository/maven-snapshots/</url>
-        </repository> 
-        <repository>
-            <id>lumine-test</id>
-            <url>https://mvn.lumine.io/repository/maven-test/</url>
+            <url>https://mvn.lumine.io/repository/maven/</url>
         </repository>
         <repository>
            <id>paper-repo</id>
diff --git a/v1_19_R1_2/pom.xml b/v1_19_R1_2/pom.xml
index 75fe5b0ffc9889b4e8c63977723aceb0490403ed..23fb38216cedbd40e6b47b4b2e160c8f7d5fe0f9 100644
--- a/v1_19_R1_2/pom.xml
+++ b/v1_19_R1_2/pom.xml
@@ -74,21 +74,9 @@
             <id>spigot-repo</id> 
             <url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
         </repository>
-        <repository>
-            <id>nexus</id>
-            <url>https://mvn.lumine.io/repository/maven/</url>
-        </repository>
         <repository>
             <id>lumine</id>
-            <url>https://mvn.lumine.io/repository/maven-private/</url>
-        </repository>
-        <repository>
-            <id>lumine-snapshots</id>
-            <url>https://mvn.lumine.io/repository/maven-snapshots/</url>
-        </repository> 
-        <repository> 
-            <id>lumine-test</id>
-            <url>https://mvn.lumine.io/repository/maven-test/</url>
+            <url>https://mvn.lumine.io/repository/maven/</url>
         </repository>
         <repository>
            <id>paper-repo</id>
diff --git a/v1_19_R2/.gitignore b/v1_19_R2/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..e3425b9cc114cdcb390fb9c7201b1ffdce23d875
--- /dev/null
+++ b/v1_19_R2/.gitignore
@@ -0,0 +1 @@
+/.flattened-pom.xml
diff --git a/v1_19_R2/pom.xml b/v1_19_R2/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..0262f4a64f6ee0f7df9d9ba1908dd88c01af35d4
--- /dev/null
+++ b/v1_19_R2/pom.xml
@@ -0,0 +1,164 @@
+ <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    
+    <artifactId>MCCosmetics-v1_19_R2</artifactId>
+    <parent>
+        <groupId>io.lumine</groupId>
+        <artifactId>MCCosmetics-Plugin</artifactId>
+        <version>${mccosmetics.version}</version>
+    </parent>
+
+    <build> 
+        <plugins>
+            <plugin>
+                <groupId>net.md-5</groupId>
+                <artifactId>specialsource-maven-plugin</artifactId>
+                <version>1.2.3</version>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>remap</goal>
+                        </goals>
+                        <id>remap-obf</id>
+                        <configuration>
+                            <srgIn>org.spigotmc:minecraft-server:1.19.3-R0.1-SNAPSHOT:txt:maps-mojang</srgIn>
+                            <reverse>true</reverse>
+                            <remappedDependencies>org.spigotmc:spigot:1.19.3-R0.1-SNAPSHOT:jar:remapped-mojang</remappedDependencies>
+                            <remappedArtifactAttached>true</remappedArtifactAttached>
+                            <remappedClassifierName>remapped-obf</remappedClassifierName>
+                        </configuration>
+                    </execution>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>remap</goal>
+                        </goals>
+                        <id>remap-spigot</id>
+                        <configuration>
+                            <inputFile>${project.build.directory}/${project.artifactId}-${project.version}-remapped-obf.jar</inputFile>
+                            <srgIn>org.spigotmc:minecraft-server:1.19.3-R0.1-SNAPSHOT:csrg:maps-spigot</srgIn>
+                            <remappedDependencies>org.spigotmc:spigot:1.19.3-R0.1-SNAPSHOT:jar:remapped-obf</remappedDependencies>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>  
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>flatten-maven-plugin</artifactId>
+                <version>1.2.2</version>
+                <configuration>
+                    <updatePomFile>true</updatePomFile>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>flatten</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>flatten</goal>
+                        </goals>
+                    </execution>
+                    <execution>
+                        <id>flatten.clean</id>
+                        <phase>clean</phase>
+                        <goals>
+                            <goal>clean</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build> 
+    <repositories> 
+        <repository>
+            <id>spigot-repo</id> 
+            <url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
+        </repository>
+        <repository>
+            <id>lumine</id>
+            <url>https://mvn.lumine.io/repository/maven/</url>
+        </repository>
+        <repository>
+           <id>paper-repo</id>
+           <url>https://papermc.io/repo/repository/maven-public/</url>
+        </repository>
+        <repository>
+            <id>minecraft-libraries</id>
+            <name>Minecraft Libraries</name>
+            <url>https://libraries.minecraft.net</url>
+        </repository>
+    </repositories>
+
+    <dependencies>
+        <!-- Modules -->
+        <dependency>
+            <groupId>io.lumine</groupId>
+            <artifactId>LumineUtils</artifactId>
+            <version>${lumineutils.version}</version>
+            <scope>provided</scope> 
+        </dependency>
+        <dependency>
+            <groupId>io.lumine</groupId>
+            <artifactId>MCCosmetics</artifactId>
+            <version>${project.parent.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency> 
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <version>${lombok.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <!-- Spigot API -->
+        <dependency>
+            <groupId>org.spigotmc</groupId>
+            <artifactId>spigot-api</artifactId>
+            <version>1.19.3-R0.1-SNAPSHOT</version>
+            <classifier>shaded</classifier>
+            <scope>provided</scope>
+        </dependency> 
+        
+        <!-- Spigot NMS version -->  
+        <dependency>
+            <groupId>org.spigotmc</groupId>
+            <artifactId>spigot</artifactId>
+            <version>1.19.3-R0.1-SNAPSHOT</version>
+            <classifier>remapped-mojang</classifier>
+            <scope>provided</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>*</groupId>
+                    <artifactId>*</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>com.mojang</groupId>
+            <artifactId>authlib</artifactId>
+            <version>3.11.50</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.mojang</groupId>
+            <artifactId>datafixerupper</artifactId>
+            <version>1.0.20</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-all</artifactId>
+            <version>4.1.68.Final</version>
+            <scope>provided</scope>
+        </dependency>
+        
+        <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <version>1.8.0-beta4</version>
+        </dependency>
+
+    </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/v1_19_R2/src/main/java/io/lumine/cosmetics/nms/VolatileCodeEnabled_v1_19_R2.java b/v1_19_R2/src/main/java/io/lumine/cosmetics/nms/VolatileCodeEnabled_v1_19_R2.java
new file mode 100644
index 0000000000000000000000000000000000000000..d54012c5e7f6387b785aee842b7f7dee12fb7b08
--- /dev/null
+++ b/v1_19_R2/src/main/java/io/lumine/cosmetics/nms/VolatileCodeEnabled_v1_19_R2.java
@@ -0,0 +1,184 @@
+package io.lumine.cosmetics.nms;
+
+import com.google.common.collect.Maps;
+import io.lumine.cosmetics.MCCosmeticsPlugin;
+import io.lumine.cosmetics.api.cosmetics.Cosmetic;
+import io.lumine.cosmetics.api.players.wardrobe.Mannequin;
+import io.lumine.cosmetics.api.players.wardrobe.WardrobeTracker;
+import io.lumine.cosmetics.managers.back.BackAccessory;
+import io.lumine.cosmetics.managers.gestures.Gesture;
+import io.lumine.cosmetics.managers.hats.Hat;
+import io.lumine.cosmetics.managers.offhand.Offhand;
+import io.lumine.cosmetics.managers.sprays.Spray;
+import io.lumine.cosmetics.nms.cosmetic.VolatileCosmeticHelper;
+import io.lumine.cosmetics.nms.v1_19_R2.cosmetic.*;
+import io.lumine.cosmetics.nms.v1_19_R2.network.VolatileChannelHandler;
+import io.lumine.cosmetics.nms.v1_19_R2.wardrobe.MannequinEntity;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelPipeline;
+import lombok.Getter;
+import net.minecraft.network.Connection;
+import net.minecraft.network.protocol.Packet;
+import net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.server.level.ServerPlayer;
+import net.minecraft.world.level.entity.LevelEntityGetter;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.World;
+import org.bukkit.craftbukkit.v1_19_R2.CraftWorld;
+import org.bukkit.craftbukkit.v1_19_R2.entity.CraftLivingEntity;
+import org.bukkit.craftbukkit.v1_19_R2.entity.CraftPlayer;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.LivingEntity;
+import org.bukkit.entity.Player;
+
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.Map;
+
+public class VolatileCodeEnabled_v1_19_R2 implements VolatileCodeHandler {
+
+    @Getter private final MCCosmeticsPlugin plugin;
+    private final Map<Class<? extends Cosmetic>, VolatileCosmeticHelper> cosmeticHelpers = Maps.newConcurrentMap();
+    
+    private Method entityGetter;
+
+    public VolatileCodeEnabled_v1_19_R2(MCCosmeticsPlugin plugin) {
+        this.plugin = plugin;
+        cosmeticHelpers.put(Hat.class, new VolatileHatImpl(plugin, this));
+        cosmeticHelpers.put(BackAccessory.class, new VolatileBackImpl(plugin, this));
+        cosmeticHelpers.put(Spray.class, new VolatileSprayImpl(plugin, this));
+        cosmeticHelpers.put(Offhand.class, new VolatileOffhandImpl(plugin, this));
+        cosmeticHelpers.put(Gesture.class, new VolatileGestureImpl(plugin, this));
+        
+        for(var method : ServerLevel.class.getMethods()) {
+            if(LevelEntityGetter.class.isAssignableFrom(method.getReturnType()) && method.getReturnType() != LevelEntityGetter.class) {
+                entityGetter = method;
+                break;
+            }
+        }
+    }
+
+    @Override
+    public VolatileCosmeticHelper getCosmeticHelper(Class<? extends Cosmetic> tClass) {
+        return cosmeticHelpers.get(tClass);
+    }
+
+    @Override
+    public Collection<VolatileCosmeticHelper> getCosmeticHelpers() {
+        return cosmeticHelpers.values();
+    }
+
+    @Override
+    public void injectPlayer(Player player) {
+        ServerPlayer ply = ((CraftPlayer) player).getHandle();
+        VolatileChannelHandler cdh = new VolatileChannelHandler(player, this);
+
+        ChannelPipeline pipeline = ply.connection.getConnection().channel.pipeline();
+        for (String name : pipeline.toMap().keySet()) {
+            if (pipeline.get(name) instanceof Connection) {
+                pipeline.addBefore(name, "mc_cosmetics_packet_handler", cdh);
+                break;
+            }
+        }
+    }
+
+    @Override
+    public void removePlayer(Player player) {
+        Channel channel = ((CraftPlayer) player).getHandle().connection.getConnection().channel;
+        channel.eventLoop().submit(() -> {
+            channel.pipeline().remove("mc_cosmetics_packet_handler");
+            return null;
+        });
+    }
+
+    public void broadcast(Packet<?>... packets) {
+        for(Player player : Bukkit.getOnlinePlayers()) {
+            var connection = ((CraftPlayer) player).getHandle().connection;
+            for(Packet<?> packet : packets) {
+                connection.send(packet);
+            }
+        }
+    }
+
+    public void broadcast(Player player, Packet<?>... packets) {
+        var connection = ((CraftPlayer) player).getHandle().connection;
+        for(Packet<?> packet : packets) {
+            connection.send(packet);
+        }
+    }
+
+    public void broadcastAroundAndSelf(Player wearer, Packet<?>... packets) {
+        final var level = ((CraftWorld) wearer.getWorld()).getHandle();
+        final var trackedEntity = level.getChunkSource().chunkMap.entityMap.get(wearer.getEntityId());
+
+        if(trackedEntity == null) {
+            broadcast(wearer.getWorld(), packets);
+            return;
+        }
+
+        for(Packet<?> packet : packets)
+            trackedEntity.broadcastAndSend(packet);
+    }
+
+    public void broadcastAround(Player wearer, Packet<?>... packets) {
+        final var level = ((CraftWorld) wearer.getWorld()).getHandle();
+        final var trackedEntity = level.getChunkSource().chunkMap.entityMap.get(wearer.getEntityId());
+
+        if(trackedEntity == null) {
+            broadcast(wearer.getWorld(), packets);
+            return;
+        }
+
+        for(Packet<?> packet : packets)
+            trackedEntity.broadcast(packet);
+    }
+
+    public void broadcast(World world, Packet<?>... packets) {
+        for(Player player : world.getPlayers()) {
+            var connection = ((CraftPlayer) player).getHandle().connection;
+            for(Packet<?> packet : packets) {
+                connection.send(packet);
+            }
+        }
+    }
+
+    public Entity getEntity(World world, int id) {
+        ServerLevel level = ((CraftWorld) world).getHandle();
+        
+        try {
+            var getter = entityGetter != null ?
+                    (LevelEntityGetter<net.minecraft.world.entity.Entity>) entityGetter.invoke(level) :
+                    level.entityManager.getEntityGetter();
+
+            final var entity = getter.get(id);
+            return entity == null ? null : entity.getBukkitEntity();
+        } catch(Exception | Error ex) {
+            ex.printStackTrace();
+            return null;
+        }
+    }
+    
+    @Override
+    public Mannequin createMannequin(WardrobeTracker tracker, Player player, Location location) {
+        return new MannequinEntity(tracker,this,player,location);
+    }
+    
+    @Override
+    public void removeFakeEntity(int id) {
+        ClientboundRemoveEntitiesPacket packet = new ClientboundRemoveEntitiesPacket(id);
+        broadcast(packet);
+    }
+
+    @Override
+    public void setBodyYaw(LivingEntity entity, double yaw) {
+        ((CraftLivingEntity) entity).getHandle().yBodyRot = (float) yaw;
+    }
+
+    @Override
+    public float getBodyYaw(LivingEntity entity) {
+        return ((CraftLivingEntity) entity).getHandle().yBodyRot;
+    }
+}
diff --git a/v1_19_R2/src/main/java/io/lumine/cosmetics/nms/v1_19_R2/cosmetic/VolatileBackImpl.java b/v1_19_R2/src/main/java/io/lumine/cosmetics/nms/v1_19_R2/cosmetic/VolatileBackImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..b38467199e4d32dc48033a2ae9e76d38e0008d2c
--- /dev/null
+++ b/v1_19_R2/src/main/java/io/lumine/cosmetics/nms/v1_19_R2/cosmetic/VolatileBackImpl.java
@@ -0,0 +1,305 @@
+package io.lumine.cosmetics.nms.v1_19_R2.cosmetic;
+
+import com.google.common.collect.Maps;
+import com.mojang.datafixers.util.Pair;
+import io.lumine.cosmetics.MCCosmeticsPlugin;
+import io.lumine.cosmetics.api.cosmetics.Cosmetic;
+import io.lumine.cosmetics.api.cosmetics.EquippedCosmetic;
+import io.lumine.cosmetics.api.cosmetics.ItemCosmetic;
+import io.lumine.cosmetics.api.players.CosmeticProfile;
+import io.lumine.cosmetics.api.players.wardrobe.Mannequin;
+import io.lumine.cosmetics.constants.CosmeticType;
+import io.lumine.cosmetics.logging.MCLogger;
+import io.lumine.cosmetics.managers.back.BackAccessory;
+import io.lumine.cosmetics.managers.offhand.Offhand;
+import io.lumine.cosmetics.nms.VolatileCodeEnabled_v1_19_R2;
+import io.lumine.cosmetics.nms.cosmetic.VolatileEquipmentHelper;
+import io.lumine.cosmetics.nms.v1_19_R2.wardrobe.MannequinEntity;
+import io.lumine.cosmetics.players.Profile;
+import io.netty.buffer.Unpooled;
+import lombok.Getter;
+import net.minecraft.network.FriendlyByteBuf;
+import net.minecraft.network.protocol.Packet;
+import net.minecraft.network.protocol.game.*;
+import net.minecraft.world.entity.EntityType;
+import net.minecraft.world.entity.EquipmentSlot;
+import net.minecraft.world.entity.decoration.ArmorStand;
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_19_R2.CraftWorld;
+import org.bukkit.craftbukkit.v1_19_R2.entity.CraftPlayer;
+import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftItemStack;
+import org.bukkit.entity.Player;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+public class VolatileBackImpl implements VolatileEquipmentHelper {
+
+	@Getter
+	private final MCCosmeticsPlugin plugin;
+	private final VolatileCodeEnabled_v1_19_R2 nmsHandler;
+	private final Map<Player, ArmorStand> activeProfile = Maps.newConcurrentMap();
+	private final Map<Integer, Player> playerTracker = Maps.newConcurrentMap();
+
+	public VolatileBackImpl(MCCosmeticsPlugin plugin, VolatileCodeEnabled_v1_19_R2 nmsHandler) {
+		this.plugin = plugin;
+		this.nmsHandler = nmsHandler;
+	}
+
+	@Override
+	public void apply(CosmeticProfile profile) {
+		if (profile == null)
+			return;
+		Player player = profile.getPlayer();
+		
+		var maybeEquipped = profile.getEquipped(BackAccessory.class);
+		if(maybeEquipped.isEmpty()) {
+		    return;
+		}
+		var equipped = maybeEquipped.get();
+		var cosmetic = equipped.getCosmetic();
+		
+		if (!(cosmetic instanceof ItemCosmetic back)) {
+			return;
+		}
+
+		var nmsPlayer = ((CraftPlayer) player).getHandle();
+		var nmsBack = CraftItemStack.asNMSCopy(back.getCosmetic(equipped));
+
+		ArmorStand stand = activeProfile.get(player);
+		if(stand == null) {
+			playerTracker.put(player.getEntityId(), player);
+			stand = new ArmorStand(EntityType.ARMOR_STAND, ((CraftWorld) player.getWorld()).getHandle());
+			stand.moveTo(nmsPlayer.getX(), nmsPlayer.getY() + nmsPlayer.getPassengersRidingOffset() + stand.getMyRidingOffset(), nmsPlayer.getZ(), nmsPlayer.getYRot(), 0);
+			stand.setMarker(true);
+			stand.setInvisible(true);
+			stand.setSilent(true);
+
+			activeProfile.put(player, stand);
+
+			var mobPacket = new ClientboundAddEntityPacket(stand);
+			var dataPacket = new ClientboundSetEntityDataPacket(stand.getId(), stand.getEntityData().getNonDefaultValues());
+			var passengersPacket = createPassengerPacket(player.getEntityId(), stand.getId());
+
+			nmsHandler.broadcastAroundAndSelf(player, mobPacket, dataPacket, passengersPacket);
+		}
+
+		stand.setItemSlot(EquipmentSlot.HEAD, nmsBack);
+
+		var equipmentPacket = new ClientboundSetEquipmentPacket(stand.getId(), List.of(Pair.of(EquipmentSlot.HEAD, nmsBack)));
+
+		nmsHandler.broadcastAroundAndSelf(player, equipmentPacket);
+	}
+
+	@Override
+	public void unapply(CosmeticProfile profile) {
+		Player player = profile.getPlayer();
+		ArmorStand stand = activeProfile.remove(player);
+		if(stand == null)
+			return;
+		playerTracker.remove(player.getEntityId());
+		ClientboundRemoveEntitiesPacket removePacket = new ClientboundRemoveEntitiesPacket(stand.getId());
+		nmsHandler.broadcastAroundAndSelf(player, removePacket);
+	}
+	
+    @Override 
+    public void equipMannequin(Mannequin mannequin, EquippedCosmetic cosmetic) {
+        if(!(cosmetic.getCosmetic() instanceof BackAccessory back)) {
+            return;
+        }
+        
+        final var entityId = mannequin.getEntityId();
+        final var player = mannequin.getPlayer();
+        var nmsBack = CraftItemStack.asNMSCopy(back.getCosmetic(cosmetic));
+
+        var mannequinLocation = mannequin.getLocation();
+        
+        mannequin.removeExtraEntity(BackAccessory.class);
+
+        var stand = new ArmorStand(EntityType.ARMOR_STAND, ((CraftWorld) player.getWorld()).getHandle());
+        stand.moveTo(mannequinLocation.getX(), mannequinLocation.getY() + stand.getMyRidingOffset(), mannequinLocation.getZ(), mannequinLocation.getYaw(), 0);
+        stand.setMarker(true);
+        stand.setInvisible(true);
+        stand.setSilent(true);
+
+        mannequin.addExtraEntity(BackAccessory.class, stand.getId());
+        
+        var mobPacket = new ClientboundAddEntityPacket(stand);
+        var dataPacket = new ClientboundSetEntityDataPacket(stand.getId(), stand.getEntityData().getNonDefaultValues());
+        var passengersPacket = createPassengerPacket(entityId, stand.getId());
+
+        nmsHandler.broadcast(player, mobPacket, dataPacket, passengersPacket);
+
+        stand.setItemSlot(EquipmentSlot.HEAD, nmsBack);
+
+        var equipmentPacket = new ClientboundSetEquipmentPacket(stand.getId(), List.of(Pair.of(EquipmentSlot.HEAD, nmsBack)));
+
+        nmsHandler.broadcast(player, equipmentPacket);
+    }
+
+    @Override
+    public void unequipMannequin(Mannequin mannequin) {
+        mannequin.removeExtraEntity(BackAccessory.class);
+    }
+
+	@Override
+	public boolean read(Player sender, Object packet, boolean isCanceled) {
+		final var profile = MCCosmeticsPlugin.inst().getProfiles().getProfile(sender);
+		if(profile == null || profile.isHidden(BackAccessory.class))
+			return true;
+		if(packet instanceof ServerboundMovePlayerPacket) {
+			handleRotate(profile);
+		}else if(packet instanceof ServerboundAcceptTeleportationPacket) {
+			final var list = handleSpawn(profile);
+			if(list == null)
+				return true;
+			final var connection = ((CraftPlayer) sender).getHandle().connection;
+			for(Object obj : list) {
+				connection.send((Packet<?>) obj);
+			}
+		}
+		return true;
+	}
+
+	@Override
+	public List<Object> write(Player receiver, Object packet) {
+		if(packet instanceof ClientboundAddPlayerPacket playerPacket) {
+			int id = playerPacket.getEntityId();
+			if(playerTracker.containsKey(id)) {
+				final var spawnedPlayer = playerTracker.get(id);
+				final var profile = MCCosmeticsPlugin.inst().getProfiles().getProfile(spawnedPlayer);
+				if(profile == null || profile.isHidden(BackAccessory.class))
+					return null;
+				return handleSpawn(profile);
+			}
+		}else if(packet instanceof ClientboundRemoveEntitiesPacket removePacket) {
+			for(int id : removePacket.getEntityIds()) {
+				if(playerTracker.containsKey(id)) {
+					return handleDespawn(playerTracker.get(id));
+				}
+			}
+		}
+		/*
+		else if(packet instanceof ClientboundMoveEntityPacket moveEntityPacket) {
+			FriendlyByteBuf byteBuf = new FriendlyByteBuf(Unpooled.buffer());
+			moveEntityPacket.write(byteBuf);
+			int id = byteBuf.readVarInt();
+			if(playerTracker.containsKey(id)) {
+				final var spawnedPlayer = playerTracker.get(id);
+				final var profile = MCCosmeticsPlugin.inst().getProfiles().getProfile(spawnedPlayer);
+				if(profile == null || profile.isHidden(BackAccessory.class))
+					return null;
+				return handleMove(profile, moveEntityPacket);
+			}
+		}else if(packet instanceof ClientboundTeleportEntityPacket teleportEntityPacket) {
+			int id = teleportEntityPacket.getId();
+			if(playerTracker.containsKey(id)) {
+				final var spawnedPlayer = playerTracker.get(id);
+				final var profile = MCCosmeticsPlugin.inst().getProfiles().getProfile(spawnedPlayer);
+				if(profile == null || profile.isHidden(BackAccessory.class))
+					return null;
+				return handleTeleport(profile);
+			}
+		}*/
+
+		return null;
+	}
+
+	private void handleRotate(Profile profile) {
+		if(!hasBack(profile))
+			return;
+
+		final var wearer = profile.getPlayer();
+		final var nmsPlayer = ((CraftPlayer) wearer).getHandle();
+		final var stand = activeProfile.get(wearer);
+
+		ClientboundRotateHeadPacket packet = new ClientboundRotateHeadPacket(stand, VolatileEquipmentHelper.toByte(nmsPlayer.getYRot()));
+		nmsHandler.broadcastAroundAndSelf(wearer, packet);
+	}
+
+	private List<Object> handleSpawn(Profile profile) {
+		if(!hasBack(profile))
+			return null;
+
+		final var wearer = profile.getPlayer();
+		final var stand = activeProfile.get(wearer);
+
+		var mobPacket = new ClientboundAddEntityPacket(stand);
+		ClientboundSetEntityDataPacket dataPacket = new ClientboundSetEntityDataPacket(stand.getId(), stand.getEntityData().getNonDefaultValues());
+		ClientboundSetEquipmentPacket equipmentPacket = new ClientboundSetEquipmentPacket(stand.getId(), List.of(Pair.of(EquipmentSlot.HEAD, stand.getItemBySlot(EquipmentSlot.HEAD))));
+		ClientboundSetPassengersPacket passengersPacket = createPassengerPacket(wearer.getEntityId(), stand.getId());
+
+		return List.of(mobPacket, dataPacket, equipmentPacket, passengersPacket);
+	}
+
+	private List<Object> handleDespawn(Player player) {
+		final var stand = activeProfile.get(player);
+		if(stand == null)
+			return null;
+		ClientboundRemoveEntitiesPacket removePacket = new ClientboundRemoveEntitiesPacket(stand.getId());
+		return List.of(removePacket);
+	}
+
+	private List<Object> handleMove(Profile profile, ClientboundMoveEntityPacket moveEntityPacket) {
+		if(!hasBack(profile))
+			return null;
+
+		final var wearer = profile.getPlayer();
+		final var stand = activeProfile.get(wearer);
+
+		ClientboundMoveEntityPacket move;
+		if(moveEntityPacket.hasPosition() && moveEntityPacket.hasRotation()) {
+			move = new ClientboundMoveEntityPacket.PosRot(
+					stand.getId(),
+					moveEntityPacket.getXa(),
+					moveEntityPacket.getYa(),
+					moveEntityPacket.getZa(),
+					moveEntityPacket.getyRot(),
+					moveEntityPacket.getxRot(),
+					false);
+		}else if(moveEntityPacket.hasPosition()) {
+			move = new ClientboundMoveEntityPacket.Pos(
+					stand.getId(),
+					moveEntityPacket.getXa(),
+					moveEntityPacket.getYa(),
+					moveEntityPacket.getZa(),
+					false);
+		}else {
+			move = new ClientboundMoveEntityPacket.Rot(
+					stand.getId(),
+					moveEntityPacket.getyRot(),
+					moveEntityPacket.getxRot(),
+					false);
+		}
+		return List.of(move);
+	}
+
+	private List<Object> handleTeleport(Profile profile) {
+		if(!hasBack(profile))
+			return null;
+
+		final var wearer = profile.getPlayer();
+		final var nmsPlayer = ((CraftPlayer) wearer).getHandle();
+		final var stand = activeProfile.get(wearer);
+		stand.moveTo(nmsPlayer.getX(), nmsPlayer.getY() + nmsPlayer.getPassengersRidingOffset() + stand.getMyRidingOffset(), nmsPlayer.getZ(), nmsPlayer.getYRot(), 0);
+
+		return List.of(new ClientboundTeleportEntityPacket(stand));
+	}
+
+	private boolean hasBack(Profile profile) {
+		if(profile == null)
+			return false;
+
+		var maybeBack = profile.getEquipped(BackAccessory.class);
+		return maybeBack.isPresent() && maybeBack.get().getCosmetic() instanceof ItemCosmetic && activeProfile.containsKey(profile.getPlayer());
+	}
+
+	private ClientboundSetPassengersPacket createPassengerPacket(int mount, int... driver) {
+		FriendlyByteBuf bb = new FriendlyByteBuf(Unpooled.buffer());
+		bb.writeVarInt(mount);
+		bb.writeVarIntArray(driver);
+		return new ClientboundSetPassengersPacket(bb);
+	}
+
+}
\ No newline at end of file
diff --git a/v1_19_R2/src/main/java/io/lumine/cosmetics/nms/v1_19_R2/cosmetic/VolatileGestureImpl.java b/v1_19_R2/src/main/java/io/lumine/cosmetics/nms/v1_19_R2/cosmetic/VolatileGestureImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..d27c988ea5b4afb1e11ccb642fba9402c867678f
--- /dev/null
+++ b/v1_19_R2/src/main/java/io/lumine/cosmetics/nms/v1_19_R2/cosmetic/VolatileGestureImpl.java
@@ -0,0 +1,178 @@
+package io.lumine.cosmetics.nms.v1_19_R2.cosmetic;
+
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.mojang.datafixers.util.Pair;
+import io.lumine.cosmetics.MCCosmeticsPlugin;
+import io.lumine.cosmetics.api.cosmetics.manager.HideableCosmetic;
+import io.lumine.cosmetics.api.players.CosmeticProfile;
+import io.lumine.cosmetics.managers.gestures.Gesture;
+import io.lumine.cosmetics.managers.gestures.GestureManager;
+import io.lumine.cosmetics.managers.gestures.QuitMethod;
+import io.lumine.cosmetics.nms.VolatileCodeEnabled_v1_19_R2;
+import io.lumine.cosmetics.nms.cosmetic.VolatileEquipmentHelper;
+import io.lumine.cosmetics.players.Profile;
+import io.netty.buffer.Unpooled;
+import lombok.Getter;
+import net.minecraft.network.FriendlyByteBuf;
+import net.minecraft.network.protocol.game.*;
+import net.minecraft.world.entity.EntityType;
+import net.minecraft.world.entity.EquipmentSlot;
+import net.minecraft.world.entity.animal.horse.Horse;
+import net.minecraft.world.item.ItemStack;
+import org.bukkit.attribute.Attribute;
+import org.bukkit.craftbukkit.v1_19_R2.CraftWorld;
+import org.bukkit.craftbukkit.v1_19_R2.attribute.CraftAttributeMap;
+import org.bukkit.craftbukkit.v1_19_R2.entity.CraftPlayer;
+import org.bukkit.entity.Player;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class VolatileGestureImpl implements VolatileEquipmentHelper {
+
+	private static final List<Pair<EquipmentSlot, ItemStack>> empty = new ArrayList<>();
+
+	static {
+		for(final var slot : EquipmentSlot.values())
+			empty.add(Pair.of(slot, ItemStack.EMPTY));
+	}
+
+	@Getter
+	private final MCCosmeticsPlugin plugin;
+	private final VolatileCodeEnabled_v1_19_R2 nmsHandler;
+	private final Set<Player> activeProfile = Sets.newConcurrentHashSet();
+	private final Map<Integer, Player> playerTracker = Maps.newConcurrentMap();
+
+	private Horse horse;
+
+	public VolatileGestureImpl(MCCosmeticsPlugin plugin, VolatileCodeEnabled_v1_19_R2 nmsHandler) {
+		this.plugin = plugin;
+		this.nmsHandler = nmsHandler;
+	}
+
+	@Override
+	public void apply(CosmeticProfile profile) {
+		if (profile == null)
+			return;
+		Player player = profile.getPlayer();
+		if(activeProfile.contains(player))
+			return;
+
+        final var maybeEquipped = profile.getEquipped(Gesture.class);
+        if(maybeEquipped.isEmpty()) {
+            return;
+        }
+        var opt = maybeEquipped.get().getCosmetic();
+        
+        if(!(opt instanceof Gesture gesture))
+            return;
+
+		playerTracker.put(player.getEntityId(), player);
+		activeProfile.add(player);
+		if(!gesture.isCanMove())
+			getHorsed(player);
+		player.setInvisible(true);
+
+		for(final var value : ((Profile) profile).getEquipped().values()) {
+			final var manager = value.getCosmetic().getManager();
+			if(!(manager instanceof HideableCosmetic hide))
+				continue;
+			hide.hide(profile, gesture);
+		}
+		ClientboundSetEquipmentPacket equipmentPacket = new ClientboundSetEquipmentPacket(player.getEntityId(), empty);
+		nmsHandler.broadcastAroundAndSelf(player, equipmentPacket);
+	}
+
+	@Override
+	public void unapply(CosmeticProfile profile) {
+		if (profile == null)
+			return;
+		Player player = profile.getPlayer();
+		if(!activeProfile.contains(player))
+			return;
+
+		activeProfile.remove(player);
+		playerTracker.remove(player.getEntityId());
+		nmsHandler.broadcast(player, new ClientboundRemoveEntitiesPacket(horse.getId()));
+
+		final var nmsPlayer = ((CraftPlayer) player).getHandle();
+		List<Pair<EquipmentSlot, ItemStack>> equipment = new ArrayList<>();
+		for(EquipmentSlot slot : EquipmentSlot.values())
+			equipment.add(Pair.of(slot, nmsPlayer.getItemBySlot(slot)));
+		ClientboundSetEquipmentPacket equipmentPacket = new ClientboundSetEquipmentPacket(player.getEntityId(), equipment);
+		nmsHandler.broadcastAroundAndSelf(player, equipmentPacket);
+
+		player.setInvisible(false);
+		for(final var value : ((Profile) profile).getEquipped().values()) {
+			final var manager = value.getCosmetic().getManager();
+			if(!(manager instanceof HideableCosmetic hide))
+				continue;
+			hide.show(profile);
+		}
+	}
+
+	private ClientboundSetPassengersPacket createPassengerPacket(int mount, int... driver) {
+		FriendlyByteBuf bb = new FriendlyByteBuf(Unpooled.buffer());
+		bb.writeVarInt(mount);
+		bb.writeVarIntArray(driver);
+		return new ClientboundSetPassengersPacket(bb);
+	}
+
+	private void getHorsed(Player player) {
+		final var nmsPlayer = ((CraftPlayer) player).getHandle();
+
+		if(horse == null) {
+			horse = new Horse(EntityType.HORSE, ((CraftWorld) player.getWorld()).getHandle());
+			horse.setInvisible(true);
+			horse.setHealth(0);
+			horse.getAttribute(CraftAttributeMap.toMinecraft(Attribute.GENERIC_MAX_HEALTH)).setBaseValue(0);
+		}
+		horse.setPos(nmsPlayer.getX(), nmsPlayer.getY() - horse.getPassengersRidingOffset() - nmsPlayer.getMyRidingOffset(), nmsPlayer.getZ());
+
+		var mobPacket = new ClientboundAddEntityPacket(horse);
+		var dataPacket = new ClientboundSetEntityDataPacket(horse.getId(), horse.getEntityData().getNonDefaultValues());
+		var attributesPacket = new ClientboundUpdateAttributesPacket(horse.getId(), horse.getAttributes().getSyncableAttributes());
+		var passengersPacket = createPassengerPacket(horse.getId(), nmsPlayer.getId());
+
+		nmsHandler.broadcast(player, mobPacket, dataPacket, attributesPacket, passengersPacket);
+	}
+
+	@Override
+	public boolean read(Player sender, Object packet, boolean isCanceled) {
+		if(!plugin.getGestureManager().getTicking().containsKey(sender)) {
+		    return true;
+		}
+
+		if(packet instanceof ServerboundPlayerInputPacket inputPacket) {
+			final var manager = (GestureManager) plugin.getGestureManager();
+			if(inputPacket.isShiftKeyDown())
+				manager.quit(sender, QuitMethod.SNEAK);
+			if(inputPacket.isJumping())
+				manager.quit(sender, QuitMethod.JUMP);
+		}else if(packet instanceof ServerboundSetCarriedItemPacket setSlotPacket) {
+			int oSlot = sender.getInventory().getHeldItemSlot();
+			if(oSlot != setSlotPacket.getSlot()) {
+				nmsHandler.broadcast(sender, new ClientboundSetCarriedItemPacket(oSlot));
+				return false;
+			}
+		}
+		return true;
+	}
+
+	@Override
+	public List<Object> write(Player receiver, Object packet) {
+		if(packet instanceof ClientboundSetEquipmentPacket equipmentPacket) {
+			int id = equipmentPacket.getEntity();
+			final var spawnedPlayer = playerTracker.get(id);
+			if(spawnedPlayer == null)
+				return null;
+			final var profile = MCCosmeticsPlugin.inst().getProfiles().getProfile(spawnedPlayer);
+			if(profile != null)
+				return List.of(new ClientboundSetEquipmentPacket(id, empty));
+		}
+		return null;
+	}
+}
diff --git a/v1_19_R2/src/main/java/io/lumine/cosmetics/nms/v1_19_R2/cosmetic/VolatileHatImpl.java b/v1_19_R2/src/main/java/io/lumine/cosmetics/nms/v1_19_R2/cosmetic/VolatileHatImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..c5831f673937a33a5b519e7a2a1a96fb61a32043
--- /dev/null
+++ b/v1_19_R2/src/main/java/io/lumine/cosmetics/nms/v1_19_R2/cosmetic/VolatileHatImpl.java
@@ -0,0 +1,199 @@
+package io.lumine.cosmetics.nms.v1_19_R2.cosmetic;
+
+import com.google.common.collect.Maps;
+import com.mojang.datafixers.util.Pair;
+import io.lumine.cosmetics.MCCosmeticsPlugin;
+import io.lumine.cosmetics.api.cosmetics.EquippedCosmetic;
+import io.lumine.cosmetics.api.cosmetics.ItemCosmetic;
+import io.lumine.cosmetics.api.players.CosmeticProfile;
+import io.lumine.cosmetics.api.players.wardrobe.Mannequin;
+import io.lumine.cosmetics.managers.hats.Hat;
+import io.lumine.cosmetics.nms.VolatileCodeEnabled_v1_19_R2;
+import io.lumine.cosmetics.nms.cosmetic.VolatileEquipmentHelper;
+import io.lumine.cosmetics.players.Profile;
+import io.lumine.utils.logging.Log;
+import io.lumine.utils.reflection.Reflector;
+import io.netty.buffer.Unpooled;
+import lombok.Getter;
+import net.minecraft.network.FriendlyByteBuf;
+import net.minecraft.network.protocol.game.ClientboundAddPlayerPacket;
+import net.minecraft.network.protocol.game.ClientboundSetEquipmentPacket;
+import net.minecraft.network.protocol.game.ServerboundAcceptTeleportationPacket;
+import net.minecraft.world.entity.EquipmentSlot;
+import org.bukkit.craftbukkit.v1_19_R2.entity.CraftPlayer;
+import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftItemStack;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class VolatileHatImpl implements VolatileEquipmentHelper {
+
+    @Getter private final MCCosmeticsPlugin plugin;
+    private final VolatileCodeEnabled_v1_19_R2 nmsHandler;
+    //private final Map<Integer, Player> playerTracker = Maps.newConcurrentMap();
+
+    public VolatileHatImpl(MCCosmeticsPlugin plugin, VolatileCodeEnabled_v1_19_R2 nmsHandler) {
+        this.plugin = plugin;
+        this.nmsHandler = nmsHandler;
+    }
+    
+    @Override
+    public void apply(CosmeticProfile profile) {
+
+        if (profile == null)
+            return;
+
+        Player player = profile.getPlayer();
+
+        final var maybeEquipped = profile.getEquipped(Hat.class);
+        if(maybeEquipped.isEmpty()) {
+            return;
+        }
+        var opt = maybeEquipped.get().getCosmetic();
+        
+        if(!(opt instanceof Hat hat))
+            return;
+
+        var nmsHat = CraftItemStack.asNMSCopy(hat.getCosmetic(maybeEquipped.get()));
+
+        //playerTracker.put(player.getEntityId(), player);
+
+        ClientboundSetEquipmentPacket equipmentPacket = new ClientboundSetEquipmentPacket(player.getEntityId(), List.of(Pair.of(EquipmentSlot.HEAD, nmsHat)));
+
+        nmsHandler.broadcastAroundAndSelf(player, equipmentPacket);
+    }
+    
+    @Override
+    public void unapply(CosmeticProfile profile) {
+        final var nmsPlayer = ((CraftPlayer) profile.getPlayer()).getHandle();
+        final var item = nmsPlayer.getItemBySlot(EquipmentSlot.HEAD);
+        ClientboundSetEquipmentPacket equipmentPacket = new ClientboundSetEquipmentPacket(nmsPlayer.getId(), List.of(Pair.of(EquipmentSlot.HEAD, item)));
+        nmsHandler.broadcastAroundAndSelf(nmsPlayer.getBukkitEntity(), equipmentPacket);
+    }
+    
+    @Override 
+    public void equipMannequin(Mannequin mannequin, EquippedCosmetic cosmetic) {
+        if(!(cosmetic.getCosmetic() instanceof Hat hat)) {
+            return;
+        }
+        
+        final var entityId = mannequin.getEntityId();
+        final var player = mannequin.getPlayer();
+        
+        var nmsHat = CraftItemStack.asNMSCopy(hat.getCosmetic(cosmetic));
+        
+        ClientboundSetEquipmentPacket equipmentPacket = new ClientboundSetEquipmentPacket(entityId, 
+                List.of(Pair.of(EquipmentSlot.HEAD, nmsHat)));
+
+        nmsHandler.broadcast(player, equipmentPacket);
+    }
+
+    @Override
+    public void unequipMannequin(Mannequin mannequin) {
+        final var nmsPlayer = ((CraftPlayer) mannequin.getPlayer()).getHandle();
+        final var item = nmsPlayer.getItemBySlot(EquipmentSlot.HEAD);
+        ClientboundSetEquipmentPacket equipmentPacket = new ClientboundSetEquipmentPacket(nmsPlayer.getId(), List.of(Pair.of(EquipmentSlot.HEAD, item)));
+        nmsHandler.broadcast(nmsPlayer.getBukkitEntity(), equipmentPacket);
+    }
+    
+    @Override
+    public boolean read(Player sender, Object packet, boolean isCanceled) {
+        if(packet instanceof ServerboundAcceptTeleportationPacket) {
+            final var profile = MCCosmeticsPlugin.inst().getProfiles().getProfile(sender);
+            if(profile == null || profile.isHidden(Hat.class))
+                return true;
+            handleSpawn(profile);
+        }
+        return true;
+    }
+
+    @Override
+    public List<Object> write(Player receiver, Object packet) {
+        /*
+        if(packet instanceof ClientboundAddPlayerPacket playerPacket) {
+            int id = playerPacket.getEntityId();
+            Profile profile = getProfile(receiver, id);
+            if(profile != null && !profile.isHidden(Hat.class)) {
+                handleSpawn(profile);
+            }
+        } else */
+        
+        if(packet instanceof ClientboundSetEquipmentPacket equipmentPacket) {
+            int id = equipmentPacket.getEntity();
+            Profile profile = getProfile(receiver, id);
+            if(profile != null && !profile.isHidden(Hat.class)) {
+                modifyPacket(profile, equipmentPacket);
+            }
+        }
+        
+        return null;
+    }
+
+    private Profile getProfile(Player receiver, int id) {
+        final var entity = nmsHandler.getEntity(receiver.getWorld(), id);
+        if(!(entity instanceof Player player))
+            return null;
+        return plugin.getProfiles().getProfile(player);
+    }
+
+    public void handleSpawn(Profile profile) {
+
+        final var maybeEquipped = profile.getEquipped(Hat.class);
+        if(maybeEquipped.isEmpty()) {
+            return;
+        }
+        var equip = maybeEquipped.get();
+        var opt = equip.getCosmetic();
+        
+        if(!(opt instanceof ItemCosmetic hat)) {
+            return;
+        }
+
+        final var player = profile.getPlayer();
+        final var nmsHat = CraftItemStack.asNMSCopy(hat.getCosmetic(equip));
+        ClientboundSetEquipmentPacket equipmentPacket = new ClientboundSetEquipmentPacket(player.getEntityId(), List.of(Pair.of(EquipmentSlot.HEAD, nmsHat)));
+
+        nmsHandler.broadcastAroundAndSelf(player, equipmentPacket);
+    }
+    
+    private static Reflector<ClientboundSetEquipmentPacket> refEq = new Reflector(ClientboundSetEquipmentPacket.class, "c");
+    
+    private void modifyPacket(Profile profile, ClientboundSetEquipmentPacket packet) {
+        final var maybeEquipped = profile.getEquipped(Hat.class);
+        if(maybeEquipped.isEmpty()) {
+            return;
+        }
+        var equip = maybeEquipped.get();
+        var opt = equip.getCosmetic();
+        
+        if(!(opt instanceof ItemCosmetic hat)) {
+            return;
+        }
+
+        final var nmsItem = CraftItemStack.asNMSCopy(hat.getCosmetic(equip));
+        
+        var slots = (List<Pair<EquipmentSlot,net.minecraft.world.item.ItemStack>>) packet.getSlots();
+        List<Pair<EquipmentSlot,net.minecraft.world.item.ItemStack>> newSlots = new ArrayList<>();
+
+        boolean foundHead = false;
+        for(var pair : slots) {
+            final EquipmentSlot slot = pair.getFirst();
+            
+            if(slot == EquipmentSlot.HEAD) {
+                foundHead = true;
+                newSlots.add(Pair.of(pair.getFirst(), nmsItem));
+            } else {
+                newSlots.add(pair);
+            }
+        }
+        if(!foundHead) {
+            newSlots.add(Pair.of(EquipmentSlot.HEAD, nmsItem));
+        }
+        
+        refEq.set(packet, "c", newSlots);
+    }
+
+}
diff --git a/v1_19_R2/src/main/java/io/lumine/cosmetics/nms/v1_19_R2/cosmetic/VolatileOffhandImpl.java b/v1_19_R2/src/main/java/io/lumine/cosmetics/nms/v1_19_R2/cosmetic/VolatileOffhandImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..0a7b6271f6415fb69b7a97d89c5349a17a461c0d
--- /dev/null
+++ b/v1_19_R2/src/main/java/io/lumine/cosmetics/nms/v1_19_R2/cosmetic/VolatileOffhandImpl.java
@@ -0,0 +1,173 @@
+package io.lumine.cosmetics.nms.v1_19_R2.cosmetic;
+
+import com.google.common.collect.Maps;
+import com.mojang.datafixers.util.Pair;
+import io.lumine.cosmetics.MCCosmeticsPlugin;
+import io.lumine.cosmetics.api.cosmetics.EquippedCosmetic;
+import io.lumine.cosmetics.api.cosmetics.ItemCosmetic;
+import io.lumine.cosmetics.api.players.CosmeticProfile;
+import io.lumine.cosmetics.api.players.wardrobe.Mannequin;
+import io.lumine.cosmetics.managers.hats.Hat;
+import io.lumine.cosmetics.managers.offhand.Offhand;
+import io.lumine.cosmetics.nms.VolatileCodeEnabled_v1_19_R2;
+import io.lumine.cosmetics.nms.cosmetic.VolatileEquipmentHelper;
+import io.lumine.cosmetics.players.Profile;
+import io.lumine.utils.reflection.Reflector;
+import io.netty.buffer.Unpooled;
+import lombok.Getter;
+import net.minecraft.network.FriendlyByteBuf;
+import net.minecraft.network.protocol.game.ClientboundAddPlayerPacket;
+import net.minecraft.network.protocol.game.ClientboundSetEquipmentPacket;
+import net.minecraft.world.entity.EquipmentSlot;
+import org.bukkit.craftbukkit.v1_19_R2.entity.CraftPlayer;
+import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftItemStack;
+import org.bukkit.entity.Player;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class VolatileOffhandImpl implements VolatileEquipmentHelper {
+
+    @Getter private final MCCosmeticsPlugin plugin;
+    private final VolatileCodeEnabled_v1_19_R2 nmsHandler;
+    private final Map<Integer, Player> playerTracker = Maps.newConcurrentMap();
+
+    public VolatileOffhandImpl(MCCosmeticsPlugin plugin, VolatileCodeEnabled_v1_19_R2 nmsHandler) {
+        this.plugin = plugin;
+        this.nmsHandler = nmsHandler;
+    }
+    
+    @Override
+    public void apply(CosmeticProfile profile) {
+
+        if (profile == null)
+            return;
+
+        Player player = profile.getPlayer();
+       
+        final var maybeEquipped = profile.getEquipped(Offhand.class);
+        if(maybeEquipped.isEmpty()) {
+            return;
+        }
+        var equip = maybeEquipped.get();
+        var opt = equip.getCosmetic();
+        
+        if(!(opt instanceof ItemCosmetic offhand))
+            return;
+
+        var nmsOffhand = CraftItemStack.asNMSCopy(offhand.getCosmetic(equip));
+
+        playerTracker.put(player.getEntityId(), player);
+
+        ClientboundSetEquipmentPacket equipmentPacket = new ClientboundSetEquipmentPacket(player.getEntityId(), List.of(Pair.of(EquipmentSlot.OFFHAND, nmsOffhand)));
+
+        nmsHandler.broadcastAroundAndSelf(player, equipmentPacket);
+
+    }
+
+    @Override
+    public void unapply(CosmeticProfile profile) {
+        final var nmsPlayer = ((CraftPlayer) profile.getPlayer()).getHandle();
+        final var item = nmsPlayer.getItemBySlot(EquipmentSlot.OFFHAND);
+        ClientboundSetEquipmentPacket equipmentPacket = new ClientboundSetEquipmentPacket(nmsPlayer.getId(), List.of(Pair.of(EquipmentSlot.OFFHAND, item)));
+        nmsHandler.broadcastAroundAndSelf(nmsPlayer.getBukkitEntity(), equipmentPacket);
+    }
+    
+    @Override 
+    public void equipMannequin(Mannequin mannequin, EquippedCosmetic cosmetic) {
+        if(!(cosmetic.getCosmetic() instanceof Offhand offhand)) {
+            return;
+        }
+        
+        final var entityId = mannequin.getEntityId();
+        final var player = mannequin.getPlayer();
+        
+        var nmsHat = CraftItemStack.asNMSCopy(offhand.getCosmetic(cosmetic));
+        
+        ClientboundSetEquipmentPacket equipmentPacket = new ClientboundSetEquipmentPacket(entityId, List.of(Pair.of(EquipmentSlot.OFFHAND, nmsHat)));
+
+        nmsHandler.broadcast(player, equipmentPacket);
+    }
+
+    @Override
+    public void unequipMannequin(Mannequin mannequin) {
+        final var nmsPlayer = ((CraftPlayer) mannequin.getPlayer()).getHandle();
+        final var item = nmsPlayer.getItemBySlot(EquipmentSlot.OFFHAND);
+        ClientboundSetEquipmentPacket equipmentPacket = new ClientboundSetEquipmentPacket(nmsPlayer.getId(), List.of(Pair.of(EquipmentSlot.OFFHAND, item)));
+        nmsHandler.broadcast(nmsPlayer.getBukkitEntity(), equipmentPacket);
+    }
+
+    @Override
+    public List<Object> write(Player receiver, Object packet) {
+        if(packet instanceof ClientboundSetEquipmentPacket equipmentPacket) {
+            int id = equipmentPacket.getEntity();
+            Profile profile = getProfile(receiver, id);
+            if(profile != null && !profile.isHidden(Offhand.class))
+                modifyPacket(profile, equipmentPacket);
+        }
+
+        return null;
+    }
+
+    private Profile getProfile(Player receiver, int id) {
+        final var entity = nmsHandler.getEntity(receiver.getWorld(), id);
+        if(!(entity instanceof Player player))
+            return null;
+        return plugin.getProfiles().getProfile(player);
+    }
+
+    public void handleSpawn(Profile profile) {
+        final var maybeEquipped = profile.getEquipped(Offhand.class);
+        if(maybeEquipped.isEmpty()) {
+            return;
+        }
+        var equip = maybeEquipped.get();
+        var opt = equip.getCosmetic();
+        
+        if(!(opt instanceof ItemCosmetic offhand))
+            return;
+
+        final var player = profile.getPlayer();
+        final var nmsOffhand = CraftItemStack.asNMSCopy(offhand.getCosmetic(equip));
+        ClientboundSetEquipmentPacket equipmentPacket = new ClientboundSetEquipmentPacket(player.getEntityId(), List.of(Pair.of(EquipmentSlot.OFFHAND, nmsOffhand)));
+        nmsHandler.broadcastAroundAndSelf(player, equipmentPacket);
+    }
+    
+    private static Reflector<ClientboundSetEquipmentPacket> refEq = new Reflector(ClientboundSetEquipmentPacket.class, "c");
+    
+    private void modifyPacket(Profile profile, ClientboundSetEquipmentPacket packet) {
+        final var maybeEquipped = profile.getEquipped(Hat.class);
+        if(maybeEquipped.isEmpty()) {
+            return;
+        }
+        var equip = maybeEquipped.get();
+        var opt = equip.getCosmetic();
+        
+        if(!(opt instanceof ItemCosmetic hat)) {
+            return;
+        }
+
+        final var nmsItem = CraftItemStack.asNMSCopy(hat.getCosmetic(equip));
+        
+        var slots = (List<Pair<EquipmentSlot,net.minecraft.world.item.ItemStack>>) packet.getSlots();
+        List<Pair<EquipmentSlot,net.minecraft.world.item.ItemStack>> newSlots = new ArrayList<>();
+
+        boolean foundHead = false;
+        for(var pair : slots) {
+            final EquipmentSlot slot = pair.getFirst();
+            
+            if(slot == EquipmentSlot.HEAD) {
+                foundHead = true;
+                newSlots.add(Pair.of(pair.getFirst(), nmsItem));
+            } else {
+                newSlots.add(pair);
+            }
+        }
+        if(!foundHead) {
+            newSlots.add(Pair.of(EquipmentSlot.HEAD, nmsItem));
+        }
+        refEq.set(packet, "c", newSlots);
+    }
+
+}
diff --git a/v1_19_R2/src/main/java/io/lumine/cosmetics/nms/v1_19_R2/cosmetic/VolatileSprayImpl.java b/v1_19_R2/src/main/java/io/lumine/cosmetics/nms/v1_19_R2/cosmetic/VolatileSprayImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..57e60298334d89d787eb3dda63bc53cf9e0a5584
--- /dev/null
+++ b/v1_19_R2/src/main/java/io/lumine/cosmetics/nms/v1_19_R2/cosmetic/VolatileSprayImpl.java
@@ -0,0 +1,90 @@
+package io.lumine.cosmetics.nms.v1_19_R2.cosmetic;
+
+import io.lumine.cosmetics.MCCosmeticsPlugin;
+import io.lumine.cosmetics.managers.sprays.Spray;
+import io.lumine.cosmetics.nms.VolatileCodeEnabled_v1_19_R2;
+import io.lumine.cosmetics.nms.cosmetic.VolatileSprayHelper;
+import lombok.Getter;
+import net.minecraft.core.BlockPos;
+import net.minecraft.core.Direction;
+import net.minecraft.network.protocol.game.ClientboundAddEntityPacket;
+import net.minecraft.network.protocol.game.ClientboundMapItemDataPacket;
+import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket;
+import net.minecraft.world.entity.decoration.ItemFrame;
+import net.minecraft.world.level.saveddata.maps.MapItemSavedData;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.block.BlockFace;
+import org.bukkit.craftbukkit.v1_19_R2.CraftWorld;
+import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftItemStack;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.MapMeta;
+
+public class VolatileSprayImpl implements VolatileSprayHelper {
+
+    @Getter private final MCCosmeticsPlugin plugin;
+    private final VolatileCodeEnabled_v1_19_R2 nmsHandler;
+
+    public VolatileSprayImpl(MCCosmeticsPlugin plugin, VolatileCodeEnabled_v1_19_R2 nmsHandler) {
+        this.plugin = plugin;
+        this.nmsHandler = nmsHandler;
+    }
+
+    private static final ItemStack map = new ItemStack(Material.FILLED_MAP);
+    private static final MapMeta mapMeta = (MapMeta) map.getItemMeta();
+    
+    @Override
+    public int drawSpray(Spray spray, Location location, BlockFace face, int rotation) {
+        var world = ((CraftWorld) location.getWorld()).getHandle();
+        var pos = new BlockPos(location.getX(), location.getY(), location.getZ());
+
+        var image = spray.getImage();
+        
+        mapMeta.setMapId(image.getMapNumber());
+        map.setItemMeta(mapMeta);
+        
+        var nmsMap = CraftItemStack.asNMSCopy(map);
+
+        final int dir = switch(face) {
+            case DOWN  -> 0;
+            case UP    -> 1;
+            case NORTH -> 2;
+            case SOUTH -> 3;
+            case WEST  -> 4;
+            case EAST  -> 5;
+            default    -> 1;
+        };
+        
+        var frame = new ItemFrame(world, pos, Direction.UP);
+        frame.setItem(nmsMap);
+        frame.setInvisible(true);
+        frame.setRotation(rotation);
+
+        var packetAdd = new ClientboundAddEntityPacket(frame, dir);
+        var packetData = new ClientboundSetEntityDataPacket(frame.getId(), frame.getEntityData().getNonDefaultValues());
+        var packetMap = constructMapPacket(image.getMapNumber(), image.getPixels());
+        
+        nmsHandler.broadcast(packetAdd, packetData, packetMap);
+        
+        return frame.getId();
+    }
+    
+    private static final int startX = 0;
+    private static final int startY = 0;
+    private static final int mapWidth = 128;
+    private static final int mapHeight = 128;
+    private static final byte mapScale = 1;
+    private static final boolean mapLocked = true;
+    
+    private ClientboundMapItemDataPacket constructMapPacket(int id, byte[] pixels) {
+        var mapData = constructMapData(startX, startY, mapWidth, mapHeight, pixels);
+        var packet = new ClientboundMapItemDataPacket(id, mapScale, mapLocked, null, mapData);
+
+        return packet;
+    }
+    
+    private MapItemSavedData.MapPatch constructMapData(int startX, int startY, int width, int height, byte[] pixels) {
+        return new MapItemSavedData.MapPatch(startX, startY, width, height, pixels);
+    }
+
+}
diff --git a/v1_19_R2/src/main/java/io/lumine/cosmetics/nms/v1_19_R2/network/VolatileChannelHandler.java b/v1_19_R2/src/main/java/io/lumine/cosmetics/nms/v1_19_R2/network/VolatileChannelHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..017ae708279545c0ff73e661ede9817f16e6fda6
--- /dev/null
+++ b/v1_19_R2/src/main/java/io/lumine/cosmetics/nms/v1_19_R2/network/VolatileChannelHandler.java
@@ -0,0 +1,61 @@
+package io.lumine.cosmetics.nms.v1_19_R2.network;
+
+import io.lumine.cosmetics.nms.VolatileCodeEnabled_v1_19_R2;
+import io.netty.channel.ChannelDuplexHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelPromise;
+import lombok.Getter;
+import org.bukkit.entity.Player;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class VolatileChannelHandler extends ChannelDuplexHandler {
+
+	private final VolatileCodeEnabled_v1_19_R2 nmsHandler;
+	@Getter private final Player player;
+
+	public VolatileChannelHandler(Player player, VolatileCodeEnabled_v1_19_R2 nmsHandler) {
+		this.player = player;
+		this.nmsHandler = nmsHandler;
+	}
+
+	@Override
+	public void write(ChannelHandlerContext ctx, Object obj, ChannelPromise promise) {
+
+		try {
+
+			List<Object> packets = new ArrayList<>();
+
+			for(final var helper : nmsHandler.getCosmeticHelpers()) {
+				final var writes = helper.write(player, obj);
+				if(writes != null)
+					packets.addAll(writes);
+			}
+
+			if(!packets.contains(obj))
+				super.write(ctx, obj, promise);
+			packets.remove(obj);
+
+			for (var p : packets) {
+				super.write(ctx, p, promise.channel().newPromise());
+			}
+		}catch (Exception e) {
+			e.printStackTrace();
+		}
+
+	}
+
+	@Override
+	public void channelRead(ChannelHandlerContext ctx, Object obj) throws Exception {
+
+		boolean isCanceled = false;
+		for(final var helper : nmsHandler.getCosmeticHelpers()) {
+			isCanceled |= !helper.read(player, obj, isCanceled);
+		}
+
+		if(!isCanceled)
+			super.channelRead(ctx, obj);
+	}
+
+}
diff --git a/v1_19_R2/src/main/java/io/lumine/cosmetics/nms/v1_19_R2/wardrobe/MannequinEntity.java b/v1_19_R2/src/main/java/io/lumine/cosmetics/nms/v1_19_R2/wardrobe/MannequinEntity.java
new file mode 100644
index 0000000000000000000000000000000000000000..92b8e21f4c037cc09d71ac491276073b0b6524f9
--- /dev/null
+++ b/v1_19_R2/src/main/java/io/lumine/cosmetics/nms/v1_19_R2/wardrobe/MannequinEntity.java
@@ -0,0 +1,132 @@
+package io.lumine.cosmetics.nms.v1_19_R2.wardrobe;
+
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import org.bukkit.Location;
+import org.bukkit.craftbukkit.v1_19_R2.CraftWorld;
+import org.bukkit.craftbukkit.v1_19_R2.entity.CraftPlayer;
+import org.bukkit.entity.Player;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.mojang.authlib.GameProfile;
+import com.mojang.datafixers.util.Pair;
+
+import io.lumine.cosmetics.api.cosmetics.Cosmetic;
+import io.lumine.cosmetics.api.players.wardrobe.Mannequin;
+import io.lumine.cosmetics.api.players.wardrobe.WardrobeTracker;
+import io.lumine.cosmetics.nms.VolatileCodeEnabled_v1_19_R2;
+import io.netty.buffer.Unpooled;
+import lombok.Getter;
+import net.minecraft.network.FriendlyByteBuf;
+import net.minecraft.network.protocol.game.ClientboundAddPlayerPacket;
+import net.minecraft.network.protocol.game.ClientboundAnimatePacket;
+import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket;
+import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket;
+import net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket;
+import net.minecraft.network.protocol.game.ClientboundRotateHeadPacket;
+import net.minecraft.network.protocol.game.ClientboundSetEquipmentPacket;
+import net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket;
+import net.minecraft.server.level.ServerPlayer;
+import net.minecraft.world.entity.EquipmentSlot;
+
+public class MannequinEntity implements Mannequin {
+
+    private final VolatileCodeEnabled_v1_19_R2 handler;
+    @Getter private final WardrobeTracker tracker;
+    @Getter private final UUID uniqueId;
+    @Getter private final Player player;
+    @Getter private final Location location;
+    @Getter private float rotation;
+    
+    @Getter private ServerPlayer fakePlayer;
+    private Map<Class<? extends Cosmetic>,Integer> extraEntities = Maps.newConcurrentMap();
+
+    public MannequinEntity(WardrobeTracker tracker, VolatileCodeEnabled_v1_19_R2 handler, Player player, Location location) {
+        this.tracker = tracker;
+        this.handler = handler;
+        this.player = player;
+        this.location = location;
+        this.uniqueId = UUID.randomUUID();
+        
+        var serverPlayer = ((CraftPlayer) player).getHandle();
+        var level = ((CraftWorld) location.getWorld()).getHandle();
+
+        this.rotation = serverPlayer.getYRot() + 180;
+        
+        if(rotation > 180) { 
+            rotation = rotation - 360;
+        }
+        
+        var gameProfile = serverPlayer.getGameProfile();
+        
+        gameProfile = new GameProfile(uniqueId, "Wardrobe");
+        gameProfile.getProperties().putAll(serverPlayer.getGameProfile().getProperties());
+        
+        this.fakePlayer = new ServerPlayer(level.getServer(), level, gameProfile);
+        
+        var spawn = new FriendlyByteBuf(Unpooled.buffer());
+        
+        spawn.writeVarInt(fakePlayer.getId());
+        spawn.writeUUID(fakePlayer.getUUID());
+        spawn.writeDouble(location.getX());
+        spawn.writeDouble(location.getY());
+        spawn.writeDouble(location.getZ());
+        spawn.writeByte((byte)(int)(serverPlayer.getYRot() * 256.0F / 360.0F));
+        spawn.writeByte((byte)(int)(serverPlayer.getXRot() * 256.0F / 360.0F));
+
+        var packetPlayerInfo = new ClientboundPlayerInfoUpdatePacket(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER, fakePlayer);
+        var packetAddPlayer = new ClientboundAddPlayerPacket(spawn);
+
+        var equipmentPacket = new ClientboundSetEquipmentPacket(fakePlayer.getId(), 
+                List.of(Pair.of(EquipmentSlot.HEAD, serverPlayer.getItemBySlot(EquipmentSlot.HEAD)),
+                        Pair.of(EquipmentSlot.CHEST, serverPlayer.getItemBySlot(EquipmentSlot.CHEST)),
+                        Pair.of(EquipmentSlot.LEGS, serverPlayer.getItemBySlot(EquipmentSlot.LEGS)),
+                        Pair.of(EquipmentSlot.FEET, serverPlayer.getItemBySlot(EquipmentSlot.FEET)),
+                        Pair.of(EquipmentSlot.MAINHAND, serverPlayer.getItemBySlot(EquipmentSlot.MAINHAND)),
+                        Pair.of(EquipmentSlot.OFFHAND, serverPlayer.getItemBySlot(EquipmentSlot.OFFHAND))));
+
+        var head = (byte)(int)(rotation * 256.0F / 360.0F);
+        var rotatePacket = new ClientboundRotateHeadPacket(fakePlayer, head);
+        
+        var swingPacket = new ClientboundAnimatePacket(fakePlayer, 0);
+        
+        handler.broadcast(player, packetPlayerInfo, packetAddPlayer, equipmentPacket, rotatePacket, swingPacket);
+        
+        for(var eq : handler.getPlugin().getProfiles().getProfile(player).getEquipped().values()) {
+            eq.getCosmetic().getManager().equipMannequin(this, eq);
+        }
+    }
+    
+    public void despawn() {
+        var packetPlayerInfo = new ClientboundPlayerInfoRemovePacket(Lists.newArrayList(fakePlayer.getUUID()));
+        var packetRemovePlayer = new ClientboundRemoveEntitiesPacket(fakePlayer.getId());
+        
+        handler.broadcast(player, packetPlayerInfo, packetRemovePlayer);
+        
+        for(var i : extraEntities.values()) {
+            var removePacket = new ClientboundRemoveEntitiesPacket(i);
+            handler.broadcast(player, removePacket);
+        }
+    }
+    
+    public void addExtraEntity(Class<? extends Cosmetic> type, int id) {
+        extraEntities.put(type, id);
+    }
+    
+    public void removeExtraEntity(Class<? extends Cosmetic> type) {
+        var id = extraEntities.remove(type);
+        
+        if(id != null) {
+            var removePacket = new ClientboundRemoveEntitiesPacket(id);
+            handler.broadcast(player, removePacket);
+        }
+    }
+    
+    public int getEntityId() {
+        return fakePlayer.getId();
+    }
+
+}