diff --git a/build.gradle.kts b/build.gradle.kts
index 9b61f98b1..6afe1dc86 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -26,3 +26,22 @@ ext["lwjglNatives"] = Pair(
             throw Error("Unrecognized or unsupported platform. Please set \"lwjglNatives\" manually")
     }
 }
+
+plugins {
+    id("java")
+}
+
+
+
+val genJavadoc by tasks.registering(Javadoc::class) {
+    val exposedProjects = listOf(
+        ":game:core",
+        ":game:client",
+        ":game:server",
+    )
+    val sourceSets = exposedProjects.map { project(it).sourceSets["main"] }
+    classpath = files(sourceSets.map { it.compileClasspath })
+    setSource(exposedProjects.map { project(it).sourceSets["main"].allJava })
+    setDestinationDir(file("${layout.buildDirectory.get()}/docs/javadoc"))
+    print(destinationDir)
+}
\ No newline at end of file
diff --git a/game/client/build.gradle.kts b/game/client/build.gradle.kts
index 5e85d1edc..1e3053fb0 100644
--- a/game/client/build.gradle.kts
+++ b/game/client/build.gradle.kts
@@ -45,7 +45,7 @@ dependencies {
     implementation(project(":util:logging"))?.let { include(it) }
     // </core dependencies>
 
-    implementation("com.github.Better-than-Adventure:legacy-lwjgl3:1.0.5")?.let { include(it) }
+    implementation("com.github.Better-than-Adventure:legacy-lwjgl3:1.0.6")?.let { include(it) }
 
     implementation("org.apache-extras.beanshell:bsh:2.0b6")?.let { include(it) }
     implementation("com.deepoove:codehighlight:1.0.3")?.let { include(it) }
diff --git a/game/client/src/main/java/net/minecraft/client/Minecraft.java b/game/client/src/main/java/net/minecraft/client/Minecraft.java
index 3b5c24ef7..b4018277a 100644
--- a/game/client/src/main/java/net/minecraft/client/Minecraft.java
+++ b/game/client/src/main/java/net/minecraft/client/Minecraft.java
@@ -1047,7 +1047,7 @@ public class Minecraft
                     if (this.currentScreen instanceof ScreenSelectServer) {
                         this.currentScreen.tick();
                     }
-                	
+
                     for(int tick = 0; tick < timer.frames; tick++)
                     {
                         ticksRan++;
@@ -1371,7 +1371,7 @@ public class Minecraft
                     if (stack == null) {
                         break bridge;
                     }
-                    if (this.playerController.onlyUseItemStackOn(thePlayer, currentWorld, stack, blockX, blockY, blockZ, side, xPlaced, yPlaced)) {
+                    if (this.playerController.placeItemStackOnTile(thePlayer, currentWorld, stack, blockX, blockY, blockZ, side, xPlaced, yPlaced)) {
                         this.playerController.swingItem(true);
                     }
                     if (stack.stackSize <= 0) {
@@ -1426,7 +1426,7 @@ public class Minecraft
             {
                 ItemStack stack = thePlayer.inventory.getCurrentItem();
                 int numItemsInStack = stack == null ? 0 : stack.stackSize;
-                if(playerController.useItemOn(thePlayer, currentWorld, stack, blockX, blockY, blockZ, side, xPlaced, yPlaced))
+                if(playerController.useOrPlaceItemStackOnTile(thePlayer, currentWorld, stack, blockX, blockY, blockZ, side, xPlaced, yPlaced))
                 {
                     flag = false;
                     playerController.swingItem(true);
@@ -1448,7 +1448,7 @@ public class Minecraft
         if(flag && clickType == 1)
         {
             ItemStack itemstack = thePlayer.inventory.getCurrentItem();
-            if(itemstack != null && playerController.useItem(thePlayer, currentWorld, itemstack))
+            if(itemstack != null && playerController.useItemStackOnNothing(thePlayer, currentWorld, itemstack))
             {
                 worldRenderer.itemRenderer.resetEquippedProgress();
             }
diff --git a/game/client/src/main/java/net/minecraft/client/gui/ScreenSignEditor.java b/game/client/src/main/java/net/minecraft/client/gui/ScreenSignEditor.java
index 0c2126d44..0ca398904 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/ScreenSignEditor.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/ScreenSignEditor.java
@@ -4,16 +4,16 @@ import net.minecraft.client.gui.keyboard.ScreenKeyboard;
 import net.minecraft.client.gui.modelviewer.elements.ListenerButtonElement;
 import net.minecraft.client.input.InputType;
 import net.minecraft.client.input.controller.ControllerInput;
+import net.minecraft.client.render.TileEntityRenderDispatcher;
+import net.minecraft.client.render.tessellator.Tessellator;
 import net.minecraft.core.block.Block;
 import net.minecraft.core.block.BlockLogicSign;
 import net.minecraft.core.block.Blocks;
 import net.minecraft.core.block.entity.TileEntitySign;
 import net.minecraft.core.enums.EnumSignPicture;
-import net.minecraft.core.util.helper.ChatAllowedCharacters;
-import net.minecraft.core.net.packet.PacketSignUpdate;
-import net.minecraft.client.render.TileEntityRenderDispatcher;
-import net.minecraft.client.render.tessellator.Tessellator;
 import net.minecraft.core.lang.I18n;
+import net.minecraft.core.net.packet.PacketSignUpdate;
+import net.minecraft.core.util.helper.ChatAllowedCharacters;
 import org.lwjgl.input.Keyboard;
 import org.lwjgl.opengl.GL11;
 
@@ -63,7 +63,7 @@ public class ScreenSignEditor extends Screen
     {
         Keyboard.enableRepeatEvents(false);
         // I have no idea how the hell the world is null sometimes
-        // but thaboar was getting a crash from the world being null
+        // but thaboard was getting a crash from the world being null
         if(mc.currentWorld != null && mc.currentWorld.isClientSide)
         {
             mc.getSendQueue().addToSendQueue(new PacketSignUpdate(entitySign.x, entitySign.y, entitySign.z, entitySign.signText, entitySign.getPicture().getId(), entitySign.getColor().id));
diff --git a/game/client/src/main/java/net/minecraft/client/gui/TooltipElement.java b/game/client/src/main/java/net/minecraft/client/gui/TooltipElement.java
index 09a62b274..25fb3a196 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/TooltipElement.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/TooltipElement.java
@@ -13,6 +13,7 @@ import net.minecraft.client.render.texture.stitcher.IconCoordinate;
 import net.minecraft.client.render.texture.stitcher.TextureRegistry;
 import net.minecraft.client.render.texturepack.TexturePack;
 import net.minecraft.client.render.texturepack.TexturePackList;
+import net.minecraft.client.option.enums.DescriptionPromptEnum;
 import net.minecraft.core.WeightedRandomBag;
 import net.minecraft.core.data.registry.recipe.entry.RecipeEntryTrommel;
 import net.minecraft.core.data.registry.recipe.RecipeSymbol;
@@ -220,7 +221,7 @@ public class TooltipElement extends Gui
 
 			if(showDescription) {
 				text.append('\n').append(formatDescription(itemStack.getItemDescription(), 16));
-			} else if (!(slot instanceof SlotResult)) {
+			} else if (!(slot instanceof SlotResult) && mc.gameSettings.itemDescriptions.value != DescriptionPromptEnum.NEVER_PROMPT) {
                 text.append('\n').append(formatDescription(trans.translateKeyAndFormat("gui.tooltip.prompt.description", mc.gameSettings.keyDescription.getKeyName()), 16));
             }
 
diff --git a/game/client/src/main/java/net/minecraft/client/gui/container/ScreenContainerAbstract.java b/game/client/src/main/java/net/minecraft/client/gui/container/ScreenContainerAbstract.java
index b0cd70b6c..970ce5186 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/container/ScreenContainerAbstract.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/container/ScreenContainerAbstract.java
@@ -11,6 +11,7 @@ import net.minecraft.client.gui.guidebook.GuidebookSections;
 import net.minecraft.client.gui.guidebook.SearchableGuidebookSection;
 import net.minecraft.client.gui.guidebook.search.GuidebookPageSearch;
 import net.minecraft.client.input.controller.ControllerInput;
+import net.minecraft.client.option.enums.DescriptionPromptEnum;
 import net.minecraft.client.util.helper.ItemDragHandler;
 import net.minecraft.core.InventoryAction;
 import net.minecraft.core.block.entity.TileEntityFurnaceBlast;
@@ -112,7 +113,7 @@ public abstract class ScreenContainerAbstract extends Screen
 		GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
 		if (inventoryplayer.getHeldItemStack() == null && slot != null && slot.hasItem())
 		{
-			boolean showDescription = mc.gameSettings.alwaysShowDescriptions.value || mc.gameSettings.keyDescription.isPressed();
+			boolean showDescription = DescriptionPromptEnum.showDescription(mc);
 			String str = tooltipElement.getTooltipText(slot.getItemStack(), showDescription, slot);
 			if(str.length() > 0)
 			{
diff --git a/game/client/src/main/java/net/minecraft/client/gui/guidebook/crafting/RecipePageCrafting.java b/game/client/src/main/java/net/minecraft/client/gui/guidebook/crafting/RecipePageCrafting.java
index 2df94b267..9de269fac 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/guidebook/crafting/RecipePageCrafting.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/guidebook/crafting/RecipePageCrafting.java
@@ -8,6 +8,7 @@ import net.minecraft.client.gui.guidebook.GuidebookSection;
 import net.minecraft.client.gui.guidebook.GuidebookSections;
 import net.minecraft.client.gui.guidebook.GuidebookPageManager;
 import net.minecraft.client.gui.guidebook.RecipePage;
+import net.minecraft.client.gui.guidebook.crafting.displays.DisplayAdapterDyeing;
 import net.minecraft.client.gui.guidebook.crafting.displays.DisplayAdapterLabel;
 import net.minecraft.client.gui.guidebook.crafting.displays.DisplayAdapterLabelDye;
 import net.minecraft.client.gui.guidebook.crafting.displays.DisplayAdapterMapDuplication;
@@ -19,6 +20,7 @@ import net.minecraft.client.gui.guidebook.crafting.displays.DisplayAdapterShapel
 import net.minecraft.client.gui.guidebook.crafting.displays.DisplayAdapterTool;
 import net.minecraft.client.gui.guidebook.crafting.displays.DisplayAdapterUndye;
 import net.minecraft.client.gui.guidebook.search.GuidebookPageSearch;
+import net.minecraft.client.option.enums.DescriptionPromptEnum;
 import net.minecraft.client.render.TextureManager;
 import net.minecraft.client.render.font.FontRenderer;
 import net.minecraft.core.achievement.Achievements;
@@ -28,6 +30,7 @@ import net.minecraft.core.data.registry.recipe.entry.RecipeEntryCrafting;
 import net.minecraft.core.data.registry.recipe.entry.RecipeEntryCraftingShaped;
 import net.minecraft.core.data.registry.recipe.entry.RecipeEntryCraftingShapeless;
 import net.minecraft.core.data.registry.recipe.entry.RecipeEntryCraftingWithTool;
+import net.minecraft.core.data.registry.recipe.entry.RecipeEntryDyeing;
 import net.minecraft.core.data.registry.recipe.entry.RecipeEntryLabel;
 import net.minecraft.core.data.registry.recipe.entry.RecipeEntryLabelDye;
 import net.minecraft.core.data.registry.recipe.entry.RecipeEntryMapDuplication;
@@ -65,6 +68,7 @@ public class RecipePageCrafting
         recipeToDisplayAdapterMap.put(RecipeEntryLabelDye.class, new DisplayAdapterLabelDye());
         recipeToDisplayAdapterMap.put(RecipeEntryMapDuplication.class, new DisplayAdapterMapDuplication());
         recipeToDisplayAdapterMap.put(RecipeEntryUndyeing.class, new DisplayAdapterUndye());
+        recipeToDisplayAdapterMap.put(RecipeEntryDyeing.class, new DisplayAdapterDyeing());
     }
 
     private final TooltipElement tooltipElement;
@@ -192,7 +196,7 @@ public class RecipePageCrafting
             GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
             if (mouseOverSlot != null && mouseOverSlot.hasItem())
             {
-                boolean showDescription = mc.gameSettings.alwaysShowDescriptions.value || mc.gameSettings.keyDescription.isPressed();
+                boolean showDescription = DescriptionPromptEnum.showDescription(mc);
                 String str = tooltipElement.getTooltipText(mouseOverSlot.getItemStack(), showDescription, mouseOverSlot);
                 if(!str.isEmpty())
                 {
diff --git a/game/client/src/main/java/net/minecraft/client/gui/guidebook/crafting/displays/DisplayAdapterDyeing.java b/game/client/src/main/java/net/minecraft/client/gui/guidebook/crafting/displays/DisplayAdapterDyeing.java
new file mode 100644
index 000000000..351da5211
--- /dev/null
+++ b/game/client/src/main/java/net/minecraft/client/gui/guidebook/crafting/displays/DisplayAdapterDyeing.java
@@ -0,0 +1,68 @@
+package net.minecraft.client.gui.guidebook.crafting.displays;
+
+import net.minecraft.client.gui.guidebook.SlotGuidebook;
+import net.minecraft.core.block.Blocks;
+import net.minecraft.core.data.registry.recipe.RecipeEntryBase;
+import net.minecraft.core.data.registry.recipe.RecipeSymbol;
+import net.minecraft.core.data.registry.recipe.entry.RecipeEntryDyeing;
+import net.minecraft.core.data.registry.recipe.entry.RecipeEntryUndyeing;
+import net.minecraft.core.item.ItemStack;
+import net.minecraft.core.item.Items;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+public class DisplayAdapterDyeing implements RecipeDisplayAdapter<RecipeEntryDyeing>{
+    @Override
+    public List<SlotGuidebook> getSlots(RecipeEntryDyeing recipe, int recipeAmount, int xOffset, int yOffset) {
+        List<SlotGuidebook> recipeSlots = new ArrayList<>();
+
+        int[] sharedMeta = new int[1];
+        int slotsAmount = 10;
+        for (int i = 0; i < slotsAmount - 1; i++)
+        {
+            if (i == 4) {
+                recipeSlots.add(new SlotGuidebookDye(i, 1 + 18 * (i % 3) + xOffset, 1 + 18 * ((i / 3)+recipeAmount*3) + yOffset, new RecipeSymbol(new ItemStack(Items.DYE, 1, 26)), false, recipe, sharedMeta));
+            } else {
+                RecipeSymbol stack = recipe.inputSymbol;
+                recipeSlots.add(new SlotGuidebook(i, 1 + 18 * (i % 3) + xOffset, 1 + 18 * ((i / 3)+recipeAmount*3) + yOffset, stack, false,recipe));
+            }
+        }
+
+        int centerY = (recipeSlots.get(recipeSlots.size()-1).y + recipeSlots.get(recipeSlots.size()-8).y) / 2;
+        recipeSlots.add(new SlotGuidebookDyeResult(slotsAmount - 1, 81 + xOffset, centerY, new RecipeSymbol(new ItemStack(recipe.output.getItem(), 8, 26)), false, recipe, sharedMeta).setAsOutput());
+
+        return recipeSlots;
+    }
+
+    public static class SlotGuidebookDye extends SlotGuidebook {
+        private final int[] metadata;
+        public SlotGuidebookDye(int id, int x, int y, RecipeSymbol symbol, boolean discovered, RecipeEntryBase<?, ?, ?> recipe, int[] sharedMeta) {
+            super(id, x, y, symbol, discovered, recipe);
+            this.metadata = sharedMeta;
+        }
+
+        @Override
+        public void showRandomItem() {
+            this.metadata[0] = new Random().nextInt(16);
+            item = new ItemStack(Items.DYE, 1, this.metadata[0]);
+        }
+    }
+
+    public static class SlotGuidebookDyeResult extends SlotGuidebook {
+        private final int[] metadata;
+        private final RecipeEntryDyeing recipeEntryDyeing;
+        public SlotGuidebookDyeResult(int id, int x, int y, RecipeSymbol symbol, boolean discovered, RecipeEntryDyeing recipe, int[] sharedMeta) {
+            super(id, x, y, symbol, discovered, recipe);
+            this.metadata = sharedMeta;
+            this.recipeEntryDyeing = recipe;
+        }
+
+        @Override
+        public void showRandomItem() {
+            super.showRandomItem();
+            this.item = new ItemStack(this.item.itemID, this.item.stackSize, recipeEntryDyeing.getOutputMetaFromInputMeta(this.metadata[0]));
+        }
+    }
+}
diff --git a/game/client/src/main/java/net/minecraft/client/gui/guidebook/crafting/displays/DisplayAdapterTool.java b/game/client/src/main/java/net/minecraft/client/gui/guidebook/crafting/displays/DisplayAdapterTool.java
index 99e4931e4..ba7c1fca1 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/guidebook/crafting/displays/DisplayAdapterTool.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/guidebook/crafting/displays/DisplayAdapterTool.java
@@ -51,7 +51,7 @@ public class DisplayAdapterTool implements RecipeDisplayAdapter<RecipeEntryCraft
                         newItem = list.get(r.nextInt(list.size()));
                     }
                 }
-                item = newItem;
+                item = new ItemStack(newItem);
             }
             item.setMetadata(r.nextInt(item.getMaxDamage()));
         }
diff --git a/game/client/src/main/java/net/minecraft/client/gui/guidebook/mobs/GuidebookPageMob.java b/game/client/src/main/java/net/minecraft/client/gui/guidebook/mobs/GuidebookPageMob.java
index af98df2dd..892b461cc 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/guidebook/mobs/GuidebookPageMob.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/guidebook/mobs/GuidebookPageMob.java
@@ -6,6 +6,7 @@ import net.minecraft.client.gui.ItemElement;
 import net.minecraft.client.gui.TooltipElement;
 import net.minecraft.client.gui.guidebook.GuidebookPage;
 import net.minecraft.client.gui.guidebook.GuidebookSection;
+import net.minecraft.client.option.enums.DescriptionPromptEnum;
 import net.minecraft.client.render.*;
 import net.minecraft.client.render.font.FontRenderer;
 import net.minecraft.client.render.tessellator.Tessellator;
@@ -280,7 +281,7 @@ public class GuidebookPageMob
             GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
             if (mouseOverSlot != null && mouseOverSlot.hasItem())
             {
-                boolean showDescription = mc.gameSettings.alwaysShowDescriptions.value || mc.gameSettings.keyDescription.isPressed();
+                boolean showDescription = DescriptionPromptEnum.showDescription(mc);
                 String str = tooltipElement.getTooltipText(mouseOverSlot.getItemStack(), showDescription, mouseOverSlot);
                 if(!str.isEmpty())
                 {
diff --git a/game/client/src/main/java/net/minecraft/client/gui/guidebook/smelting/RecipePageBlastSmelting.java b/game/client/src/main/java/net/minecraft/client/gui/guidebook/smelting/RecipePageBlastSmelting.java
index bafa22b81..8329a933b 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/guidebook/smelting/RecipePageBlastSmelting.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/guidebook/smelting/RecipePageBlastSmelting.java
@@ -9,6 +9,7 @@ import net.minecraft.client.gui.guidebook.GuidebookSection;
 import net.minecraft.client.gui.guidebook.GuidebookSections;
 import net.minecraft.client.gui.guidebook.RecipePage;
 import net.minecraft.client.gui.guidebook.search.GuidebookPageSearch;
+import net.minecraft.client.option.enums.DescriptionPromptEnum;
 import net.minecraft.client.render.TextureManager;
 import net.minecraft.client.render.font.FontRenderer;
 import net.minecraft.core.achievement.Achievements;
@@ -147,7 +148,7 @@ public class RecipePageBlastSmelting
             GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
             if (mouseOverSlot != null && mouseOverSlot.hasItem())
             {
-                boolean showDescription = mc.gameSettings.alwaysShowDescriptions.value || mc.gameSettings.keyDescription.isPressed();
+                boolean showDescription = DescriptionPromptEnum.showDescription(mc);
                 String str = tooltipElement.getTooltipText(mouseOverSlot.getItemStack(), showDescription, mouseOverSlot);
                 if(!str.isEmpty())
                 {
diff --git a/game/client/src/main/java/net/minecraft/client/gui/guidebook/smelting/RecipePageSmelting.java b/game/client/src/main/java/net/minecraft/client/gui/guidebook/smelting/RecipePageSmelting.java
index 461fe1cd7..d75779401 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/guidebook/smelting/RecipePageSmelting.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/guidebook/smelting/RecipePageSmelting.java
@@ -9,6 +9,7 @@ import net.minecraft.client.gui.guidebook.GuidebookSection;
 import net.minecraft.client.gui.guidebook.GuidebookSections;
 import net.minecraft.client.gui.guidebook.RecipePage;
 import net.minecraft.client.gui.guidebook.search.GuidebookPageSearch;
+import net.minecraft.client.option.enums.DescriptionPromptEnum;
 import net.minecraft.client.render.TextureManager;
 import net.minecraft.client.render.font.FontRenderer;
 import net.minecraft.core.achievement.Achievements;
@@ -147,7 +148,7 @@ public class RecipePageSmelting
             GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
             if (mouseOverSlot != null && mouseOverSlot.hasItem())
             {
-                boolean showDescription = mc.gameSettings.alwaysShowDescriptions.value || mc.gameSettings.keyDescription.isPressed();
+                boolean showDescription = DescriptionPromptEnum.showDescription(mc);
                 String str = tooltipElement.getTooltipText(mouseOverSlot.getItemStack(), showDescription, mouseOverSlot);
                 if(!str.isEmpty())
                 {
diff --git a/game/client/src/main/java/net/minecraft/client/gui/guidebook/trommeling/RecipePageTrommel.java b/game/client/src/main/java/net/minecraft/client/gui/guidebook/trommeling/RecipePageTrommel.java
index d8d6506b9..0910282f2 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/guidebook/trommeling/RecipePageTrommel.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/guidebook/trommeling/RecipePageTrommel.java
@@ -9,6 +9,7 @@ import net.minecraft.client.gui.guidebook.GuidebookSection;
 import net.minecraft.client.gui.guidebook.GuidebookSections;
 import net.minecraft.client.gui.guidebook.RecipePage;
 import net.minecraft.client.gui.guidebook.search.GuidebookPageSearch;
+import net.minecraft.client.option.enums.DescriptionPromptEnum;
 import net.minecraft.client.render.TextureManager;
 import net.minecraft.client.render.font.FontRenderer;
 import net.minecraft.core.WeightedRandomBag;
@@ -196,7 +197,7 @@ public class RecipePageTrommel
             GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
             if (mouseOverSlot != null && mouseOverSlot.hasItem())
             {
-                boolean showDescription = mc.gameSettings.alwaysShowDescriptions.value || mc.gameSettings.keyDescription.isPressed();
+                boolean showDescription = DescriptionPromptEnum.showDescription(mc);
                 String str = tooltipElement.getTooltipText(mouseOverSlot.getItemStack(), showDescription, mouseOverSlot);
                 if(!str.isEmpty())
                 {
diff --git a/game/client/src/main/java/net/minecraft/client/gui/options/data/OptionsPages.java b/game/client/src/main/java/net/minecraft/client/gui/options/data/OptionsPages.java
index b8bd2f426..38b60524c 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/options/data/OptionsPages.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/options/data/OptionsPages.java
@@ -82,7 +82,7 @@ public abstract class OptionsPages
                 .withComponent(new BooleanOptionComponent(mc.gameSettings.armorDurabilityOverlay))
                 .withComponent(new BooleanOptionComponent(mc.gameSettings.heldItemCountOverlay))
                 .withComponent(new BooleanOptionComponent(mc.gameSettings.heldItemTooltips))
-                .withComponent(new BooleanOptionComponent(mc.gameSettings.alwaysShowDescriptions))
+                .withComponent(new ToggleableOptionComponent(mc.gameSettings.itemDescriptions))
                 .withComponent(new BooleanOptionComponent(mc.gameSettings.hideUndiscoveredItems))
                 .withComponent(new BooleanOptionComponent(mc.gameSettings.dynamicCursor))
             )
diff --git a/game/client/src/main/java/net/minecraft/client/option/GameSettings.java b/game/client/src/main/java/net/minecraft/client/option/GameSettings.java
index a45da7a3a..51b6ece0e 100644
--- a/game/client/src/main/java/net/minecraft/client/option/GameSettings.java
+++ b/game/client/src/main/java/net/minecraft/client/option/GameSettings.java
@@ -20,6 +20,7 @@ import net.minecraft.client.gui.options.ScreenOptions;
 import net.minecraft.client.input.InputDevice;
 import net.minecraft.client.option.enums.Colorblindness;
 import net.minecraft.client.render.font.Fonts;
+import net.minecraft.client.option.enums.DescriptionPromptEnum;
 import net.minecraft.core.enums.ChatVisibility;
 import net.minecraft.core.enums.Difficulty;
 import net.minecraft.client.option.enums.LightmapStyle;
@@ -131,7 +132,7 @@ public class GameSettings {
 	public OptionBoolean heatHaze = new OptionBoolean(this, "heatHaze", false);
 	public OptionBoolean alphaMenu = new OptionBoolean(this, "old", false);
 	public OptionBoolean enableItemClumping = new OptionBoolean(this, "enableItemClumping", true);
-	public OptionBoolean alwaysShowDescriptions = new OptionBoolean(this, "alwaysShowDescriptions", false);
+	public OptionEnum itemDescriptions = new OptionEnum<>(this, "itemDescriptions", DescriptionPromptEnum.class, DescriptionPromptEnum.PROMPT);
 	public OptionBoolean turnWithVehicle = new OptionBoolean(this, "turnWithVehicle", true);
 	public OptionBoolean disableCrashHandler = new OptionBoolean(this, "disableCrashHandler", false);
 	public OptionBoolean enableMultiDrawRendering = new OptionBoolean(this, "enableMultiDrawRendering", true);
diff --git a/game/client/src/main/java/net/minecraft/client/option/enums/DescriptionPromptEnum.java b/game/client/src/main/java/net/minecraft/client/option/enums/DescriptionPromptEnum.java
new file mode 100644
index 000000000..16b1e0b24
--- /dev/null
+++ b/game/client/src/main/java/net/minecraft/client/option/enums/DescriptionPromptEnum.java
@@ -0,0 +1,14 @@
+package net.minecraft.client.option.enums;
+
+import net.minecraft.client.Minecraft;
+import org.jetbrains.annotations.NotNull;
+
+public enum DescriptionPromptEnum {
+    NEVER_PROMPT,
+    PROMPT,
+    ALWAYS_SHOW;
+
+    public static boolean showDescription(@NotNull final Minecraft minecraft) {
+        return (minecraft.gameSettings.itemDescriptions.value == DescriptionPromptEnum.ALWAYS_SHOW || minecraft.gameSettings.keyDescription.isPressed());
+    }
+}
diff --git a/game/client/src/main/java/net/minecraft/client/player/controller/PlayerController.java b/game/client/src/main/java/net/minecraft/client/player/controller/PlayerController.java
index 7d3ad0cc0..151533f9e 100644
--- a/game/client/src/main/java/net/minecraft/client/player/controller/PlayerController.java
+++ b/game/client/src/main/java/net/minecraft/client/player/controller/PlayerController.java
@@ -228,20 +228,6 @@ public abstract class PlayerController {
 		return mc.thePlayer.getGamemode().getEntityReachDistance();
 	}
 
-	public boolean useItem(Player player, World world, ItemStack stack) {
-		if (!player.getGamemode().canInteract()) return false;
-		int prevStackSize = stack.stackSize;
-		ItemStack newItemStack = stack.useItemRightClick(world, player);
-		if(newItemStack != stack || newItemStack.stackSize != prevStackSize) {
-			player.inventory.mainInventory[player.inventory.getCurrentItemIndex()] = newItemStack;
-			if(newItemStack == null || newItemStack.stackSize <= 0) {
-				player.inventory.mainInventory[player.inventory.getCurrentItemIndex()] = null;
-			}
-			return true;
-		}
-		return false;
-	}
-
 	public void initPlayer(Player player) {
 		player.yRot = -180F;
 	}
@@ -261,7 +247,12 @@ public abstract class PlayerController {
 	public void adjustPlayer(Player player) {
 	}
 
-	public boolean useItemOn(@NotNull Player player, @NotNull World world, @Nullable ItemStack itemstack, int blockX, int blockY, int blockZ, Side side, double xPlaced, double yPlaced)
+	/**
+	 * Used to dynamically use or place an ItemStack on any given tile.
+	 * <p>
+	 * (i.e. interacting with a chest while holding a block, defaulting to opening the chest when standing, vs. placing the block when crouched)
+	 */
+	public boolean useOrPlaceItemStackOnTile(@NotNull Player player, @NotNull World world, @Nullable ItemStack itemstack, int blockX, int blockY, int blockZ, Side side, double xPlaced, double yPlaced)
 	{
 		if (!player.getGamemode().canInteract()) return false;
 		int blockId = world.getBlockId(blockX, blockY, blockZ);
@@ -297,7 +288,31 @@ public abstract class PlayerController {
 		}
 	}
 
-	public boolean onlyUseItemStackOn(@NotNull Player player, @NotNull World world, @Nullable ItemStack itemstack, int blockX, int blockY, int blockZ, Side side, double xPlaced, double yPlaced) {
+	/**
+	 * Used to specifically use an ItemStack on nothing, most commonly defaulting to typical "item use" behavior.
+	 * <p>
+	 * (i.e. throwing a fishing rod or shooting a bow)
+	 */
+	public boolean useItemStackOnNothing(Player player, World world, ItemStack stack) {
+		if (!player.getGamemode().canInteract()) return false;
+		int prevStackSize = stack.stackSize;
+		ItemStack newItemStack = stack.useItemRightClick(world, player);
+		if(newItemStack != stack || newItemStack.stackSize != prevStackSize) {
+			player.inventory.mainInventory[player.inventory.getCurrentItemIndex()] = newItemStack;
+			if(newItemStack == null || newItemStack.stackSize <= 0) {
+				player.inventory.mainInventory[player.inventory.getCurrentItemIndex()] = null;
+			}
+			return true;
+		}
+		return false;
+	}
+
+	/**
+	 * Used to exclusively place an ItemStack. No interactions, just placing a held ItemStack.
+	 * <p>
+	 * (i.e. placing blocks using easy bridge)
+	 */
+	public boolean placeItemStackOnTile(@NotNull Player player, @NotNull World world, @Nullable ItemStack itemstack, int blockX, int blockY, int blockZ, Side side, double xPlaced, double yPlaced) {
 		if (!player.getGamemode().canInteract()) return false;
 		if (player.getHeldObject() == null) {
 			if (itemstack == null) { // No stack then do nothing
diff --git a/game/client/src/main/java/net/minecraft/client/player/controller/PlayerControllerMP.java b/game/client/src/main/java/net/minecraft/client/player/controller/PlayerControllerMP.java
index b33ccd74b..f0942a236 100644
--- a/game/client/src/main/java/net/minecraft/client/player/controller/PlayerControllerMP.java
+++ b/game/client/src/main/java/net/minecraft/client/player/controller/PlayerControllerMP.java
@@ -64,25 +64,24 @@ public class PlayerControllerMP extends PlayerController {
 	}
 
 	@Override
-	public boolean useItemOn(Player entityplayer, World world, ItemStack itemstack, int blockX, int blockY, int blockZ, Side side, double xPlaced, double yPlaced) {
+	public boolean useOrPlaceItemStackOnTile(Player entityplayer, World world, ItemStack itemstack, int blockX, int blockY, int blockZ, Side side, double xPlaced, double yPlaced) {
 		syncCurrentPlayItem();
-		netHandler.addToSendQueue(new PacketUseItem(blockX, blockY, blockZ, side.getDirection(), entityplayer.inventory.getCurrentItem(), xPlaced, yPlaced, PacketUseItem.TYPE_USE_ITEM_ON));
-		return super.useItemOn(entityplayer, world, itemstack, blockX, blockY, blockZ, side, xPlaced, yPlaced);
+		netHandler.addToSendQueue(new PacketUseOrPlaceItemStack(blockX, blockY, blockZ, side.getDirection(), entityplayer.inventory.getCurrentItem(), xPlaced, yPlaced, PacketUseOrPlaceItemStack.TYPE_USE_OR_PLACE_ON_TILE));
+		return super.useOrPlaceItemStackOnTile(entityplayer, world, itemstack, blockX, blockY, blockZ, side, xPlaced, yPlaced);
 	}
 
 	@Override
-	public boolean onlyUseItemStackOn(Player entityplayer, World world, ItemStack itemstack, int blockX, int blockY, int blockZ, Side side, double xPlaced, double yPlaced) {
+	public boolean useItemStackOnNothing(Player entityplayer, World world, ItemStack itemstack) {
 		syncCurrentPlayItem();
-		netHandler.addToSendQueue(new PacketUseItem(blockX, blockY, blockZ, side.getDirection(), entityplayer.inventory.getCurrentItem(), xPlaced, yPlaced, PacketUseItem.TYPE_USE_STACK_ON));
-		return super.onlyUseItemStackOn(entityplayer, world, itemstack, blockX, blockY, blockZ, side, xPlaced, yPlaced);
+		netHandler.addToSendQueue(new PacketUseOrPlaceItemStack(-1, -1, -1, Direction.NONE, entityplayer.inventory.getCurrentItem(), 0, 0, PacketUseOrPlaceItemStack.TYPE_USE_ON_NOTHING));
+		return super.useItemStackOnNothing(entityplayer, world, itemstack);
 	}
 
 	@Override
-	public boolean useItem(Player entityplayer, World world, ItemStack itemstack) {
+	public boolean placeItemStackOnTile(Player entityplayer, World world, ItemStack itemstack, int blockX, int blockY, int blockZ, Side side, double xPlaced, double yPlaced) {
 		syncCurrentPlayItem();
-		netHandler.addToSendQueue(new PacketUseItem(-1, -1, -1, Direction.NONE, entityplayer.inventory.getCurrentItem(), 0, 0, PacketUseItem.TYPE_USE_ITEM));
-		boolean flag = super.useItem(entityplayer, world, itemstack);
-		return flag;
+		netHandler.addToSendQueue(new PacketUseOrPlaceItemStack(blockX, blockY, blockZ, side.getDirection(), entityplayer.inventory.getCurrentItem(), xPlaced, yPlaced, PacketUseOrPlaceItemStack.TYPE_PLACE_ONLY));
+		return super.placeItemStackOnTile(entityplayer, world, itemstack, blockX, blockY, blockZ, side, xPlaced, yPlaced);
 	}
 
 	@Override
diff --git a/game/client/src/main/java/net/minecraft/client/render/RenderGlobal.java b/game/client/src/main/java/net/minecraft/client/render/RenderGlobal.java
index 6f4ff32c7..79e3b2282 100644
--- a/game/client/src/main/java/net/minecraft/client/render/RenderGlobal.java
+++ b/game/client/src/main/java/net/minecraft/client/render/RenderGlobal.java
@@ -514,6 +514,7 @@ public class RenderGlobal
             }
         }
 
+        // DO NOT TURN THIS INTO A FOREACH EITHER
         for (int i = 0, tileEntitiesSize = renderableTileEntities.size(); i < tileEntitiesSize; i++) {
             TileEntity tileEntity = renderableTileEntities.get(i);
             TileEntityRenderDispatcher.instance.renderTileEntity(tessellator, camera, tileEntity, partialTick);
diff --git a/game/client/src/main/java/net/minecraft/client/render/block/model/BlockModelDispatcher.java b/game/client/src/main/java/net/minecraft/client/render/block/model/BlockModelDispatcher.java
index 8655dc88c..875d1a7c9 100644
--- a/game/client/src/main/java/net/minecraft/client/render/block/model/BlockModelDispatcher.java
+++ b/game/client/src/main/java/net/minecraft/client/render/block/model/BlockModelDispatcher.java
@@ -151,7 +151,10 @@ public final class BlockModelDispatcher
 
         addDispatch(new BlockModelStandard<>(Blocks.BOOKSHELF_PLANKS_OAK)
             .setTex(BLOCK_TEXTURES, "minecraft:block/bookshelf/top", S_TB)
-            .setTex(BLOCK_TEXTURES, "minecraft:block/bookshelf/side", S_SIDES));
+            .setTex(RETRO_BLOCK_TEXTURES, "minecraft:block/bookshelf/top", S_TB)
+            .setTex(BLOCK_TEXTURES, "minecraft:block/bookshelf/side", S_SIDES)
+            .setTex(RETRO_BLOCK_TEXTURES, "minecraft:block/bookshelf/side_retro", S_SIDES)
+        );
 
         addDispatch(new BlockModelWool<>(Blocks.WOOL));
         addDispatch(new BlockModelRope<>(Blocks.ROPE).setAllTextures(BLOCK_TEXTURES, "minecraft:block/rope"));
@@ -271,7 +274,7 @@ public final class BlockModelDispatcher
         addDispatch(new BlockModelStoneMossy<>(Blocks.MOSS_LIMESTONE).setAllTextures(BLOCK_TEXTURES, "minecraft:block/limestone"));
         addDispatch(new BlockModelStoneMossy<>(Blocks.MOSS_GRANITE).setAllTextures(BLOCK_TEXTURES, "minecraft:block/granite"));
 
-        addDispatch(new BlockModelStandard<>(Blocks.SAND).setAllTextures(BLOCK_TEXTURES, "minecraft:block/sand"));
+        addDispatch(new BlockModelStandard<>(Blocks.SAND).setAllTextures(BLOCK_TEXTURES, "minecraft:block/sand").setAllTextures(RETRO_BLOCK_TEXTURES, "minecraft:block/sand_retro"));
         addDispatch(new BlockModelStandard<>(Blocks.GRAVEL).setAllTextures(BLOCK_TEXTURES, "minecraft:block/gravel"));
         addDispatch(new BlockModelStandard<>(Blocks.SULFUR).setAllTextures(BLOCK_TEXTURES, "minecraft:block/sulfur"));
 
diff --git a/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRenderer.java b/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRenderer.java
index e7100d7f0..30c7370a0 100644
--- a/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRenderer.java
+++ b/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRenderer.java
@@ -16,6 +16,7 @@ import org.useless.dragonfly.models.entity.StaticEntityModel;
 import java.util.HashMap;
 import java.util.Map;
 
+
 public abstract class TileEntityRenderer<T extends TileEntity>
 {
     private final @NotNull Map<@NotNull String, @NotNull StaticEntityModel> modelMap = new HashMap<>();
@@ -26,6 +27,8 @@ public abstract class TileEntityRenderer<T extends TileEntity>
     {
     }
 
+    public void tick() {}
+
     public abstract void doRender(Tessellator tessellator, T tileEntity, double x, double y, double z,
                                   float partialTick);
 
@@ -45,10 +48,6 @@ public abstract class TileEntityRenderer<T extends TileEntity>
     {
     }
 
-    public void tick() {
-
-    }
-
     protected @NotNull StaticEntityModel getModel(@NotNull final String localId) {
         StaticEntityModel model = this.modelMap.get(localId);
         if (model != null) {
diff --git a/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererSign.java b/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererSign.java
index 2d32a1086..091088182 100644
--- a/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererSign.java
+++ b/game/client/src/main/java/net/minecraft/client/render/tileentity/TileEntityRendererSign.java
@@ -52,8 +52,6 @@ public class TileEntityRendererSign extends TileEntityRenderer<TileEntitySign>
         final Block<?> block = tileEntity.getBlock();
         if (block == null || !(block.getLogic() instanceof BlockLogicSign)) return; // Check to make sure the block for the entity is present before rendering, some edge cases have the tile entity loaded before the block data and can cause a crash
 
-        GL11.glColor4f(1, 1, 1, 1);
-
         GL11.glEnable(GL12.GL_RESCALE_NORMAL);
         GL11.glPushMatrix();
 
diff --git a/game/client/src/main/resources/assets/minecraft/textures/block/bookshelf/side_retro.png b/game/client/src/main/resources/assets/minecraft/textures/block/bookshelf/side_retro.png
new file mode 100644
index 000000000..a87a69657
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/block/bookshelf/side_retro.png differ
diff --git a/game/client/src/main/resources/assets/minecraft/textures/block/sand_retro.png b/game/client/src/main/resources/assets/minecraft/textures/block/sand_retro.png
new file mode 100644
index 000000000..b32edfa3b
Binary files /dev/null and b/game/client/src/main/resources/assets/minecraft/textures/block/sand_retro.png differ
diff --git a/game/core/src/main/java/net/minecraft/core/block/BlockLogicBasket.java b/game/core/src/main/java/net/minecraft/core/block/BlockLogicBasket.java
index 2df4076e4..3c9cfc584 100644
--- a/game/core/src/main/java/net/minecraft/core/block/BlockLogicBasket.java
+++ b/game/core/src/main/java/net/minecraft/core/block/BlockLogicBasket.java
@@ -73,6 +73,22 @@ public class BlockLogicBasket extends BlockLogic
         return true;
     }
 
+    @Override
+    public void onBlockPlacedByWorld(World world, int x, int y, int z)
+    {
+        for (Side s : Side.sides){
+            world.notifyBlocksOfNeighborChange(x + s.getOffsetX(), y + s.getOffsetY(), z + s.getOffsetZ(), id());
+        }
+    }
+
+    @Override
+    public void onBlockRemoved(World world, int x, int y, int z, int data)
+    {
+        for (Side s : Side.sides){
+            world.notifyBlocksOfNeighborChange(x + s.getOffsetX(), y + s.getOffsetY(), z + s.getOffsetZ(), id());
+        }
+    }
+
     @Override
     public boolean getDirectSignal(World world, int x, int y, int z, Side side)
     {
@@ -83,7 +99,7 @@ public class BlockLogicBasket extends BlockLogic
     {
         TileEntityBasket basketTileEntity = (TileEntityBasket) worldSource.getTileEntity(x, y, z);
         if(basketTileEntity != null){
-            return basketTileEntity.getNumUnitsInside() == basketTileEntity.getMaxUnits();
+            return basketTileEntity.getNumUnitsInside() >= basketTileEntity.getMaxUnits();
         }
         return false;
     }
diff --git a/game/core/src/main/java/net/minecraft/core/block/BlockLogicBookshelf.java b/game/core/src/main/java/net/minecraft/core/block/BlockLogicBookshelf.java
new file mode 100644
index 000000000..dbe050780
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/block/BlockLogicBookshelf.java
@@ -0,0 +1,23 @@
+package net.minecraft.core.block;
+
+import net.minecraft.core.block.entity.TileEntity;
+import net.minecraft.core.block.material.Material;
+import net.minecraft.core.enums.EnumDropCause;
+import net.minecraft.core.item.ItemStack;
+import net.minecraft.core.item.Items;
+import net.minecraft.core.world.World;
+import org.jetbrains.annotations.Nullable;
+
+public class BlockLogicBookshelf extends BlockLogic {
+    public BlockLogicBookshelf(Block<?> block, Material material) {
+        super(block, material);
+    }
+
+    @Override
+    public ItemStack @Nullable [] getBreakResult(World world, EnumDropCause dropCause, int meta, TileEntity tileEntity) {
+//        if (dropCause == EnumDropCause.PISTON_CRUSH) {
+//            return new ItemStack[] {Items.BOOK.getDefaultStack() };
+//        }
+        return super.getBreakResult(world, dropCause, meta, tileEntity);
+    }
+}
diff --git a/game/core/src/main/java/net/minecraft/core/block/BlockLogicButton.java b/game/core/src/main/java/net/minecraft/core/block/BlockLogicButton.java
index a85ea86a9..41c1e5a65 100644
--- a/game/core/src/main/java/net/minecraft/core/block/BlockLogicButton.java
+++ b/game/core/src/main/java/net/minecraft/core/block/BlockLogicButton.java
@@ -233,7 +233,7 @@ public class BlockLogicButton extends BlockLogic implements IPaintable
         }
         if(invalid)
         {
-            dropBlockWithCause(world, EnumDropCause.WORLD, x, y, z, side, null, null);
+            dropBlockWithCause(world, EnumDropCause.WORLD, x, y, z, meta, null, null);
             world.setBlockWithNotify(x, y, z, 0);
         }
     }
diff --git a/game/core/src/main/java/net/minecraft/core/block/BlockLogicButtonPainted.java b/game/core/src/main/java/net/minecraft/core/block/BlockLogicButtonPainted.java
index f3f3b3b9f..88bafad85 100644
--- a/game/core/src/main/java/net/minecraft/core/block/BlockLogicButtonPainted.java
+++ b/game/core/src/main/java/net/minecraft/core/block/BlockLogicButtonPainted.java
@@ -9,7 +9,7 @@ import net.minecraft.core.util.helper.Side;
 import net.minecraft.core.world.World;
 import org.jetbrains.annotations.Nullable;
 
-public class BlockLogicButtonPainted extends BlockLogicButton implements IPainted{
+public class BlockLogicButtonPainted extends BlockLogicButtonPlanks implements IPainted{
     public static final int MASK_COLOR = 0b1111_0000;
     public BlockLogicButtonPainted(Block<?> block) {
         super(block);
diff --git a/game/core/src/main/java/net/minecraft/core/block/BlockLogicButtonPlanks.java b/game/core/src/main/java/net/minecraft/core/block/BlockLogicButtonPlanks.java
new file mode 100644
index 000000000..bdf1b9c63
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/block/BlockLogicButtonPlanks.java
@@ -0,0 +1,37 @@
+package net.minecraft.core.block;
+
+import net.minecraft.core.block.entity.TileEntity;
+import net.minecraft.core.block.entity.TileEntityActivator;
+import net.minecraft.core.block.material.Material;
+import net.minecraft.core.entity.Mob;
+import net.minecraft.core.entity.player.Player;
+import net.minecraft.core.enums.EnumDropCause;
+import net.minecraft.core.item.Item;
+import net.minecraft.core.item.ItemStack;
+import net.minecraft.core.item.tag.ItemTags;
+import net.minecraft.core.sound.SoundCategory;
+import net.minecraft.core.util.helper.Direction;
+import net.minecraft.core.util.helper.DyeColor;
+import net.minecraft.core.util.helper.Side;
+import net.minecraft.core.util.phys.AABB;
+import net.minecraft.core.world.World;
+import net.minecraft.core.world.WorldSource;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Random;
+
+public class BlockLogicButtonPlanks extends BlockLogicButton
+{
+
+    public BlockLogicButtonPlanks(Block<?> block) {
+        super(block);
+    }
+
+    @Override
+    public int tickDelay()
+    {
+        return 10;
+    }
+
+}
diff --git a/game/core/src/main/java/net/minecraft/core/block/BlockLogicCobweb.java b/game/core/src/main/java/net/minecraft/core/block/BlockLogicCobweb.java
index d005361f8..f79d6d8c3 100644
--- a/game/core/src/main/java/net/minecraft/core/block/BlockLogicCobweb.java
+++ b/game/core/src/main/java/net/minecraft/core/block/BlockLogicCobweb.java
@@ -22,7 +22,7 @@ public class BlockLogicCobweb extends BlockLogic
     public void onEntityCollidedWithBlock(World world, int x, int y, int z, Entity entity)
     {
         entity.fallDistance = 0;
-        entity.stuck = true;
+        entity.stuckInCobweb = true;
     }
 
     @Override
diff --git a/game/core/src/main/java/net/minecraft/core/block/BlockLogicFence.java b/game/core/src/main/java/net/minecraft/core/block/BlockLogicFence.java
index ecf05a173..0f000cc75 100644
--- a/game/core/src/main/java/net/minecraft/core/block/BlockLogicFence.java
+++ b/game/core/src/main/java/net/minecraft/core/block/BlockLogicFence.java
@@ -91,6 +91,9 @@ public class BlockLogicFence extends BlockLogic implements IPaintable
         if (connectZNeg) {
             addIntersectingBoundingBox(aabb, AABB.getTemporaryBB(0.5 - thickness, 0, 0, 0.5 + thickness, 1.5, 0.5 + thickness).move(x, y, z), aabbList);
         }
+        if (!connectXPos && !connectXNeg && !connectZPos && !connectZNeg) {
+            addIntersectingBoundingBox(aabb, AABB.getTemporaryBB(0.5 - thickness, 0, 0.5 - thickness, 0.5 + thickness, 1.5, 0.5 + thickness).move(x, y, z), aabbList);
+        }
     }
 
     //    @Override
diff --git a/game/core/src/main/java/net/minecraft/core/block/BlockLogicFlower.java b/game/core/src/main/java/net/minecraft/core/block/BlockLogicFlower.java
index 0473c0cf3..5b66dce3f 100644
--- a/game/core/src/main/java/net/minecraft/core/block/BlockLogicFlower.java
+++ b/game/core/src/main/java/net/minecraft/core/block/BlockLogicFlower.java
@@ -118,16 +118,16 @@ public class BlockLogicFlower extends BlockLogic implements IBonemealable {
             }
 
             label0:
-            for (int i = 0; i < 32; i++) {
+            for (int i = 0; i < 128; i++) {
                 int x = blockX;
                 int y = blockY;
                 int z = blockZ;
                 for (int j = 0; j < i / 16; j++) {
-                    x += rand.nextInt(5) - 2;
-                    y += ((rand.nextInt(5) - 2) * rand.nextInt(3)) / 2;
-                    z += rand.nextInt(5) - 2;
-                    Block<?> b = world.getBlock(x, y - 1, z);
-                    if (b == null || !b.hasTag(BlockTags.GROWS_FLOWERS)) {
+                    x += rand.nextInt(3) - 1;
+                    y += ((rand.nextInt(3) - 1) * rand.nextInt(3)) / 2;
+                    z += rand.nextInt(3) - 1;
+                    int blockId = world.getBlockId(x, y - 1, z);
+                    if (!mayPlaceOn(blockId)) {
                         continue label0;
                     }
                 }
diff --git a/game/core/src/main/java/net/minecraft/core/block/BlockLogicMushroom.java b/game/core/src/main/java/net/minecraft/core/block/BlockLogicMushroom.java
index 8b58a6564..c6832f164 100644
--- a/game/core/src/main/java/net/minecraft/core/block/BlockLogicMushroom.java
+++ b/game/core/src/main/java/net/minecraft/core/block/BlockLogicMushroom.java
@@ -1,6 +1,11 @@
 package net.minecraft.core.block;
 
+import net.minecraft.core.block.tag.BlockTags;
+import net.minecraft.core.entity.player.Player;
+import net.minecraft.core.item.ItemStack;
+import net.minecraft.core.util.helper.Side;
 import net.minecraft.core.world.World;
+import org.jetbrains.annotations.Nullable;
 
 import java.util.Random;
 
@@ -18,7 +23,7 @@ public class BlockLogicMushroom extends BlockLogicFlower
     @Override
 	public void updateTick(World world, int x, int y, int z, Random rand)
     {
-        if(rand.nextInt(100) == 0)
+        if(rand.nextInt(25) == 0)
         {
             int x1 = (x + rand.nextInt(3)) - 1;
             int randY = (y + rand.nextInt(2)) - rand.nextInt(2);
@@ -52,4 +57,37 @@ public class BlockLogicMushroom extends BlockLogicFlower
             return world.getFullBlockLightValue(x, y, z) < 13 && mayPlaceOn(world.getBlockId(x, y - 1, z));
         }
     }
+
+    @Override
+    public boolean onBonemealUsed(ItemStack itemstack, @Nullable Player player, World world, int blockX, int blockY, int blockZ, Side side, double xPlaced, double yPlaced) {
+        Random rand = world.rand;
+        if (!world.isClientSide && canBeBonemealed) {
+            if (player == null || player.getGamemode().consumeBlocks()) {
+                itemstack.stackSize--;
+            }
+            label0:
+            for (int j1 = 0; j1 < 32; j1++) {
+                int _x = blockX;
+                int _y = blockY;
+                int _z = blockZ;
+                for (int j2 = 0; j2 < j1 / 16; j2++) {
+                    _x += rand.nextInt(3) - 1;
+                    _y += ((rand.nextInt(3) - 1) * rand.nextInt(3)) / 2;
+                    _z += rand.nextInt(3) - 1;
+                    if (world.getFullBlockLightValue(_x, _y, _z) >= 13 || !mayPlaceOn(world.getBlockId(_x, _y - 1, _z))) {
+                        continue label0;
+                    }
+                }
+
+                if (world.getBlockId(_x, _y, _z) != 0) {
+                    continue;
+                }
+                if (rand.nextFloat() > 0.85) {
+                    world.setBlockWithNotify(_x, _y, _z, block.id());
+                }
+            }
+            return true;
+        }
+        return canBeBonemealed;
+    }
 }
diff --git a/game/core/src/main/java/net/minecraft/core/block/BlockLogicPressurePlate.java b/game/core/src/main/java/net/minecraft/core/block/BlockLogicPressurePlate.java
index 73f02c2f2..d8b15d3b8 100644
--- a/game/core/src/main/java/net/minecraft/core/block/BlockLogicPressurePlate.java
+++ b/game/core/src/main/java/net/minecraft/core/block/BlockLogicPressurePlate.java
@@ -6,9 +6,11 @@ import net.minecraft.core.enums.EnumDropCause;
 import net.minecraft.core.util.helper.DyeColor;
 import net.minecraft.core.util.helper.Side;
 import net.minecraft.core.sound.SoundCategory;
-import net.minecraft.core.world.WorldSource;
-import net.minecraft.core.world.World;
+import net.minecraft.core.util.helper.DyeColor;
+import net.minecraft.core.util.helper.Side;
 import net.minecraft.core.util.phys.AABB;
+import net.minecraft.core.world.World;
+import net.minecraft.core.world.WorldSource;
 import org.jetbrains.annotations.NotNull;
 
 import java.util.List;
diff --git a/game/core/src/main/java/net/minecraft/core/block/BlockLogicSand.java b/game/core/src/main/java/net/minecraft/core/block/BlockLogicSand.java
index 7fcb7733c..df35ba8a5 100644
--- a/game/core/src/main/java/net/minecraft/core/block/BlockLogicSand.java
+++ b/game/core/src/main/java/net/minecraft/core/block/BlockLogicSand.java
@@ -1,9 +1,13 @@
 package net.minecraft.core.block;
 
+import net.minecraft.core.block.entity.TileEntity;
 import net.minecraft.core.block.tag.BlockTags;
 import net.minecraft.core.entity.EntityFallingBlock;
 import net.minecraft.core.block.material.Material;
+import net.minecraft.core.enums.EnumDropCause;
+import net.minecraft.core.item.ItemStack;
 import net.minecraft.core.world.World;
+import org.jetbrains.annotations.Nullable;
 
 import java.util.Random;
 
diff --git a/game/core/src/main/java/net/minecraft/core/block/BlockLogicSandstone.java b/game/core/src/main/java/net/minecraft/core/block/BlockLogicSandstone.java
new file mode 100644
index 000000000..c8ba42b45
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/block/BlockLogicSandstone.java
@@ -0,0 +1,22 @@
+package net.minecraft.core.block;
+
+import net.minecraft.core.block.entity.TileEntity;
+import net.minecraft.core.block.material.Material;
+import net.minecraft.core.enums.EnumDropCause;
+import net.minecraft.core.item.ItemStack;
+import net.minecraft.core.world.World;
+import org.jetbrains.annotations.Nullable;
+
+public class BlockLogicSandstone extends BlockLogic {
+    public BlockLogicSandstone(Block<?> block, Material material) {
+        super(block, material);
+    }
+
+    @Override
+    public ItemStack @Nullable [] getBreakResult(World world, EnumDropCause dropCause, int meta, TileEntity tileEntity) {
+        if (dropCause == EnumDropCause.PISTON_CRUSH) {
+            return new ItemStack[] {new ItemStack(Blocks.SAND, world.rand.nextInt(2) + 1)};
+        }
+        return super.getBreakResult(world, dropCause, meta, tileEntity);
+    }
+}
diff --git a/game/core/src/main/java/net/minecraft/core/block/BlockLogicSign.java b/game/core/src/main/java/net/minecraft/core/block/BlockLogicSign.java
index dc45de160..3ddecd935 100644
--- a/game/core/src/main/java/net/minecraft/core/block/BlockLogicSign.java
+++ b/game/core/src/main/java/net/minecraft/core/block/BlockLogicSign.java
@@ -3,15 +3,15 @@ package net.minecraft.core.block;
 import net.minecraft.core.achievement.Achievements;
 import net.minecraft.core.block.entity.TileEntity;
 import net.minecraft.core.block.entity.TileEntitySign;
+import net.minecraft.core.block.material.Material;
 import net.minecraft.core.entity.player.Player;
 import net.minecraft.core.enums.EnumDropCause;
-import net.minecraft.core.item.Items;
 import net.minecraft.core.item.ItemStack;
+import net.minecraft.core.item.Items;
 import net.minecraft.core.util.helper.DyeColor;
 import net.minecraft.core.util.helper.Side;
 import net.minecraft.core.util.phys.AABB;
 import net.minecraft.core.world.World;
-import net.minecraft.core.block.material.Material;
 import net.minecraft.core.world.WorldSource;
 
 public class BlockLogicSign extends BlockLogic implements IPaintable
diff --git a/game/core/src/main/java/net/minecraft/core/block/BlockLogicSpikes.java b/game/core/src/main/java/net/minecraft/core/block/BlockLogicSpikes.java
index d3ba8da2a..3fc0a62a9 100644
--- a/game/core/src/main/java/net/minecraft/core/block/BlockLogicSpikes.java
+++ b/game/core/src/main/java/net/minecraft/core/block/BlockLogicSpikes.java
@@ -4,6 +4,7 @@ import net.minecraft.core.block.entity.TileEntity;
 import net.minecraft.core.entity.Entity;
 import net.minecraft.core.entity.EntityItem;
 import net.minecraft.core.entity.Mob;
+import net.minecraft.core.entity.player.Player;
 import net.minecraft.core.enums.EnumDropCause;
 import net.minecraft.core.item.ItemStack;
 import net.minecraft.core.util.helper.MathHelper;
@@ -23,6 +24,8 @@ public class BlockLogicSpikes extends BlockLogic
     public BlockLogicSpikes(Block<?> block, Material mat)
     {
         super(block, mat);
+        float thickness = 3/16f;
+        this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, thickness, 1.0F);
     }
 
     @Override
@@ -43,24 +46,11 @@ public class BlockLogicSpikes extends BlockLogic
         return new ItemStack[] { new ItemStack(Blocks.SPIKES) };
     }
 
-    @Override
-	public boolean canPlaceBlockAt(World world, int x, int y, int z)
-    {
-        return world.canPlaceOnSurfaceOfBlock(x, y -1, z) || world.getBlockId(x, y - 1, z) == Blocks.MOBSPAWNER.id();
-    }
-
 	@Override
 	public void onNeighborBlockChange(World world, int x, int y, int z, int blockId) {
 		if(world.isClientSide) {
 			return;
 		}
-		
-		boolean destroy = !world.canPlaceOnSurfaceOfBlock(x, y - 1, z) && world.getBlockId(x, y - 1, z) != Blocks.MESH.id() && world.getBlockId(x, y - 1, z) != Blocks.MOBSPAWNER.id();
-		if(destroy) {
-			dropBlockWithCause(world, EnumDropCause.WORLD, x, y, z, world.getBlockMetadata(x, y, z), null, null);
-			world.setBlockWithNotify(x, y, z, 0);
-			return;
-		}
 
 		int meta = world.getBlockMetadata(x, y, z);
 		boolean wasPowered = (meta & MASK_POWERED) != 0;
@@ -79,9 +69,9 @@ public class BlockLogicSpikes extends BlockLogic
 
     @Override
 	public AABB getCollisionBoundingBoxFromPool(WorldSource world, int x, int y, int z)
-    {	
-    	float f = 1.0f / 16.0f;
-        return AABB.getTemporaryBB(x + f, y, z + f, x + 1 - f, y + 0.1875F, z + 1 - f);
+    {
+        float thickness = 3/16f;
+        return AABB.getTemporaryBB(x, y, z, x + 1,y + thickness, z + 1);
     }
 
     @Override
@@ -90,12 +80,20 @@ public class BlockLogicSpikes extends BlockLogic
         int metaData = world.getBlockMetadata(x, y, z);
         if(isSpikesUp(metaData)) {
             if (entity instanceof Mob) {
-                if(entity.fallDistance > 1.5F) {
-                    entity.hurt(null, (MathHelper.floor(entity.fallDistance) * 2) + 1, DamageType.COMBAT);
-                    entity.stuck = true;
-                } else if (entity.isWalking){
-                    entity.hurt(null, 1, DamageType.COMBAT);
+                // Alt functionality: if player is sneaking, they take half a heart of damage every two seconds, and are not stuck
+                if(entity instanceof Player && entity.isSneaking()){
+                    if(entity.tickCount % 20 == 0){
+                        entity.hurt(null, 1, DamageType.GENERIC);
+                        // Cancel player jump while sneaking
+                    }
+                    entity.yd = 0.0;
+                    return;
+                }
+                // Base functionality: all mobs are dealt one heart of damage every second
+                if(entity.tickCount % 10 == 0) {
+                    entity.hurt(null, 2, DamageType.GENERIC);
                 }
+                entity.stuckInSpikes = true;
             }
         }
     }
diff --git a/game/core/src/main/java/net/minecraft/core/block/Blocks.java b/game/core/src/main/java/net/minecraft/core/block/Blocks.java
index 72b392279..d63ef9602 100644
--- a/game/core/src/main/java/net/minecraft/core/block/Blocks.java
+++ b/game/core/src/main/java/net/minecraft/core/block/Blocks.java
@@ -18,12 +18,7 @@ import net.minecraft.core.entity.animal.MobFireflyCluster;
 import net.minecraft.core.entity.player.Player;
 import net.minecraft.core.item.Item;
 import net.minecraft.core.item.Items;
-import net.minecraft.core.item.block.ItemBlockAlgae;
-import net.minecraft.core.item.block.ItemBlockLadder;
-import net.minecraft.core.item.block.ItemBlockPainted;
-import net.minecraft.core.item.block.ItemBlockStairsPainted;
-import net.minecraft.core.item.block.ItemBlockLamp;
-import net.minecraft.core.item.block.ItemBlockSlabPainted;
+import net.minecraft.core.item.block.*;
 import net.minecraft.core.sound.BlockSounds;
 import net.minecraft.core.util.collection.NamespaceID;
 import net.minecraft.core.world.Dimension;
@@ -117,7 +112,7 @@ public final class Blocks {
         .withSound(BlockSounds.STONE).withHardness(1.0F).withBlastResistance(10F).withDisabledNeighborNotifyOnMetadataChange()
         .withTags(BlockTags.NOT_IN_CREATIVE_MENU, BlockTags.MINEABLE_BY_PICKAXE);
 
-    public static final Block<?> SANDSTONE = register("sandstone", "minecraft:block/sandstone", 30, (b) -> new BlockLogic(b, Material.stone))
+    public static final Block<?> SANDSTONE = register("sandstone", "minecraft:block/sandstone", 30, (b) -> new BlockLogicSandstone(b, Material.stone))
         .withSound(BlockSounds.STONE).withHardness(0.8F).withOverrideColor(MaterialColor.sand)
         .withTags(BlockTags.CAVES_CUT_THROUGH, BlockTags.MINEABLE_BY_PICKAXE);
 
@@ -174,7 +169,7 @@ public final class Blocks {
         .setBlockItem((b) -> new ItemBlockPainted<>(b, true))
         .withTags(BlockTags.FENCES_CONNECT, BlockTags.MINEABLE_BY_AXE);
 
-    public static final Block<?> BOOKSHELF_PLANKS_OAK = register("bookshelf.planks.oak", "minecraft:block/bookshelf_planks_oak", 100, (b) -> new BlockLogic(b, Material.wood))
+    public static final Block<?> BOOKSHELF_PLANKS_OAK = register("bookshelf.planks.oak", "minecraft:block/bookshelf_planks_oak", 100, (b) -> new BlockLogicBookshelf(b, Material.wood))
         .withSound(BlockSounds.WOOD).withHardness(1.5F)
         .withTags(BlockTags.FENCES_CONNECT, BlockTags.MINEABLE_BY_AXE);
 
@@ -577,10 +572,10 @@ public final class Blocks {
         .withSound(BlockSounds.GRASS).withHardness(0.0F).withOverrideColor(MaterialColor.paintedOrange)
         .withTags(BlockTags.BROKEN_BY_FLUIDS, BlockTags.PLANTABLE_IN_JAR, BlockTags.SHEEPS_FAVOURITE_BLOCK, BlockTags.SHEARS_DO_SILK_TOUCH);
 
-    public static final Block<?> MUSHROOM_BROWN = register("mushroom.brown", "minecraft:block/mushroom_brown", 340, BlockLogicMushroom::new)
+    public static final Block<?> MUSHROOM_BROWN = register("mushroom.brown", "minecraft:block/mushroom_brown", 340, (b) -> new BlockLogicMushroom(b).setBonemealable())
         .withSound(BlockSounds.GRASS).withHardness(0.0F).withLightEmission(0.125F).withOverrideColor(MaterialColor.paintedBrown)
         .withTags(BlockTags.BROKEN_BY_FLUIDS, BlockTags.PLANTABLE_IN_JAR, BlockTags.PIGS_FAVOURITE_BLOCK);
-    public static final Block<?> MUSHROOM_RED = register("mushroom.red", "minecraft:block/mushroom_red", 341, BlockLogicMushroom::new)
+    public static final Block<?> MUSHROOM_RED = register("mushroom.red", "minecraft:block/mushroom_red", 341, (b) -> new BlockLogicMushroom(b).setBonemealable())
         .withSound(BlockSounds.GRASS).withHardness(0.0F).withOverrideColor(MaterialColor.paintedRed)
         .withTags(BlockTags.BROKEN_BY_FLUIDS, BlockTags.PLANTABLE_IN_JAR, BlockTags.PIGS_FAVOURITE_BLOCK);
 
@@ -758,13 +753,13 @@ public final class Blocks {
     public static final Block<?> BUTTON_STONE = register("button.stone", "minecraft:block/button_stone", 470, BlockLogicButton::new)
         .withSound(BlockSounds.STONE).withHardness(0.5F).withDisabledNeighborNotifyOnMetadataChange()
         .withTags(BlockTags.BROKEN_BY_FLUIDS, BlockTags.MINEABLE_BY_PICKAXE, BlockTags.PREVENT_MOB_SPAWNS);
-    public static final Block<?> BUTTON_PLANKS = register("button.planks", "minecraft:block/button_planks", 471, BlockLogicButton::new)
+    public static final Block<?> BUTTON_PLANKS = register("button.planks", "minecraft:block/button_planks", 471, BlockLogicButtonPlanks::new)
         .withSound(BlockSounds.WOOD).withHardness(0.5F).withDisabledNeighborNotifyOnMetadataChange()
-        .withTags(BlockTags.BROKEN_BY_FLUIDS, BlockTags.MINEABLE_BY_PICKAXE, BlockTags.PREVENT_MOB_SPAWNS);
+        .withTags(BlockTags.BROKEN_BY_FLUIDS, BlockTags.MINEABLE_BY_AXE, BlockTags.PREVENT_MOB_SPAWNS);
     public static final Block<BlockLogicButtonPainted> BUTTON_PLANKS_PAINTED = register("button.planks.painted", "minecraft:block/button_planks_painted", 472, BlockLogicButtonPainted::new)
         .setBlockItem((b) -> new ItemBlockPainted<>(b, true))
         .withSound(BlockSounds.WOOD).withHardness(0.5F).withDisabledNeighborNotifyOnMetadataChange()
-        .withTags(BlockTags.BROKEN_BY_FLUIDS, BlockTags.MINEABLE_BY_PICKAXE, BlockTags.PREVENT_MOB_SPAWNS);
+        .withTags(BlockTags.BROKEN_BY_FLUIDS, BlockTags.MINEABLE_BY_AXE, BlockTags.PREVENT_MOB_SPAWNS);
 
     public static final Block<?> LEVER_COBBLE_STONE = register("lever.cobble.stone", "minecraft:block/lever_cobble_stone", 480, BlockLogicLever::new)
         .withSound(BlockSounds.STONE).withHardness(0.5F).withDisabledNeighborNotifyOnMetadataChange()
@@ -805,16 +800,23 @@ public final class Blocks {
         .withSound(BlockSounds.STONE).withDisabledNeighborNotifyOnMetadataChange()
         .withTags(BlockTags.MINEABLE_BY_PICKAXE);
     public static final Block<BlockLogicPistonHead> PISTON_HEAD = register("piston.head", "minecraft:block/piston_head", 522, (BlockLogicSupplier<BlockLogicPistonHead>)(b) -> new BlockLogicPistonHead(b, 4/16d, 4/16d))
-        .withSound(BlockSounds.STONE).withDisabledNeighborNotifyOnMetadataChange().withDisabledStats().withImmovableFlagSet()
+        .withSound(BlockSounds.WOOD).withDisabledNeighborNotifyOnMetadataChange().withDisabledStats().withImmovableFlagSet()
         .withTags(BlockTags.NOT_IN_CREATIVE_MENU, BlockTags.MINEABLE_BY_PICKAXE);
     public static final Block<BlockLogicPistonMoving> PISTON_MOVING = register("piston.moving", "minecraft:block/piston_moving", 523, BlockLogicPistonMoving::new)
         .withSound(BlockSounds.STONE).withDisabledStats()
         .withTags(BlockTags.NOT_IN_CREATIVE_MENU);
     public static final Block<BlockLogicPistonBase> PISTON_BASE_STEEL = register("piston.base.steel", "minecraft:block/piston_base_steel", 524, (BlockLogicSupplier<BlockLogicPistonBase>)b -> new BlockLogicPistonBaseSteel(b, 24))
-        .withSound(BlockSounds.METAL).withDisabledNeighborNotifyOnMetadataChange()
+        .withSound(BlockSounds.METAL)
+        .withHardness(5F)
+        .withBlastResistance(2000F)
+        .withOverrideColor(MaterialColor.steel).withDisabledNeighborNotifyOnMetadataChange()
         .withTags(BlockTags.MINEABLE_BY_PICKAXE);
     public static final Block<BlockLogicPistonHead> PISTON_HEAD_STEEL = register("piston.head.steel", "minecraft:block/piston_head_steel", 525, (BlockLogicSupplier<BlockLogicPistonHead>)(b) -> new BlockLogicPistonHead(b, 6/16d, 8/16d))
-        .withSound(BlockSounds.METAL).withDisabledNeighborNotifyOnMetadataChange().withDisabledStats()
+        .withSound(BlockSounds.METAL)
+        .withHardness(5F)
+        .withBlastResistance(2000F)
+        .withOverrideColor(MaterialColor.steel)
+        .withDisabledNeighborNotifyOnMetadataChange().withDisabledStats()
         .withTags(BlockTags.NOT_IN_CREATIVE_MENU, BlockTags.MINEABLE_BY_PICKAXE);
 
     public static final Block<?> NOTEBLOCK = register("noteblock", "minecraft:block/noteblock", 530, BlockLogicNote::new)
@@ -1006,11 +1008,11 @@ public final class Blocks {
         .withTags(BlockTags.NOT_IN_CREATIVE_MENU, BlockTags.MINEABLE_BY_AXE);
 
     public static final Block<BlockLogicSignPainted> SIGN_POST_PLANKS_OAK_PAINTED = register("sign.post.planks.oak.painted", "minecraft:block/sign_post_planks_oak_painted", 713, (BlockLogicSupplier<BlockLogicSignPainted>)(b) -> new BlockLogicSignPainted(b, true))
-        .withSound(BlockSounds.WOOD).withHardness(1.0F).withDisabledNeighborNotifyOnMetadataChange().setStatParent(() -> Items.SIGN)
-        .withTags(BlockTags.NOT_IN_CREATIVE_MENU, BlockTags.MINEABLE_BY_AXE);
+            .withSound(BlockSounds.WOOD).withHardness(1.0F).withDisabledNeighborNotifyOnMetadataChange().setStatParent(() -> Items.SIGN)
+            .withTags(BlockTags.NOT_IN_CREATIVE_MENU, BlockTags.MINEABLE_BY_AXE);
     public static final Block<BlockLogicSignPainted> SIGN_WALL_PLANKS_OAK_PAINTED = register("sign.wall.planks.oak.painted", "minecraft:block/sign_wall_planks_oak_painted", 714, (BlockLogicSupplier<BlockLogicSignPainted>)(b) -> new BlockLogicSignPainted(b, false))
-        .withSound(BlockSounds.WOOD).withHardness(1.0F).withDisabledNeighborNotifyOnMetadataChange().setStatParent(() -> SIGN_POST_PLANKS_OAK)
-        .withTags(BlockTags.NOT_IN_CREATIVE_MENU, BlockTags.MINEABLE_BY_AXE);
+            .withSound(BlockSounds.WOOD).withHardness(1.0F).withDisabledNeighborNotifyOnMetadataChange().setStatParent(() -> SIGN_POST_PLANKS_OAK)
+            .withTags(BlockTags.NOT_IN_CREATIVE_MENU, BlockTags.MINEABLE_BY_AXE);
 
     public static final Block<BlockLogicLayerSnow> LAYER_SNOW = register("layer.snow", "minecraft:block/layer_snow", 720, (BlockLogicSupplier<BlockLogicLayerSnow>)(b) -> new BlockLogicLayerSnow(b, Blocks.BLOCK_SNOW, Material.topSnow))
         .withSound(BlockSounds.CLOTH).withHardness(0.1F).withLitInteriorSurface(true)
@@ -1190,7 +1192,7 @@ public final class Blocks {
         .withTags(BlockTags.MINEABLE_BY_PICKAXE, BlockTags.CHAINLINK_FENCES_CONNECT, BlockTags.CAN_HANG_OFF);
 
     public static final Block<BlockLogicBasket> BASKET = register("basket", "minecraft:block/basket", 900, (BlockLogicSupplier<BlockLogicBasket>)(b) -> new BlockLogicBasket(b, Material.cloth))
-        .withSound(BlockSounds.CLOTH).withHardness(0.5F).withOverrideColor(MaterialColor.wood).setStatParent(() -> Items.BASKET)
+        .withSound(BlockSounds.CLOTH).withHardness(0.5F).withOverrideColor(MaterialColor.wood).setStatParent(() -> Items.BASKET).withDisabledNeighborNotifyOnMetadataChange()
         .withTags(BlockTags.NOT_IN_CREATIVE_MENU);
 
     public static final Block<?> PAPER_WALL = register("paper_wall", "minecraft:block/paper_wall", 910, (b) -> new BlockLogic(b, Material.cloth))
diff --git a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntity.java b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntity.java
index a6ea92d5a..c076eca63 100644
--- a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntity.java
+++ b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntity.java
@@ -1,6 +1,7 @@
 package net.minecraft.core.block.entity;
 
 import com.mojang.nbt.tags.CompoundTag;
+import net.minecraft.core.block.Blocks;
 import net.minecraft.core.block.motion.CarriedBlock;
 import net.minecraft.core.block.Block;
 import net.minecraft.core.block.tag.BlockTags;
@@ -38,6 +39,12 @@ public abstract class TileEntity implements ICarriable
     {
         if(worldObj != null)
         {
+            int id = getBlockId();
+            if (id != 0 && Blocks.getBlock(id).isSignalSource()) {
+                for (Side s : Side.sides){
+                    worldObj.notifyBlocksOfNeighborChange(x + s.getOffsetX(), y + s.getOffsetY(), z + s.getOffsetZ(), getBlockId());
+                }
+            }
             worldObj.updateTileEntityChunkAndSendToPlayer(x, y, z, this);
         }
     }
@@ -152,6 +159,11 @@ public abstract class TileEntity implements ICarriable
             b.onBlockPlacedByMob(world, x, y, z, side, (Mob) holder, xPlaced, yPlaced);
         }
         world.notifyBlockChange(x, y, z, carriedBlock.blockId);
+        if (carriedBlock.blockId != 0 && Blocks.getBlock(carriedBlock.blockId).isSignalSource()) {
+            for (Side s : Side.sides){
+                worldObj.notifyBlocksOfNeighborChange(x + s.getOffsetX(), y + s.getOffsetY(), z + s.getOffsetZ(), getBlockId());
+            }
+        }
         return true;
     }
 
@@ -187,6 +199,12 @@ public abstract class TileEntity implements ICarriable
         world.removeBlockTileEntity(x, y, z);
         world.setBlockRaw(x, y, z, 0);
         world.notifyBlockChange(x, y, z, 0);
+        int id = getBlockId();
+        if (currentBlock.isSignalSource()) {
+            for (Side s : Side.sides){
+                worldObj.notifyBlocksOfNeighborChange(x + s.getOffsetX(), y + s.getOffsetY(), z + s.getOffsetZ(), getBlockId());
+            }
+        }
         this.worldObj = null;
         this.carriedBlock = getCarriedEntry(world, holder, currentBlock, currentMeta);
         return carriedBlock;
diff --git a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityBasket.java b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityBasket.java
index f8c5c3073..ac116064d 100644
--- a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityBasket.java
+++ b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityBasket.java
@@ -4,9 +4,13 @@ import java.util.*;
 
 import it.unimi.dsi.fastutil.objects.Object2IntArrayMap;
 import it.unimi.dsi.fastutil.objects.Object2IntMap;
+import net.minecraft.core.block.Block;
 import net.minecraft.core.block.Blocks;
+import net.minecraft.core.block.motion.CarriedBlock;
+import net.minecraft.core.block.tag.BlockTags;
 import net.minecraft.core.entity.Entity;
 import net.minecraft.core.entity.EntityItem;
+import net.minecraft.core.entity.Mob;
 import net.minecraft.core.entity.player.Player;
 import net.minecraft.core.item.Item;
 import net.minecraft.core.item.ItemStack;
@@ -14,7 +18,9 @@ import com.mojang.nbt.tags.CompoundTag;
 import com.mojang.nbt.tags.ListTag;
 import net.minecraft.core.net.packet.Packet;
 import net.minecraft.core.net.packet.PacketTileEntityData;
+import net.minecraft.core.util.helper.Side;
 import net.minecraft.core.util.phys.AABB;
+import net.minecraft.core.world.ICarriable;
 import net.minecraft.core.world.World;
 import org.jetbrains.annotations.NotNull;
 
@@ -23,24 +29,27 @@ public class TileEntityBasket extends TileEntity
     private int numUnitsInside = 0;
     private final Object2IntMap<BasketEntry> contents = new Object2IntArrayMap<>();
 
-
     private void updateNumUnits()
     {
-        final int prevNum = this.numUnitsInside;
-        this.numUnitsInside = 0;
+        int currentNumInside = calcNumUnitsInside();
+        if (currentNumInside != numUnitsInside && worldObj != null){
+            this.numUnitsInside = currentNumInside;
+            worldObj.notifyBlockChange(x, y, z, worldObj.getBlockId(x, y, z));
+            setChanged();
+        }
+    }
 
-        for (final Object2IntMap.Entry<BasketEntry> entry : this.contents.object2IntEntrySet())
+    private int calcNumUnitsInside() {
+        int numInside = 0;
+        for (Map.Entry<BasketEntry, Integer> entry : contents.object2IntEntrySet())
         {
             final BasketEntry be = entry.getKey();
-            final int numItems = entry.getIntValue();
+            final int numItems = entry.getValue();
 
-            final int unitsPerItem = getItemSizeUnits(be.getItem());
-            this.numUnitsInside += (unitsPerItem * numItems);
-        }
-        if (prevNum != numUnitsInside && worldObj != null){
-            worldObj.notifyBlockChange(x, y, z, worldObj.getBlockId(x, y, z));
-            setChanged();
+            int unitsPerItem = getItemSizeUnits(be.getItem());
+            numInside += (unitsPerItem * numItems);
         }
+        return numInside;
     }
 
     private int getItemSizeUnits(final Item item)
@@ -117,7 +126,7 @@ public class TileEntityBasket extends TileEntity
             final int count = itemTag.getShort("Count");
             this.contents.put(entry, count);
         }
-        updateNumUnits();
+        this.numUnitsInside = calcNumUnitsInside();
     }
 
     @Override
@@ -152,13 +161,12 @@ public class TileEntityBasket extends TileEntity
 
         if (!entities.isEmpty())
         {
-            for (final EntityItem e : entities)
-            {
-                if (e.item != null && e.item.stackSize > 0 && e.basketPickupDelay == 0)
-                {
+            for (int i = 0; i < entities.size(); i++) {
+                if (calcNumUnitsInside() >= getMaxUnits()) break;
+                EntityItem e = entities.get(i);
+                if (e.item != null && e.item.stackSize > 0 && e.basketPickupDelay == 0) {
                     shouldUpdate = importItemStack(e.item);
-                    if (e.item.stackSize <= 0)
-                    {
+                    if (e.item.stackSize <= 0) {
                         e.item.stackSize = 0;
                         e.outOfWorld();
                     }
@@ -177,9 +185,9 @@ public class TileEntityBasket extends TileEntity
 
     private boolean importItemStack(final ItemStack stack)
     {
-        final BasketEntry entry = new BasketEntry(stack.itemID, stack.getMetadata(), stack.getData());
+        BasketEntry entry = new BasketEntry(stack.itemID, stack.getMetadata(), stack.getData());
         final int sizeUnits = getItemSizeUnits(stack.getItem());
-        final int freeUnits = getMaxUnits() - this.numUnitsInside;
+        final int freeUnits = getMaxUnits() - numUnitsInside;
         final int itemsToTake = Math.min(freeUnits / sizeUnits, stack.stackSize);
         if (itemsToTake <= 0) return false;
         stack.stackSize -= itemsToTake;
diff --git a/game/core/src/main/java/net/minecraft/core/block/piston/BlockLogicPistonBaseSteel.java b/game/core/src/main/java/net/minecraft/core/block/piston/BlockLogicPistonBaseSteel.java
index b24877e18..496c5a056 100644
--- a/game/core/src/main/java/net/minecraft/core/block/piston/BlockLogicPistonBaseSteel.java
+++ b/game/core/src/main/java/net/minecraft/core/block/piston/BlockLogicPistonBaseSteel.java
@@ -1,5 +1,6 @@
 package net.minecraft.core.block.piston;
 
+import net.minecraft.core.Global;
 import net.minecraft.core.block.Blocks;
 import net.minecraft.core.block.entity.TileEntity;
 import net.minecraft.core.block.material.Material;
@@ -7,6 +8,7 @@ import net.minecraft.core.block.Block;
 import net.minecraft.core.block.tag.BlockTags;
 import net.minecraft.core.entity.Entity;
 import net.minecraft.core.entity.EntityFallingBlock;
+import net.minecraft.core.entity.player.Player;
 import net.minecraft.core.enums.EnumDropCause;
 import net.minecraft.core.util.helper.Direction;
 import net.minecraft.core.util.phys.AABB;
@@ -196,7 +198,8 @@ public class BlockLogicPistonBaseSteel extends BlockLogicPistonBase {
     public void extendEvent(World world, int x, int y, int z, int data, Direction direction) {
         List<Entity> entityList = new ArrayList<>(world.getEntitiesWithinAABBExcludingEntity(null, AABB.getTemporaryBB((double)x + direction.getOffsetX(), (double)y + direction.getOffsetY(), (double)z + direction.getOffsetZ(), (double)x + direction.getOffsetX() + 1, (double)y + direction.getOffsetY() + 1, (double)z + direction.getOffsetZ() + 1)));
         for (Entity entity : entityList){
-            if (entity == null || entity.noPhysics || entity == flungBlock /*Fix for 1 tick extensions not working properly*/ ) continue;
+            if (entity == null || entity.noPhysics || entity == flungBlock /*Fix for 1 tick extensions not working properly*/
+                || (entity.world != null && entity.world.isClientSide && entity instanceof Player)) continue;
             final double speed = 2;
             entity.fling(direction.getOffsetX() * speed, direction.getOffsetY() * speed, direction.getOffsetZ() * speed, 1f);
         }
diff --git a/game/core/src/main/java/net/minecraft/core/data/registry/Registries.java b/game/core/src/main/java/net/minecraft/core/data/registry/Registries.java
index b88295fd0..fc928805f 100644
--- a/game/core/src/main/java/net/minecraft/core/data/registry/Registries.java
+++ b/game/core/src/main/java/net/minecraft/core/data/registry/Registries.java
@@ -62,6 +62,7 @@ public class Registries extends Registry<Registry<?>>
         RECIPE_TYPES.register("minecraft:crafting/uses_tool", RecipeEntryCraftingWithTool.class);
         RECIPE_TYPES.register("minecraft:crafting/map_duplication", RecipeEntryMapDuplication.class);
         RECIPE_TYPES.register("minecraft:crafting/undyeing", RecipeEntryUndyeing.class);
+        RECIPE_TYPES.register("minecraft:crafting/dyeing", RecipeEntryDyeing.class);
         RECIPE_TYPES.register("minecraft:smelting", RecipeEntryFurnace.class);
         RECIPE_TYPES.register("minecraft:smelting/blast", RecipeEntryBlastFurnace.class);
         RECIPE_TYPES.register("minecraft:trommeling", RecipeEntryTrommel.class);
@@ -96,6 +97,8 @@ public class Registries extends Registry<Registry<?>>
             List<ItemStack> invertedLampStackList = new ArrayList<>();
             List<ItemStack> woodenButtonsList = new ArrayList<>();
             List<ItemStack> woodenPressurePlatesList = new ArrayList<>();
+            List<ItemStack> buttonsList = new ArrayList<>();
+            List<ItemStack> pressurePlatesList = new ArrayList<>();
 
             plankStackList.add(Blocks.PLANKS_OAK.getDefaultStack());
             signStackList.add(Items.SIGN.getDefaultStack());
@@ -108,6 +111,9 @@ public class Registries extends Registry<Registry<?>>
             trapdoorStackList.add(Blocks.TRAPDOOR_PLANKS_OAK.getDefaultStack());
             woodenButtonsList.add(Blocks.BUTTON_PLANKS.getDefaultStack());
             woodenPressurePlatesList.add(Blocks.PRESSURE_PLATE_PLANKS_OAK.getDefaultStack());
+            buttonsList.add(Blocks.BUTTON_STONE.getDefaultStack());
+            pressurePlatesList.add(Blocks.PRESSURE_PLATE_STONE.getDefaultStack());
+            pressurePlatesList.add(Blocks.PRESSURE_PLATE_COBBLE_STONE.getDefaultStack());
 
             for (DyeColor color : DyeColor.values()) {
                 plankStackList.add(new ItemStack(Blocks.PLANKS_OAK_PAINTED, 1, color.blockMeta));
@@ -125,6 +131,10 @@ public class Registries extends Registry<Registry<?>>
                 woodenButtonsList.add(new ItemStack(Blocks.BUTTON_PLANKS_PAINTED, 1, color.blockMeta << 4));
                 woodenPressurePlatesList.add(new ItemStack(Blocks.PRESSURE_PLATE_PLANKS_OAK_PAINTED, 1, color.blockMeta << 4));
             }
+
+            buttonsList.addAll(woodenButtonsList);
+            pressurePlatesList.addAll(woodenPressurePlatesList);
+
             ITEM_GROUPS.register("minecraft:planks",plankStackList);
             ITEM_GROUPS.register("minecraft:signs",signStackList);
             ITEM_GROUPS.register("minecraft:doors",doorStackList);
@@ -139,6 +149,8 @@ public class Registries extends Registry<Registry<?>>
             ITEM_GROUPS.register("minecraft:trapdoor_planks", trapdoorStackList);
             ITEM_GROUPS.register("minecraft:button_planks", woodenButtonsList);
             ITEM_GROUPS.register("minecraft:pressureplates_planks", woodenPressurePlatesList);
+            ITEM_GROUPS.register("minecraft:buttons", buttonsList);
+            ITEM_GROUPS.register("minecraft:pressureplates", pressurePlatesList);
         }
 
         ITEM_GROUPS.register("minecraft:tool_swords",
diff --git a/game/core/src/main/java/net/minecraft/core/data/registry/recipe/adapter/ItemStackJsonAdapter.java b/game/core/src/main/java/net/minecraft/core/data/registry/recipe/adapter/ItemStackJsonAdapter.java
index 6d0b22d96..9ce38edc6 100644
--- a/game/core/src/main/java/net/minecraft/core/data/registry/recipe/adapter/ItemStackJsonAdapter.java
+++ b/game/core/src/main/java/net/minecraft/core/data/registry/recipe/adapter/ItemStackJsonAdapter.java
@@ -71,6 +71,9 @@ public class ItemStackJsonAdapter implements JsonDeserializer<ItemStack>, JsonSe
         if (obj.has("tag")) {
             tag = NbtIo.fromJson(obj.get("tag"));
         }
+        if (item == null) {
+            throw new JsonParseException("Could not parse stack for json:\n" + json);
+        }
         return new ItemStack(item, amount, meta, tag);
     }
 
diff --git a/game/core/src/main/java/net/minecraft/core/data/registry/recipe/adapter/RecipeEntryDyeingJsonAdapter.java b/game/core/src/main/java/net/minecraft/core/data/registry/recipe/adapter/RecipeEntryDyeingJsonAdapter.java
new file mode 100644
index 000000000..34e17d861
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/data/registry/recipe/adapter/RecipeEntryDyeingJsonAdapter.java
@@ -0,0 +1,44 @@
+package net.minecraft.core.data.registry.recipe.adapter;
+
+import com.google.gson.*;
+import net.minecraft.core.data.registry.Registries;
+import net.minecraft.core.data.registry.recipe.RecipeSymbol;
+import net.minecraft.core.data.registry.recipe.entry.RecipeEntryDyeing;
+import net.minecraft.core.item.ItemStack;
+
+import java.lang.reflect.Type;
+
+public class RecipeEntryDyeingJsonAdapter implements RecipeJsonAdapter<RecipeEntryDyeing>{
+    @Override
+    public RecipeEntryDyeing deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
+        JsonObject obj = json.getAsJsonObject();
+        RecipeSymbol input = context.deserialize(obj.get("input"), RecipeSymbol.class);
+        ItemStack output = context.deserialize(obj.get("output"), ItemStack.class);
+        boolean useUpperMeta = false;
+        boolean useItemMeta = false;
+        if(obj.has("useUpperMeta")){
+            useUpperMeta = obj.get("useUpperMeta").getAsBoolean();
+        }
+        if(obj.has("useItemMeta")){
+            useItemMeta = obj.get("useItemMeta").getAsBoolean();
+        }
+        return new RecipeEntryDyeing(input, output, useUpperMeta, useItemMeta);
+    }
+
+    @Override
+    public JsonElement serialize(RecipeEntryDyeing src, Type typeOfSrc, JsonSerializationContext context) {
+        JsonObject object = new JsonObject();
+        object.addProperty("name",src.toString());
+        object.addProperty("type", Registries.RECIPE_TYPES.getKey(src.getClass()));
+        if(src.useUpperMeta){
+            object.addProperty("useUpperMeta", true);
+        }
+        if(src.useItemMeta){
+            object.addProperty("useItemMeta", true);
+        }
+
+        object.add("input", context.serialize(src.inputSymbol));
+        object.add("output", context.serialize(src.output));
+        return object;
+    }
+}
diff --git a/game/core/src/main/java/net/minecraft/core/data/registry/recipe/entry/RecipeEntryDyeing.java b/game/core/src/main/java/net/minecraft/core/data/registry/recipe/entry/RecipeEntryDyeing.java
new file mode 100644
index 000000000..941dd89b3
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/data/registry/recipe/entry/RecipeEntryDyeing.java
@@ -0,0 +1,181 @@
+package net.minecraft.core.data.registry.recipe.entry;
+
+import net.minecraft.core.block.Blocks;
+import net.minecraft.core.data.registry.Registries;
+import net.minecraft.core.data.registry.recipe.*;
+import net.minecraft.core.data.registry.recipe.adapter.RecipeEntryDyeingJsonAdapter;
+import net.minecraft.core.data.registry.recipe.adapter.RecipeEntryUndyeingJsonAdapter;
+import net.minecraft.core.data.registry.recipe.adapter.RecipeJsonAdapter;
+import net.minecraft.core.item.Item;
+import net.minecraft.core.item.ItemStack;
+import net.minecraft.core.item.Items;
+import net.minecraft.core.player.inventory.container.ContainerCrafting;
+import net.minecraft.core.util.helper.DyeColor;
+
+import java.util.List;
+import java.util.Objects;
+
+public class RecipeEntryDyeing extends RecipeEntryCraftingDynamic implements HasJsonAdapter {
+    public RecipeSymbol inputSymbol;
+    public ItemStack output;
+    public boolean useUpperMeta;
+    public boolean useItemMeta;
+    public RecipeEntryDyeing() {
+
+    }
+
+    public RecipeEntryDyeing(RecipeSymbol input, ItemStack output, boolean useUpperMeta, boolean useItemMeta) {
+        this.inputSymbol = input;
+        this.output = output;
+        this.useUpperMeta = useUpperMeta;
+        this.useItemMeta = useItemMeta;
+    }
+
+    @Override
+    public boolean matches(ContainerCrafting containerCrafting) {
+        boolean hasDye = false;
+        boolean hasItem = false;
+        for (int i = 0; i < 9; i++) {
+            ItemStack stack = containerCrafting.getItem(i);
+            if (stack != null && stack.getItem() == Items.DYE) {
+                if (hasDye) return false;
+                hasDye = true;
+            } else if (stack != null && !inputSymbol.matches(stack)) {
+                return false;
+            } else if (inputSymbol.matches(stack)) {
+                hasItem = true;
+            }
+        }
+        return hasDye && hasItem;
+    }
+
+    @Override
+    public boolean matchesQuery(SearchQuery query) {
+        switch (query.mode){
+            case ALL: {
+                if ((matchesRecipe(query) || matchesUsage(query)) && matchesScope(query)) return true;
+                break;
+            }
+            case RECIPE: {
+                if (matchesRecipe(query) && matchesScope(query)) return true;
+                break;
+            }
+            case USAGE: {
+                if (matchesUsage(query) && matchesScope(query)) return true;
+                break;
+            }
+        }
+        return false;
+    }
+
+    public boolean matchesScope(SearchQuery query){
+        if(query.scope.getLeft() == SearchQuery.SearchScope.NONE) return true;
+        if(query.scope.getLeft() == SearchQuery.SearchScope.NAMESPACE) {
+            RecipeNamespace namespace = Registries.RECIPES.getItem(query.scope.getRight());
+            if(namespace == parent.getParent()){
+                return true;
+            }
+        } else if (query.scope.getLeft() == SearchQuery.SearchScope.NAMESPACE_GROUP) {
+            RecipeGroup group;
+            try {
+                group = Registries.RECIPES.getGroupFromKey(query.scope.getRight());
+            } catch (IllegalArgumentException e){
+                group = null;
+            }
+            if(group == parent){
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public boolean matchesRecipe(SearchQuery query) {
+        if(query.query.getLeft() == SearchQuery.QueryType.NAME) {
+            for (int i = 0; i < DyeColor.COLOR_AMOUNT; i++) {
+                ItemStack output = new ItemStack(this.output.itemID, this.output.stackSize, getOutputMetaFromInputMeta(i));
+                if(query.strict && output.getDisplayName().equalsIgnoreCase(query.query.getRight())){
+                    return true;
+                } else if(!query.strict && output.getDisplayName().toLowerCase().contains(query.query.getRight().toLowerCase())){
+                    return true;
+                }
+            }
+        } else if (query.query.getLeft() == SearchQuery.QueryType.GROUP && !Objects.equals(query.query.getRight(), "")) {
+            List<ItemStack> groupStacks = new RecipeSymbol(query.query.getRight()).resolve();
+            if(groupStacks == null) return false;
+            return groupStacks.contains(output);
+        }
+        return false;
+    }
+
+    public boolean matchesUsage(SearchQuery query) {
+        RecipeSymbol[] symbols = {inputSymbol, new RecipeSymbol(Blocks.SPONGE_WET.getDefaultStack())};
+        for (RecipeSymbol symbol : symbols) {
+            if(symbol == null) continue;
+            List<ItemStack> stacks = symbol.resolve();
+            if(query.query.getLeft() == SearchQuery.QueryType.NAME) {
+                for (ItemStack stack : stacks) {
+                    if(query.strict && stack.getDisplayName().equalsIgnoreCase(query.query.getRight())){
+                        return true;
+                    } else if (!query.strict && stack.getDisplayName().toLowerCase().contains(query.query.getRight().toLowerCase())) {
+                        return true;
+                    }
+                }
+            } else if (query.query.getLeft() == SearchQuery.QueryType.GROUP && !Objects.equals(query.query.getRight(), "")) {
+                List<ItemStack> groupStacks = new RecipeSymbol(query.query.getRight()).resolve();
+                if(groupStacks == null) return false;
+                if(stacks.stream().anyMatch((groupStacks::contains))){
+                    return true;
+                }
+            }
+
+        }
+        return false;
+    }
+
+    @Override
+    public ItemStack getCraftingResult(ContainerCrafting containerCrafting) {
+        int dyeMeta = 0;
+        int items = 0;
+        for (int i = 0; i < containerCrafting.getContainerSize(); i++) {
+            ItemStack itemstack1 = containerCrafting.getItem(i);
+            if (itemstack1 == null){
+                continue;
+            }
+            if (itemstack1.getItem() == Items.DYE){
+                dyeMeta = itemstack1.getMetadata();
+                continue;
+            }
+            items++;
+        }
+
+        return new ItemStack(output.getItem(), items, getOutputMetaFromInputMeta(dyeMeta));
+    }
+
+    public int getOutputMetaFromInputMeta(int meta) {
+        DyeColor dyeColor = DyeColor.colorFromItemMeta(meta);
+        if(useItemMeta){
+            return useUpperMeta ? dyeColor.itemMeta << 4 : dyeColor.itemMeta;
+        } else {
+            return useUpperMeta ? dyeColor.blockMeta << 4 : dyeColor.blockMeta;
+        }
+    }
+
+    @Override
+    public int getRecipeSize() {
+        return 2;
+    }
+
+    @Override
+    public ItemStack[] onCraftResult(ContainerCrafting containerCrafting) {
+        ItemStack[] returnStack = new ItemStack[9];
+        for (int i = 0; i < containerCrafting.getContainerSize(); i++) {
+            containerCrafting.removeItem(i, 1);
+        }
+        return returnStack;
+    }
+
+    @Override
+    public RecipeJsonAdapter<?> getAdapter() {
+        return new RecipeEntryDyeingJsonAdapter();
+    }
+}
diff --git a/game/core/src/main/java/net/minecraft/core/data/tag/Tag.java b/game/core/src/main/java/net/minecraft/core/data/tag/Tag.java
index 43dc5b087..e165d01a3 100644
--- a/game/core/src/main/java/net/minecraft/core/data/tag/Tag.java
+++ b/game/core/src/main/java/net/minecraft/core/data/tag/Tag.java
@@ -87,7 +87,7 @@ public class Tag<E extends ITaggable<E>>
     }
 
     /**
-     * Determines if the tag applies to the given element, i.e. <tt>element</tt> is in {@link Tag#elements}.
+     * Determines if the tag applies to the given element, i.e. {@code element} is in {@link Tag#elements}.
      */
     public boolean appliesTo(E element) 
     {
diff --git a/game/core/src/main/java/net/minecraft/core/entity/Entity.java b/game/core/src/main/java/net/minecraft/core/entity/Entity.java
index 047631c48..53b096042 100644
--- a/game/core/src/main/java/net/minecraft/core/entity/Entity.java
+++ b/game/core/src/main/java/net/minecraft/core/entity/Entity.java
@@ -73,7 +73,8 @@ public abstract class Entity
     public boolean collision = false;
     public boolean hurtMarked = false;
     public boolean hadNicknameSet;
-    public boolean stuck;
+    public boolean stuckInCobweb;
+    public boolean stuckInSpikes;
     public boolean slide = true;
     public boolean removed = false;
     public float heightOffset = 0.0F;
@@ -431,15 +432,32 @@ public abstract class Entity
         ySlideOffset *= 0.4f;
         double d3 = this.x;
         double d4 = this.z;
-        if(stuck)
+        if(stuckInCobweb)
         {
-            stuck = false;
+            // Restricts all movement
             xd *= 0.25;
-            yd *= 0.05;
-            zd *= 0.25;
             this.xd = 0.0;
+            zd *= 0.25;
+            this.zd = 0.0;
+            yd *= 0.05;
+
+            // Cancels the player's jump
             this.yd = 0.0;
+
+            stuckInCobweb = false;
+        }
+        if(stuckInSpikes)
+        {
+            // Restricts all horizontal movement
+            xd *= 0.25;
+            this.xd = 0.0;
+            zd *= 0.25;
             this.zd = 0.0;
+
+            // Cancels the player's jump
+            this.yd = 0.0;
+
+            stuckInSpikes = false;
         }
         double oldXd = xd;
         double oldYd = yd;
diff --git a/game/core/src/main/java/net/minecraft/core/entity/Mob.java b/game/core/src/main/java/net/minecraft/core/entity/Mob.java
index 21e66a545..ed375d1ba 100644
--- a/game/core/src/main/java/net/minecraft/core/entity/Mob.java
+++ b/game/core/src/main/java/net/minecraft/core/entity/Mob.java
@@ -235,6 +235,8 @@ public abstract class Mob extends Entity
         {
             if(item.hasCustomColor()) {
             	this.chatColor = item.getCustomColor();
+            } else {
+                this.chatColor = 0;
             }
             setNickname(StringUtils.substring(item.getCustomName(), 0, 32));
             return false;
diff --git a/game/core/src/main/java/net/minecraft/core/item/ItemDye.java b/game/core/src/main/java/net/minecraft/core/item/ItemDye.java
index 7b180f59f..bb0a2aa6b 100644
--- a/game/core/src/main/java/net/minecraft/core/item/ItemDye.java
+++ b/game/core/src/main/java/net/minecraft/core/item/ItemDye.java
@@ -60,23 +60,23 @@ public class ItemDye extends Item
         if(itemstack.getMetadata() == DyeColor.WHITE.itemMeta)
         {
             if (Block.hasLogicClass(block, IBonemealable.class)){
-               if (((IBonemealable) block.getLogic()).onBonemealUsed(itemstack, player, world, blockX, blockY, blockZ, side, xPlaced, yPlaced)){
-                   return true;
-               }
+                if (((IBonemealable) block.getLogic()).onBonemealUsed(itemstack, player, world, blockX, blockY, blockZ, side, xPlaced, yPlaced)){
+                    return true;
+                }
             }
             if(block == Blocks.DIRT)
             {
                 if(!world.isClientSide)
                 {
                     if(Blocks.lightBlock[world.getBlockId(blockX, blockY + 1, blockZ)] <= 2) {
-                    	int grass = Blocks.GRASS.id();
-                    	if(world.dimensionData.getWorldType() == WorldTypes.OVERWORLD_RETRO) {
-                    		grass = Blocks.GRASS_RETRO.id();
-                    	}
+                        int grass = Blocks.GRASS.id();
+                        if(world.dimensionData.getWorldType() == WorldTypes.OVERWORLD_RETRO) {
+                            grass = Blocks.GRASS_RETRO.id();
+                        }
                         world.setBlockWithNotify(blockX, blockY, blockZ, grass);
                         if (player == null || player.getGamemode().consumeBlocks())
                         {
-                        	itemstack.stackSize--;        	
+                            itemstack.stackSize--;
                         }
                     }
                 }
@@ -104,9 +104,9 @@ public class ItemDye extends Item
                 {
                     if (player == null || player.getGamemode().consumeBlocks())
                     {
-                    	itemstack.stackSize--;        	
+                        itemstack.stackSize--;
                     }
-label0:
+                    label0:
                     for(int j1 = 0; j1 < 128; j1++)
                     {
                         int k1 = blockX;
diff --git a/game/core/src/main/java/net/minecraft/core/item/ItemLabel.java b/game/core/src/main/java/net/minecraft/core/item/ItemLabel.java
index b15a9b6db..4dea72b1b 100644
--- a/game/core/src/main/java/net/minecraft/core/item/ItemLabel.java
+++ b/game/core/src/main/java/net/minecraft/core/item/ItemLabel.java
@@ -50,7 +50,10 @@ public class ItemLabel extends Item
 		AABB box = AABB.getTemporaryBB(blockX, blockY, blockZ, blockX + 1, blockY + 1, blockZ + 1);
 		List<Entity> entities = world.getEntitiesWithinAABBExcludingEntity(null, box);
         for (Entity entity : entities) {
-            if (entity instanceof Mob && !entity.hadNicknameSet) {
+            if (!(entity instanceof Player) && entity instanceof Mob && !entity.hadNicknameSet) {
+				if (itemStack.hasCustomColor()) {
+					((Mob) entity).chatColor = itemStack.getCustomColor();
+				}
                 ((Mob) entity).setNickname(StringUtils.substring(itemStack.getCustomName(), 0, 32));
 				break;
             }
diff --git a/game/core/src/main/java/net/minecraft/core/item/ItemStack.java b/game/core/src/main/java/net/minecraft/core/item/ItemStack.java
index 0d59e5f00..63237381b 100644
--- a/game/core/src/main/java/net/minecraft/core/item/ItemStack.java
+++ b/game/core/src/main/java/net/minecraft/core/item/ItemStack.java
@@ -29,57 +29,57 @@ public final class ItemStack {
     @NotNull
     private CompoundTag tag;
 
-    public ItemStack(IItemConvertible item)
+    public ItemStack(@NotNull IItemConvertible item)
     {
         this(item, 1);
     }
 
-    public ItemStack(IItemConvertible item, int stackSize)
+    public ItemStack(@NotNull IItemConvertible item, int stackSize)
     {
         this(item, stackSize, 0);
     }
 
-    public ItemStack(IItemConvertible item, int stackSize, int metadata)
+    public ItemStack(@NotNull IItemConvertible item, int stackSize, int metadata)
     {
         this(item.asItem(), stackSize, metadata);
     }
 
-    public ItemStack(Block<?> block)
+    public ItemStack(@NotNull Block<?> block)
     {
         this(block, 1);
     }
 
-    public ItemStack(Block<?> block, int stackSize)
+    public ItemStack(@NotNull Block<?> block, int stackSize)
     {
         this(block.id(), stackSize, 0);
     }
 
-    public ItemStack(Block<?> block, int stackSize, int metadata)
+    public ItemStack(@NotNull Block<?> block, int stackSize, int metadata)
     {
         this(block.id(), stackSize, metadata);
     }
 
-    public ItemStack(Block<?> block, int stackSize, int metadata, @Nullable CompoundTag tag)
+    public ItemStack(@NotNull Block<?> block, int stackSize, int metadata, @Nullable CompoundTag tag)
     {
         this(block.id(), stackSize, metadata, tag);
     }
 
-    public ItemStack(Item item)
+    public ItemStack(@NotNull Item item)
     {
         this(item.id, 1, 0);
     }
 
-    public ItemStack(Item item, int stackSize)
+    public ItemStack(@NotNull Item item, int stackSize)
     {
         this(item.id, stackSize, 0);
     }
 
-    public ItemStack(Item item, int stackSize, int metadata)
+    public ItemStack(@NotNull Item item, int stackSize, int metadata)
     {
         this(item.id, stackSize, metadata);
     }
 
-    public ItemStack(Item item, int stackSize, int metadata, @Nullable CompoundTag tag)
+    public ItemStack(@NotNull Item item, int stackSize, int metadata, @Nullable CompoundTag tag)
     {
         this(item.id, stackSize, metadata, tag);
     }
@@ -89,7 +89,7 @@ public final class ItemStack {
         this(itemID, stackSize, metadata, Item.itemsList[itemID].getDefaultTag());
     }
     
-    public ItemStack(ItemStack itemStack) {
+    public ItemStack(@NotNull ItemStack itemStack) {
         this.itemID = itemStack.itemID;
         this.metadata = itemStack.metadata;
         this.stackSize = itemStack.stackSize;
@@ -111,7 +111,7 @@ public final class ItemStack {
         tag = new CompoundTag();
     }
 
-    public ItemStack splitStack(int i)
+    public @NotNull ItemStack splitStack(int i)
     {
         ItemStack itemStack = new ItemStack(this);
         itemStack.stackSize = i;
@@ -124,7 +124,7 @@ public final class ItemStack {
         return Objects.requireNonNull(Item.itemsList[itemID]);
     }
 
-    public boolean useItem(Player entityplayer, World world, int blockX, int blockY, int blockZ, Side side, double xPlaced, double yPlaced)
+    public boolean useItem(@NotNull Player entityplayer, @NotNull World world, int blockX, int blockY, int blockZ, Side side, double xPlaced, double yPlaced)
     {
         boolean flag = getItem().onUseItemOnBlock(this, entityplayer, world, blockX, blockY, blockZ, side, xPlaced, yPlaced);
         if(flag)
@@ -134,17 +134,17 @@ public final class ItemStack {
         return flag;
     }
 
-    public float getStrVsBlock(Block<?> block)
+    public float getStrVsBlock(@NotNull Block<?> block)
     {
         return getItem().getStrVsBlock(this, block);
     }
 
-    public ItemStack useItemRightClick(World world, Player entityplayer)
+    public ItemStack useItemRightClick(@NotNull World world, @NotNull Player entityplayer)
     {
         return getItem().onUseItem(this, world, entityplayer);
     }
 
-	public CompoundTag writeToNBT(CompoundTag nbt) {
+	public @NotNull CompoundTag writeToNBT(@NotNull CompoundTag nbt) {
 		nbt.putShort("id", (short) itemID);
 		nbt.putByte("Count", (byte) stackSize);
 		nbt.putShort("Damage", (short) metadata);
@@ -156,7 +156,7 @@ public final class ItemStack {
 		return nbt;
 	}
 
-	public void readFromNBT(CompoundTag nbt) {
+	public void readFromNBT(@NotNull CompoundTag nbt) {
 		itemID = nbt.getShort("id");
 		stackSize = nbt.getByte("Count");
 		metadata = nbt.getShort("Damage");
@@ -342,27 +342,27 @@ public final class ItemStack {
         return Item.itemsList[itemID].useItemOnEntity(this, mob, entityPlayer);
     }
 
-    public ItemStack copy()
+    public @NotNull ItemStack copy()
     {
         return new ItemStack(itemID, stackSize, metadata, new CompoundTag(tag));
     }
 
-    public static boolean areItemStacksEqual(ItemStack itemstack, ItemStack itemstack1)
+    public static boolean areItemStacksEqual(@Nullable ItemStack stack1, @Nullable ItemStack stack2)
     {
-        if(itemstack == null && itemstack1 == null)
+        if(stack1 == null && stack2 == null)
         {
             return true;
         }
-        if(itemstack == null || itemstack1 == null)
+        if(stack1 == null || stack2 == null)
         {
             return false;
         } else
         {
-            return itemstack.isItemStackEqual(itemstack1);
+            return stack1.isItemStackEqual(stack2);
         }
     }
 
-    private boolean isItemStackEqual(ItemStack itemstack)
+    private boolean isItemStackEqual(@NotNull ItemStack itemstack)
     {
         if(stackSize != itemstack.stackSize)
         {
@@ -375,7 +375,7 @@ public final class ItemStack {
         return metadata == itemstack.metadata;
     }
 
-    public boolean isItemEqual(ItemStack itemstack)
+    public boolean isItemEqual(@NotNull ItemStack itemstack)
     {
         return itemID == itemstack.itemID && metadata == itemstack.metadata;
     }
@@ -388,7 +388,7 @@ public final class ItemStack {
         return Item.itemsList[itemID].getTranslatedDescription(this);
     }
 
-    public static ItemStack copyItemStack(ItemStack itemstack)
+    public static ItemStack copyItemStack(@Nullable ItemStack itemstack)
     {
         return itemstack != null ? itemstack.copy() : null;
     }
@@ -399,7 +399,7 @@ public final class ItemStack {
         return stackSize + " * " + Item.itemsList[itemID].getKey() + ":" + metadata;
     }
 
-    public void updateAnimation(World world, Entity entity, int slotId, boolean flag)
+    public void updateAnimation(@NotNull World world, Entity entity, int slotId, boolean flag)
     {
         if(animationsToGo > 0)
         {
@@ -408,18 +408,18 @@ public final class ItemStack {
         Item.itemsList[itemID].inventoryTick(this, world, entity, slotId, flag);
     }
 
-    public void onCrafting(World world, Player entityplayer)
+    public void onCrafting(@NotNull World world, @NotNull Player entityplayer)
     {
         entityplayer.addStat(getItem().getStat(StatList.STAT_CRAFTED), stackSize);
         Item.itemsList[itemID].onCraftedBy(this, world, entityplayer);
     }
 
-    public boolean isStackEqual(ItemStack itemstack)
+    public boolean isStackEqual(@NotNull ItemStack itemstack)
     {
         return itemID == itemstack.itemID && stackSize == itemstack.stackSize && metadata == itemstack.metadata;
     }
     
-    public boolean canStackWith(ItemStack itemStack) {
+    public boolean canStackWith(@Nullable ItemStack itemStack) {
         if(itemStack == null) return true;
         if(this.itemID != itemStack.itemID || this.metadata != itemStack.metadata) {
         	return false;
@@ -435,7 +435,7 @@ public final class ItemStack {
         return getMaxStackSize() == 1 && isItemStackDamageable();
     }
     
-    public static ItemStack readItemStackFromNbt(CompoundTag nbt) {
+    public static ItemStack readItemStackFromNbt(@Nullable CompoundTag nbt) {
         if(nbt == null) {
             return null;
         }
diff --git a/game/core/src/main/java/net/minecraft/core/item/ItemWandSpawner.java b/game/core/src/main/java/net/minecraft/core/item/ItemWandSpawner.java
index 0468892fe..fb1e30cdd 100644
--- a/game/core/src/main/java/net/minecraft/core/item/ItemWandSpawner.java
+++ b/game/core/src/main/java/net/minecraft/core/item/ItemWandSpawner.java
@@ -55,6 +55,9 @@ public class ItemWandSpawner extends Item implements IDispensable
             entity.setPos(x, y, z);
             entity.spawnInit();
             if (itemStack.hasCustomName() && entity instanceof Mob){
+                if (itemStack.hasCustomColor()) {
+                    ((Mob) entity).chatColor = itemStack.getCustomColor();
+                }
                 ((Mob)entity).setNickname(itemStack.getCustomName());
             }
             world.entityJoinedWorld(entity);
diff --git a/game/core/src/main/java/net/minecraft/core/lang/LanguageSeeker.java b/game/core/src/main/java/net/minecraft/core/lang/LanguageSeeker.java
index f6f6f52df..7dce01afa 100644
--- a/game/core/src/main/java/net/minecraft/core/lang/LanguageSeeker.java
+++ b/game/core/src/main/java/net/minecraft/core/lang/LanguageSeeker.java
@@ -27,10 +27,10 @@ public class LanguageSeeker {
      */
     public static final File LANGUAGE_DIR = Global.accessor.getMinecraftDir().toPath().resolve("languages").toFile();
     /**
-     * Finds a language with a specific ID. If nothing is found, <tt>null</tt> is returned.
+     * Finds a language with a specific ID. If nothing is found, {@code null} is returned.
      *
      * @param id the ID of the language to find
-     * @return the language found, or <tt>null</tt> if no language with the specific ID is found
+     * @return the language found, or {@code null} if no language with the specific ID is found
      * @throws IOException if an I/O error has occured
      */
     @Nullable
@@ -99,7 +99,7 @@ public class LanguageSeeker {
     }
 
     /**
-     * Get all the languages available in the <tt>languages</tt> folder.
+     * Get all the languages available in the {@code null} folder.
      *
      * @return all the available languages as a list
      * @throws IOException if an I/O error has occured
diff --git a/game/core/src/main/java/net/minecraft/core/net/handler/PacketHandler.java b/game/core/src/main/java/net/minecraft/core/net/handler/PacketHandler.java
index 32eba03f5..95cde2c70 100644
--- a/game/core/src/main/java/net/minecraft/core/net/handler/PacketHandler.java
+++ b/game/core/src/main/java/net/minecraft/core/net/handler/PacketHandler.java
@@ -82,7 +82,7 @@ public abstract class PacketHandler
         handleInvalidPacket(packetEntityFling);
     }
 
-    public void handlePlace(PacketUseItem useItemPacket)
+    public void handlePlace(PacketUseOrPlaceItemStack useItemPacket)
     {
         handleInvalidPacket(useItemPacket);
     }
diff --git a/game/core/src/main/java/net/minecraft/core/net/packet/Packet.java b/game/core/src/main/java/net/minecraft/core/net/packet/Packet.java
index ebb157a66..d2f511dd4 100644
--- a/game/core/src/main/java/net/minecraft/core/net/packet/Packet.java
+++ b/game/core/src/main/java/net/minecraft/core/net/packet/Packet.java
@@ -270,7 +270,7 @@ public abstract class Packet
         addMapping(12, true, true, PacketMovePlayer.Rot.class);
         addMapping(13, true, true, PacketMovePlayer.PosRot.class);
         addMapping(14, false, true, PacketPlayerAction.class);
-        addMapping(15, false, true, PacketUseItem.class);
+        addMapping(15, false, true, PacketUseOrPlaceItemStack.class);
         addMapping(16, true, true, PacketSetCarriedItem.class);
         addMapping(17, true, false, PacketSleep.class);
         addMapping(18, true, true, PacketAnimate.class);
diff --git a/game/core/src/main/java/net/minecraft/core/net/packet/PacketUseItem.java b/game/core/src/main/java/net/minecraft/core/net/packet/PacketUseOrPlaceItemStack.java
similarity index 62%
rename from game/core/src/main/java/net/minecraft/core/net/packet/PacketUseItem.java
rename to game/core/src/main/java/net/minecraft/core/net/packet/PacketUseOrPlaceItemStack.java
index 374b39e0b..8f1f04fe5 100644
--- a/game/core/src/main/java/net/minecraft/core/net/packet/PacketUseItem.java
+++ b/game/core/src/main/java/net/minecraft/core/net/packet/PacketUseOrPlaceItemStack.java
@@ -4,15 +4,51 @@ import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.IOException;
 
+import net.minecraft.core.entity.player.Player;
 import net.minecraft.core.item.ItemStack;
 import net.minecraft.core.net.handler.PacketHandler;
 import net.minecraft.core.util.helper.Direction;
+import net.minecraft.core.util.helper.Side;
+import net.minecraft.core.world.World;
 
-public class PacketUseItem extends Packet
+public class PacketUseOrPlaceItemStack extends Packet
 {
-    public static final byte TYPE_USE_ITEM_ON = 0;
-    public static final byte TYPE_USE_ITEM = 1;
-    public static final byte TYPE_USE_STACK_ON = 2;
+    public static final byte TYPE_USE_OR_PLACE_ON_TILE = 0;
+    public static final byte TYPE_USE_ON_NOTHING = 1;
+    public static final byte TYPE_PLACE_ONLY = 2;
+
+    /**
+     * Deprecated in 7.3_03
+     * <p>
+     * Equals {@link PacketUseOrPlaceItemStack#TYPE_USE_OR_PLACE_ON_TILE}
+     * <p>
+     * Can be removed at any time.
+     */
+    @Deprecated
+    public static final byte TYPE_USE_ITEM_ON =
+        TYPE_USE_OR_PLACE_ON_TILE;
+
+    /**
+     * Deprecated in 7.3_03
+     * <p>
+     * Equals {@link PacketUseOrPlaceItemStack#TYPE_USE_ON_NOTHING}
+     * <p>
+     * Can be removed at any time.
+     */
+    @Deprecated
+    public static final byte TYPE_USE_ITEM =
+        TYPE_USE_ON_NOTHING;
+
+    /**
+     * Deprecated in 7.3_03
+     * <p>
+     * Equals {@link PacketUseOrPlaceItemStack#TYPE_PLACE_ONLY}
+     * <p>
+     * Can be removed at any time.
+     */
+    @Deprecated
+    public static final byte TYPE_USE_STACK_ON =
+        TYPE_PLACE_ONLY;
 
     public int xPosition;
     public int yPosition;
@@ -23,11 +59,11 @@ public class PacketUseItem extends Packet
     public ItemStack itemStack;
     public byte type;
 
-    public PacketUseItem()
+    public PacketUseOrPlaceItemStack()
     {
     }
 
-    public PacketUseItem(int x, int y, int z, Direction direction, ItemStack itemstack, double xPlaced, double yPlaced, byte type)
+    public PacketUseOrPlaceItemStack(int x, int y, int z, Direction direction, ItemStack itemstack, double xPlaced, double yPlaced, byte type)
     {
         xPosition = x;
         yPosition = y;
diff --git a/game/core/src/main/java/net/minecraft/core/world/pathfinder/PathFinder.java b/game/core/src/main/java/net/minecraft/core/world/pathfinder/PathFinder.java
index c889e23fb..fdc7611ee 100644
--- a/game/core/src/main/java/net/minecraft/core/world/pathfinder/PathFinder.java
+++ b/game/core/src/main/java/net/minecraft/core/world/pathfinder/PathFinder.java
@@ -6,9 +6,12 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
 import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
 import net.minecraft.core.block.Block;
 import net.minecraft.core.block.BlockLogicDoor;
+import net.minecraft.core.block.BlockLogicTrapDoor;
 import net.minecraft.core.block.Blocks;
 import net.minecraft.core.entity.Entity;
 import net.minecraft.core.block.material.Material;
+import net.minecraft.core.entity.Mob;
+import net.minecraft.core.entity.monster.MobCreeper;
 import net.minecraft.core.util.helper.MathHelper;
 import net.minecraft.core.world.WorldSource;
 import org.jetbrains.annotations.Nullable;
@@ -190,21 +193,31 @@ public class PathFinder
             {
                 for(int z1 = z; z1 < z + pathpoint.z; z1++)
                 {
-                    int k1 = worldSource.getBlockId(x1, y1, z1);
-                    if(k1 <= 0)
+                    int blockId = worldSource.getBlockId(x1, y1, z1);
+                    if(blockId <= 0)
                     {
                         continue;
                     }
-                    if(Block.hasLogicClass(Blocks.blocksList[k1], BlockLogicDoor.class))
+                    if(Block.hasLogicClass(Blocks.blocksList[blockId], BlockLogicDoor.class))
                     {
-                        int l1 = worldSource.getBlockMetadata(x1, y1, z1);
-                        if(!BlockLogicDoor.isOpen(l1))
+                        int blockMetadata = worldSource.getBlockMetadata(x1, y1, z1);
+                        if(!BlockLogicDoor.isOpen(blockMetadata))
                         {
                             return 0;
                         }
                         continue;
                     }
-                    Material material = Blocks.blocksList[k1].getMaterial();
+                    //Check to allow creepers to pathfind underneath top-closed trapdoors
+                    if(entity instanceof MobCreeper){
+                        int blockMetadata = worldSource.getBlockMetadata(x1, y1, z1);
+                        if(Block.hasLogicClass(Blocks.blocksList[blockId], BlockLogicTrapDoor.class)){
+                            boolean isTopClosedTrapdoor = !BlockLogicTrapDoor.isTrapdoorOpen(blockMetadata) && BlockLogicTrapDoor.isUpperHalf(blockMetadata);
+                            if(isTopClosedTrapdoor){
+                                return 1;
+                            }
+                        }
+                    }
+                    Material material = Blocks.blocksList[blockId].getMaterial();
                     if(material.blocksMotion())
                     {
                         return 0;
diff --git a/game/core/src/main/resources/lang/en_US/gui.lang b/game/core/src/main/resources/lang/en_US/gui.lang
index a6e2b5d42..db6c86912 100644
--- a/game/core/src/main/resources/lang/en_US/gui.lang
+++ b/game/core/src/main/resources/lang/en_US/gui.lang
@@ -16,7 +16,7 @@ gui.achievements.page.overworld.desc=Achievements of the starting dimension!
 gui.achievements.page.nether.name=Nether
 gui.achievements.page.nether.desc=Achievements from Hell!
 
-gui.add_server.label.title=Add server
+gui.add_server.label.title=Add Server
 gui.add_server.button.save=Save
 gui.add_server.button.cancel=Cancel
 gui.add_server.button.show_ip=Show IP: %s
diff --git a/game/core/src/main/resources/lang/en_US/options.lang b/game/core/src/main/resources/lang/en_US/options.lang
index a5a5e8698..953aaf240 100644
--- a/game/core/src/main/resources/lang/en_US/options.lang
+++ b/game/core/src/main/resources/lang/en_US/options.lang
@@ -150,7 +150,10 @@ options.colorblindnessFix.protanopia=Protanopia
 options.colorblindnessFix.deuteranopia=Deuteranopia
 options.colorblindnessFix.tritanopia=Tritanopia
 options.colorblindnessFix.grayscale=Grayscale
-options.alwaysShowDescriptions=Always Show Item Descriptions
+options.itemDescriptions=Item Description
+options.itemDescriptions.never_prompt=Never Prompt
+options.itemDescriptions.prompt=Prompt
+options.itemDescriptions.always_show=Always Show
 options.flightSpeed=Flight Speed
 options.flightSmoothness=Flight Smoothness
 options.customAnimations=Custom Animations
diff --git a/game/core/src/main/resources/lang/en_US/tile.lang b/game/core/src/main/resources/lang/en_US/tile.lang
index ea25c034b..05fcf0d53 100644
--- a/game/core/src/main/resources/lang/en_US/tile.lang
+++ b/game/core/src/main/resources/lang/en_US/tile.lang
@@ -668,7 +668,7 @@ tile.wire.redstone.name=Redstone Wire
 tile.wire.redstone.desc=A strand of redstone dust laid on the ground.
 
 tile.torch.redstone.idle.name=Redstone Torch
-tile.torch.redstone.idle.desc=Emits a constant redstone signal.
+tile.torch.redstone.idle.desc=Exudes negative energy.
 tile.torch.redstone.active.name=Redstone Torch
 tile.torch.redstone.active.desc=Emits a constant redstone signal.
 
@@ -678,7 +678,7 @@ tile.button.planks.name=Wooden Button
 tile.button.planks.desc=Emits a brief redstone signal when pressed.
 
 tile.button.planks.painted.name=Mystery Button
-tile.button.planks.painted.desc=Emits a brief redstone signal when pressed.
+tile.button.planks.painted.desc=How did you get this?!
 tile.button.planks.painted.black.name=Black Wooden Button
 tile.button.planks.painted.black.desc=Emits a brief redstone signal when pressed.
 tile.button.planks.painted.red.name=Red Wooden Button
@@ -1420,4 +1420,4 @@ tile.statue.pigman.upper.desc=
 tile.comparer.name=Comparer
 tile.comparer.desc=Compares the block infront to the block behind.
 tile.comparer.active.name=Comparer
-tile.comparer.desc=Compares the block infront to the block behind.
\ No newline at end of file
+tile.comparer.desc=Compares the block infront to the block behind.
diff --git a/game/server/src/main/java/net/minecraft/server/MinecraftServer.java b/game/server/src/main/java/net/minecraft/server/MinecraftServer.java
index 1763b5436..afaffe79b 100644
--- a/game/server/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/game/server/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -15,6 +15,7 @@ import net.minecraft.core.data.DataLoader;
 import net.minecraft.core.data.legacy.LegacyWorldTypes;
 import net.minecraft.core.data.registry.Registries;
 import net.minecraft.core.entity.EntityDispatcher;
+import net.minecraft.core.entity.EntityItem;
 import net.minecraft.core.entity.SkinVariantList;
 import net.minecraft.core.item.Items;
 import net.minecraft.core.lang.I18n;
@@ -253,6 +254,7 @@ public class MinecraftServer
         autoSaveInterval = propertyManager.getIntProperty("autosaveInterval", 5);
         chunksSavedPerAutosave = propertyManager.getIntProperty("maxChunksSavedPerAutosave", 24);
         forceSaveAllChunksOnAutosave = propertyManager.getBooleanProperty("forceSaveAllChunksOnAutosave", false);
+        EntityItem.enableItemClumping = propertyManager.getBooleanProperty("enable-item-clumping", true);
 
         String gamemodeProperty = propertyManager.getStringProperty("default-gamemode", "survival");
         defaultGamemode = null;
diff --git a/game/server/src/main/java/net/minecraft/server/net/handler/PacketHandlerServer.java b/game/server/src/main/java/net/minecraft/server/net/handler/PacketHandlerServer.java
index 163871243..7f5267794 100644
--- a/game/server/src/main/java/net/minecraft/server/net/handler/PacketHandlerServer.java
+++ b/game/server/src/main/java/net/minecraft/server/net/handler/PacketHandlerServer.java
@@ -242,7 +242,7 @@ public class PacketHandlerServer extends PacketHandler
         playerEntity.move(dx, dy, dz);
         // Get the difference in intended move and actual move
         dx = newPosX - playerEntity.x;
-        double ndy = newPosY - playerEntity.y;
+        double ndy = Math.max(newPosY - playerEntity.y, -0.0784);
         dz = newPosZ - playerEntity.z;
         velSquared = dx * dx + ndy * ndy + dz * dz;
         boolean movedWrong = false;
@@ -387,14 +387,14 @@ public class PacketHandlerServer extends PacketHandler
     }
 
     @Override
-    public void handlePlace(PacketUseItem packet)
+    public void handlePlace(PacketUseOrPlaceItemStack packet)
     {
         if (!canInteract()) return;
         WorldServer worldserver = mcServer.getDimensionWorld(playerEntity.dimension);
         ItemStack itemstack = playerEntity.inventory.getCurrentItem();
         boolean ignoreSpawnProtection = mcServer.spawnProtectionRange <= 0 || worldserver.dimension.id != 0 || mcServer.playerList.isOp(playerEntity.uuid);
         switch (packet.type) {
-            case PacketUseItem.TYPE_USE_ITEM_ON: {
+            case PacketUseOrPlaceItemStack.TYPE_USE_OR_PLACE_ON_TILE: {
                 int x = packet.xPosition;
                 int y = packet.yPosition;
                 int z = packet.zPosition;
@@ -410,22 +410,24 @@ public class PacketHandlerServer extends PacketHandler
                 }
                 if (hasMoved && playerEntity.distanceToSqr((double) x + 0.5D, (double) y + 0.5D, (double) z + 0.5D) < 64D && (j1 > mcServer.spawnProtectionRange || ignoreSpawnProtection))
                 {
-                    playerEntity.playerController.useItemOn(playerEntity, worldserver, itemstack, x, y, z, direction.getSide(), xPlaced, yPlaced);
+                    playerEntity.playerController.useOrPlaceItemStackOnTile(playerEntity, worldserver, itemstack, x, y, z, direction.getSide(), xPlaced, yPlaced);
                 }
                 playerEntity.playerNetServerHandler.sendPacket(new PacketBlockUpdate(x, y, z, worldserver));
                 x += direction.getOffsetX();
                 y += direction.getOffsetY();
                 z += direction.getOffsetZ();
                 playerEntity.playerNetServerHandler.sendPacket(new PacketBlockUpdate(x, y, z, worldserver));
+                break;
             }
-            case PacketUseItem.TYPE_USE_ITEM: {
+            case PacketUseOrPlaceItemStack.TYPE_USE_ON_NOTHING: {
                 if (itemstack == null)
                 {
                     return;
                 }
-                playerEntity.playerController.useItemOnNothing(playerEntity, worldserver, itemstack);
+                playerEntity.playerController.useItemStackOnNothing(playerEntity, worldserver, itemstack);
+                break;
             }
-            case PacketUseItem.TYPE_USE_STACK_ON: {
+            case PacketUseOrPlaceItemStack.TYPE_PLACE_ONLY: {
                 int x = packet.xPosition;
                 int y = packet.yPosition;
                 int z = packet.zPosition;
@@ -441,13 +443,14 @@ public class PacketHandlerServer extends PacketHandler
                 }
                 if (hasMoved && playerEntity.distanceToSqr((double) x + 0.5D, (double) y + 0.5D, (double) z + 0.5D) < 64D && (j1 > mcServer.spawnProtectionRange || ignoreSpawnProtection))
                 {
-                    playerEntity.playerController.onlyUseItemStackOn(playerEntity, worldserver, itemstack, x, y, z, direction.getSide(), xPlaced, yPlaced);
+                    playerEntity.playerController.placeItemStackOnTile(playerEntity, worldserver, itemstack, x, y, z, direction.getSide(), xPlaced, yPlaced);
                 }
                 playerEntity.playerNetServerHandler.sendPacket(new PacketBlockUpdate(x, y, z, worldserver));
                 x += direction.getOffsetX();
                 y += direction.getOffsetY();
                 z += direction.getOffsetZ();
                 playerEntity.playerNetServerHandler.sendPacket(new PacketBlockUpdate(x, y, z, worldserver));
+                break;
             }
         }
         itemstack = playerEntity.inventory.getCurrentItem();
diff --git a/game/server/src/main/java/net/minecraft/server/world/ServerPlayerController.java b/game/server/src/main/java/net/minecraft/server/world/ServerPlayerController.java
index 57692bb5f..bd853354b 100644
--- a/game/server/src/main/java/net/minecraft/server/world/ServerPlayerController.java
+++ b/game/server/src/main/java/net/minecraft/server/world/ServerPlayerController.java
@@ -144,26 +144,24 @@ public class ServerPlayerController
         return flag;
     }
 
-    public boolean useItemOnNothing(Player entityplayer, World world, ItemStack itemstack)
-    {
-        if (!entityplayer.getGamemode().canInteract()) return false;
-        int i = itemstack.stackSize;
-        ItemStack itemstack1 = itemstack.useItemRightClick(world, entityplayer);
-        if(itemstack1 != itemstack || itemstack1 != null && itemstack1.stackSize != i)
-        {
-            entityplayer.inventory.mainInventory[entityplayer.inventory.getCurrentItemIndex()] = itemstack1;
-            if(itemstack1.stackSize <= 0)
-            {
-                entityplayer.inventory.mainInventory[entityplayer.inventory.getCurrentItemIndex()] = null;
-            }
-            return true;
-        } else
-        {
-            return false;
-        }
+    /**
+     * Deprecated in 7.3_03
+     * <p>
+     * Calls through to {@link ServerPlayerController#useOrPlaceItemStackOnTile(Player, World, ItemStack, int, int, int, Side, double, double)}
+     * <p>
+     * Can be removed at any time.
+     */
+    @Deprecated
+    public boolean useItemOn(Player entityplayer, World world, ItemStack itemstack, int blockX, int blockY, int blockZ, Side side, double xPlaced, double yPlaced){
+        return useOrPlaceItemStackOnTile(entityplayer, world, itemstack, blockX, blockY, blockZ, side, xPlaced, yPlaced);
     }
 
-    public boolean useItemOn(Player entityplayer, World world, ItemStack itemstack, int blockX, int blockY, int blockZ, Side side, double xPlaced, double yPlaced)
+    /**
+     * Used to dynamically use or place an ItemStack on any given tile.
+     * <p>
+     * (i.e. interacting with a chest while holding a block, defaulting to opening the chest when standing, vs. placing the block when crouched)
+     */
+    public boolean useOrPlaceItemStackOnTile(Player entityplayer, World world, ItemStack itemstack, int blockX, int blockY, int blockZ, Side side, double xPlaced, double yPlaced)
     {
         if (!entityplayer.getGamemode().canInteract()) return false;
         int blockId = world.getBlockId(blockX, blockY, blockZ);
@@ -199,7 +197,60 @@ public class ServerPlayerController
         }
     }
 
+    /**
+     * Deprecated in 7.3_03
+     * <p>
+     * Calls through to {@link ServerPlayerController#useItemStackOnNothing(Player, World, ItemStack)}
+     * <p>
+     * Can be removed at any time.
+     */
+    @Deprecated
+    public boolean useItemOnNothing(Player entityplayer, World world, ItemStack itemstack) {
+        return useItemStackOnNothing(entityplayer, world, itemstack);
+    }
+
+    /**
+     * Used to specifically use an ItemStack on nothing, most commonly defaulting to typical "item use" behavior.
+     * <p>
+     * (i.e. throwing a fishing rod or shooting a bow)
+     */
+    public boolean useItemStackOnNothing(Player entityplayer, World world, ItemStack itemstack)
+    {
+        if (!entityplayer.getGamemode().canInteract()) return false;
+        int i = itemstack.stackSize;
+        ItemStack itemstack1 = itemstack.useItemRightClick(world, entityplayer);
+        if(itemstack1 != itemstack || itemstack1 != null && itemstack1.stackSize != i)
+        {
+            entityplayer.inventory.mainInventory[entityplayer.inventory.getCurrentItemIndex()] = itemstack1;
+            if(itemstack1.stackSize <= 0)
+            {
+                entityplayer.inventory.mainInventory[entityplayer.inventory.getCurrentItemIndex()] = null;
+            }
+            return true;
+        } else
+        {
+            return false;
+        }
+    }
+
+    /**
+     * Deprecated in 7.3_03
+     * <p>
+     * Calls through to {@link ServerPlayerController#placeItemStackOnTile(Player, World, ItemStack, int, int, int, Side, double, double)}
+     * <p>
+     * Can be removed at any time.
+     */
+    @Deprecated
     public boolean onlyUseItemStackOn(@NotNull Player player, @NotNull World world, @Nullable ItemStack itemstack, int blockX, int blockY, int blockZ, Side side, double xPlaced, double yPlaced) {
+        return placeItemStackOnTile(player, world, itemstack, blockX, blockY, blockZ, side, xPlaced, yPlaced);
+    }
+
+    /**
+     * Used to exclusively place an ItemStack. No interactions, just placing a held ItemStack.
+     * <p>
+     * (i.e. placing blocks using easy bridge)
+     */
+    public boolean placeItemStackOnTile(@NotNull Player player, @NotNull World world, @Nullable ItemStack itemstack, int blockX, int blockY, int blockZ, Side side, double xPlaced, double yPlaced) {
         if (!player.getGamemode().canInteract()) return false;
         if (player.getHeldObject() == null) {
             if (itemstack == null) { // No stack then do nothing
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index cdbd9729e..a6dc3f026 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,6 +1,6 @@
 [versions]
 log4j = "2.19.0"
-lwjgl = "3.3.4"
+lwjgl = "3.3.3"
 
 [libraries]
 jetbrains-annotations = { module = "org.jetbrains:annotations", version = "24.0.0" }
diff --git a/util/datagen/src/main/java/net/minecraft/datagen/WorkbenchGenerator.java b/util/datagen/src/main/java/net/minecraft/datagen/WorkbenchGenerator.java
index 69bc0014e..c284e03aa 100644
--- a/util/datagen/src/main/java/net/minecraft/datagen/WorkbenchGenerator.java
+++ b/util/datagen/src/main/java/net/minecraft/datagen/WorkbenchGenerator.java
@@ -1,16 +1,17 @@
 package net.minecraft.datagen;
 
 import net.minecraft.core.block.Block;
+import net.minecraft.core.data.registry.recipe.entry.RecipeEntryCraftingWithTool;
+import net.minecraft.core.data.registry.recipe.entry.RecipeEntryDyeing;
+import net.minecraft.core.data.registry.recipe.entry.RecipeEntryRepairable;
+import net.minecraft.core.data.registry.recipe.entry.RecipeEntryScrap;
+import net.minecraft.core.data.registry.recipe.entry.RecipeEntryUndyeing;
 import net.minecraft.core.item.Item;
 import net.minecraft.datagen.recipeBuilders.RecipeBuilder;
 import net.minecraft.datagen.recipeBuilders.RecipeBuilderShaped;
 import net.minecraft.core.block.Blocks;
 import net.minecraft.core.data.registry.Registries;
 import net.minecraft.core.data.registry.recipe.RecipeSymbol;
-import net.minecraft.core.data.registry.recipe.entry.RecipeEntryCraftingWithTool;
-import net.minecraft.core.data.registry.recipe.entry.RecipeEntryRepairable;
-import net.minecraft.core.data.registry.recipe.entry.RecipeEntryScrap;
-import net.minecraft.core.data.registry.recipe.entry.RecipeEntryUndyeing;
 import net.minecraft.core.item.ItemStack;
 import net.minecraft.core.item.Items;
 import net.minecraft.core.util.helper.DyeColor;
@@ -48,8 +49,6 @@ class WorkbenchGenerator {
 
         generateLogToPlanks();
         generateDyes();
-        generateDyedWools();
-        generateDyedPlanks();
 
         generateInformationals();
         generateRedstoneComponents();
@@ -61,6 +60,7 @@ class WorkbenchGenerator {
         generateToolRecipes();
 
         generateUndyeingRecipes();
+        generateDyeingRecipes();
 
         generateStatueRecipes();
 
@@ -110,7 +110,7 @@ class WorkbenchGenerator {
                 "###")
             .addInput('#', "minecraft:planks")
             .addInput('X', Items.BOOK)
-            .create("bookshelf", Blocks.BOOKSHELF_PLANKS_OAK.getDefaultStack());
+            .create("bookshelf", new ItemStack(Blocks.BOOKSHELF_PLANKS_OAK, 4));
 
         RecipeBuilder.Shaped(CORE_NAMESPACE)
             .setShape(
@@ -425,13 +425,10 @@ class WorkbenchGenerator {
             .addInput('#', Blocks.MARBLE)
             .create("marble_pillar", new ItemStack(Blocks.PILLAR_MARBLE, 2));
 
-        RecipeBuilder.Shaped(CORE_NAMESPACE)
-            .setShape(
-                "PI",
-                "S ")
-            .addInput('P', Items.PAPER)
-            .addInput('I', new ItemStack(Items.DYE, 1, DyeColor.BLACK.itemMeta))
-            .addInput('S', Items.STRING)
+        RecipeBuilder.Shapeless(CORE_NAMESPACE)
+            .addInput(Items.PAPER)
+            .addInput(new ItemStack(Items.DYE, 1, DyeColor.BLACK.itemMeta))
+            .addInput(Items.STRING)
             .create("blank_label", Items.LABEL.getDefaultStack());
     }
     private static void generatePickaxes() {
@@ -871,15 +868,15 @@ class WorkbenchGenerator {
 
     private static void generateSigns() {
         RecipeBuilderShaped sign = RecipeBuilder.Shaped(CORE_NAMESPACE)
-            .setShape(
-                "XXX",
-                "XXX",
-                " # ")
-            .addInput('#', Items.STICK);
+                .setShape(
+                        "XXX",
+                        "XXX",
+                        " # ")
+                .addInput('#', Items.STICK);
         final int stackSize = 4;
 
         sign.addInput('X', Blocks.PLANKS_OAK)
-            .create("sign", new ItemStack(Items.SIGN, stackSize));
+                .create("sign", new ItemStack(Items.SIGN, stackSize));
         for (final DyeColor color : DyeColor.values()) {
             sign.addInput('X', Blocks.PLANKS_OAK_PAINTED, color.blockMeta).create(color.colorID + "_sign", new ItemStack(Items.SIGN_PAINTED, stackSize, color.itemMeta));
         }
@@ -948,8 +945,6 @@ class WorkbenchGenerator {
 
         for (DyeColor color : DyeColor.values()) {
             chest.addInput('X', Blocks.PLANKS_OAK_PAINTED, color.blockMeta).create(color.colorID + "_wooden_chest_from_planks", new ItemStack(Blocks.CHEST_PLANKS_OAK_PAINTED, 1, color.blockMeta << 4));
-
-            RecipeBuilder.Shapeless(CORE_NAMESPACE).addInput("minecraft:chests").addInput(Items.DYE, color.itemMeta).create(color.colorID + "_wooden_chest_from_chest", new ItemStack(Blocks.CHEST_PLANKS_OAK_PAINTED, 1, color.blockMeta << 4));
         }
         chest.addInput('X', Blocks.PLANKS_OAK).create("wooden_chest", Blocks.CHEST_PLANKS_OAK.getDefaultStack());
     }
@@ -967,24 +962,6 @@ class WorkbenchGenerator {
         RecipeBuilder.Shapeless(CORE_NAMESPACE).addInput(Blocks.LOG_PINE).create(String.format(format, "pine", "brown"), new ItemStack(Blocks.PLANKS_OAK_PAINTED, stackSize, DyeColor.BROWN.blockMeta));
     }
 
-    private static void generateDyedWools() {
-        for (DyeColor color : DyeColor.values()) {
-            RecipeBuilder.Shapeless(CORE_NAMESPACE).addInput("minecraft:wools").addInput(Items.DYE, color.itemMeta).create(color.colorID + "_wool", new ItemStack(Blocks.WOOL, 1, color.blockMeta));
-        }
-    }
-
-    private static void generateDyedPlanks() {
-        RecipeBuilderShaped planks = RecipeBuilder.Shaped(CORE_NAMESPACE)
-            .setShape(
-                "XXX",
-                "XDX",
-                "XXX"
-            ).addInput('X', "minecraft:planks");
-        for (DyeColor color : DyeColor.values()) {
-            planks.addInput('D', Items.DYE, color.itemMeta).create(color.colorID + "_wooden_planks", new ItemStack(Blocks.PLANKS_OAK_PAINTED, 8, color.blockMeta));
-        }
-    }
-
     private static void generatePolishedStones() {
         RecipeBuilderShaped shape = RecipeBuilder.Shaped(CORE_NAMESPACE)
             .setShape(
@@ -1247,13 +1224,6 @@ class WorkbenchGenerator {
             .addInput('X', Items.DUST_REDSTONE)
             .create("lamp", new ItemStack(Blocks.LAMP_IDLE, 4));
 
-        for (DyeColor color : DyeColor.values()) {
-            RecipeBuilder.Shapeless(CORE_NAMESPACE)
-                .addInput("minecraft:lamps")
-                .addInput(new ItemStack(Items.DYE, 1, color.itemMeta))
-                .create(color.colorID + "_lamp_convert", new ItemStack(Blocks.LAMP_IDLE, 1, color.blockMeta));
-        }
-
         RecipeBuilder.Shaped(CORE_NAMESPACE)
             .setShape(
                 "X",
@@ -1303,7 +1273,7 @@ class WorkbenchGenerator {
                 "X X",
                 "X#X",
                 "XRX")
-            .addInput('#', Blocks.PRESSURE_PLATE_STONE)
+            .addInput('#', "minecraft:pressureplates")
             .addInput('X', Items.INGOT_IRON)
             .addInput('R', Items.DUST_REDSTONE)
             .create("detector_rail", new ItemStack(Blocks.RAIL_DETECTOR, 8));
@@ -1348,6 +1318,25 @@ class WorkbenchGenerator {
         Registries.RECIPES.WORKBENCH.register("carve_pumpkin_sword",     new RecipeEntryCraftingWithTool(new RecipeSymbol(Blocks.PUMPKIN.getDefaultStack()), new RecipeSymbol("minecraft:tool_swords"),     new ItemStack(Blocks.PUMPKIN_CARVED_IDLE)));
     }
 
+    private static void generateStatueRecipes() {
+        final String[] statueNames = new String[]{"stone", "basalt", "limestone", "granite", "marble"};
+        final Item[] statueItems = new Item[]{Items.STATUE_STONE, Items.STATUE_BASALT, Items.STATUE_LIMESTONE, Items.STATUE_GRANITE, Items.STATUE_MARBLE};
+        final Block<?>[] statueBodyBlocks = new Block[]{Blocks.STONE, Blocks.BASALT, Blocks.LIMESTONE, Blocks.GRANITE, Blocks.MARBLE};
+        final Block<?>[] statueBaseBlocks = new Block[]{Blocks.SLAB_STONE_POLISHED, Blocks.SLAB_BASALT_POLISHED, Blocks.SLAB_LIMESTONE_POLISHED, Blocks.SLAB_GRANITE_POLISHED, Blocks.SLAB_CAPSTONE_MARBLE};
+
+        for (int i = 0; i < statueItems.length; i++) {
+            RecipeBuilder.Shaped(CORE_NAMESPACE)
+                .setShape(
+                    "#",
+                    "#",
+                    "_"
+                )
+                .addInput('#', statueBodyBlocks[i])
+                .addInput('_', statueBaseBlocks[i])
+                .create("statue_" + statueNames[i], new ItemStack(statueItems[i], 1));
+        }
+    }
+
     private static void generateUndyeingRecipes() {
         final String format = "%s_undyeing";
 
@@ -1362,26 +1351,25 @@ class WorkbenchGenerator {
         Registries.RECIPES.WORKBENCH.register(String.format(format, "lamps"), new RecipeEntryUndyeing(new RecipeSymbol("minecraft:lamps"), new ItemStack(Blocks.LAMP_IDLE)));
         Registries.RECIPES.WORKBENCH.register(String.format(format, "wools"), new RecipeEntryUndyeing(new RecipeSymbol("minecraft:wools"), new ItemStack(Blocks.WOOL)));
         Registries.RECIPES.WORKBENCH.register(String.format(format, "chests"), new RecipeEntryUndyeing(new RecipeSymbol("minecraft:chests"), new ItemStack(Blocks.CHEST_PLANKS_OAK)));
-        Registries.RECIPES.WORKBENCH.register(String.format(format, "button_planks"), new RecipeEntryUndyeing(new RecipeSymbol("minecraft:button_planks"), new ItemStack(Blocks.BUTTON_PLANKS)));
+        Registries.RECIPES.WORKBENCH.register(String.format(format, "buttons_planks"), new RecipeEntryUndyeing(new RecipeSymbol("minecraft:button_planks"), new ItemStack(Blocks.BUTTON_PLANKS)));
         Registries.RECIPES.WORKBENCH.register(String.format(format, "pressureplates_planks"), new RecipeEntryUndyeing(new RecipeSymbol("minecraft:pressureplates_planks"), new ItemStack(Blocks.PRESSURE_PLATE_PLANKS_OAK)));
     }
 
-    private static void generateStatueRecipes() {
-        final String[] statueNames = new String[] { "stone", "basalt", "limestone", "granite", "marble" };
-        final Item[] statueItems = new Item[ ] { Items.STATUE_STONE, Items.STATUE_BASALT, Items.STATUE_LIMESTONE, Items.STATUE_GRANITE, Items.STATUE_MARBLE };
-        final Block<?>[] statueBodyBlocks = new Block[] { Blocks.STONE, Blocks.BASALT, Blocks.LIMESTONE, Blocks.GRANITE, Blocks.MARBLE };
-        final Block<?>[] statueBaseBlocks = new Block[] { Blocks.SLAB_STONE_POLISHED, Blocks.SLAB_BASALT_POLISHED, Blocks.SLAB_LIMESTONE_POLISHED, Blocks.SLAB_GRANITE_POLISHED, Blocks.SLAB_CAPSTONE_MARBLE };
-
-        for (int i = 0; i < statueItems.length; i++) {
-            RecipeBuilder.Shaped(CORE_NAMESPACE)
-                .setShape(
-                    "#",
-                    "#",
-                    "_"
-                )
-                .addInput('#', statueBodyBlocks[i])
-                .addInput('_', statueBaseBlocks[i])
-                .create("statue_" + statueNames[i], new ItemStack(statueItems[i], 1));
-        }
+    private static void generateDyeingRecipes() {
+        final String format = "%s_dyeing";
+
+        Registries.RECIPES.WORKBENCH.register(String.format(format, "planks"), new RecipeEntryDyeing(new RecipeSymbol("minecraft:planks"), new ItemStack(Blocks.PLANKS_OAK_PAINTED), false, false));
+        Registries.RECIPES.WORKBENCH.register(String.format(format, "signs"), new RecipeEntryDyeing(new RecipeSymbol("minecraft:signs"), new ItemStack(Items.SIGN_PAINTED), false, true));
+        Registries.RECIPES.WORKBENCH.register(String.format(format, "doors"), new RecipeEntryDyeing(new RecipeSymbol("minecraft:doors"), new ItemStack(Items.DOOR_OAK_PAINTED), false, true));
+        Registries.RECIPES.WORKBENCH.register(String.format(format, "stairs_planks"), new RecipeEntryDyeing(new RecipeSymbol("minecraft:stairs_planks"), new ItemStack(Blocks.STAIRS_PLANKS_PAINTED), true, false));
+        Registries.RECIPES.WORKBENCH.register(String.format(format, "slabs_planks"), new RecipeEntryDyeing(new RecipeSymbol("minecraft:slabs_planks"), new ItemStack(Blocks.SLAB_PLANKS_PAINTED), true, false));
+        Registries.RECIPES.WORKBENCH.register(String.format(format, "fences_planks"), new RecipeEntryDyeing(new RecipeSymbol("minecraft:fences_planks"), new ItemStack(Blocks.FENCE_PLANKS_OAK_PAINTED), false, false));
+        Registries.RECIPES.WORKBENCH.register(String.format(format, "fence_gates_planks"), new RecipeEntryDyeing(new RecipeSymbol("minecraft:fence_gates_planks"), new ItemStack(Blocks.FENCE_GATE_PLANKS_OAK_PAINTED), true, false));
+        Registries.RECIPES.WORKBENCH.register(String.format(format, "trapdoor_planks"), new RecipeEntryDyeing(new RecipeSymbol("minecraft:trapdoor_planks"), new ItemStack(Blocks.TRAPDOOR_PLANKS_PAINTED), true, false));
+        Registries.RECIPES.WORKBENCH.register(String.format(format, "lamps"), new RecipeEntryDyeing(new RecipeSymbol("minecraft:lamps"), new ItemStack(Blocks.LAMP_IDLE), false, false));
+        Registries.RECIPES.WORKBENCH.register(String.format(format, "wools"), new RecipeEntryDyeing(new RecipeSymbol("minecraft:wools"), new ItemStack(Blocks.WOOL), false, false));
+        Registries.RECIPES.WORKBENCH.register(String.format(format, "chests"), new RecipeEntryDyeing(new RecipeSymbol("minecraft:chests"), new ItemStack(Blocks.CHEST_PLANKS_OAK_PAINTED), true, false));
+        Registries.RECIPES.WORKBENCH.register(String.format(format, "buttons_planks"), new RecipeEntryDyeing(new RecipeSymbol("minecraft:button_planks"), new ItemStack(Blocks.BUTTON_PLANKS_PAINTED), true, false));
+        Registries.RECIPES.WORKBENCH.register(String.format(format, "pressureplates_planks"), new RecipeEntryDyeing(new RecipeSymbol("minecraft:pressureplates_planks"), new ItemStack(Blocks.PRESSURE_PLATE_PLANKS_OAK_PAINTED), true, false));
     }
 }
diff --git a/util/datagen/src/main/java/net/minecraft/datagen/recipeBuilders/RecipeBuilder.java b/util/datagen/src/main/java/net/minecraft/datagen/recipeBuilders/RecipeBuilder.java
index c1f6d643c..3fcfa9d36 100644
--- a/util/datagen/src/main/java/net/minecraft/datagen/recipeBuilders/RecipeBuilder.java
+++ b/util/datagen/src/main/java/net/minecraft/datagen/recipeBuilders/RecipeBuilder.java
@@ -74,7 +74,7 @@ public final class RecipeBuilder {
     }
 
     /**
-     * Gets or creates a specified ItemGroup. <br></br>
+     * Gets or creates a specified ItemGroup. <br>
      *
      * Example code:
      * <pre>{@code
@@ -96,7 +96,7 @@ public final class RecipeBuilder {
     }
 
     /**
-     * Adds specified items to an ItemGroup. If the group does not exist before the method is called then it will be created. <br></br>
+     * Adds specified items to an ItemGroup. If the group does not exist before the method is called then it will be created. <br>
      *
      * Example code:
      * <pre>{@code
