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 c2d6b8d3b..1a4dc6ad3 100644
--- a/game/client/src/main/java/net/minecraft/client/Minecraft.java
+++ b/game/client/src/main/java/net/minecraft/client/Minecraft.java
@@ -1656,7 +1656,7 @@ public class Minecraft
 
                 if(Keyboard.getEventKeyState())
                 {
-                    if(Keyboard.getEventKey() == Keyboard.KEY_F11)
+                    if(gameSettings.keyFullScreen.isPressEvent())
                     {
                     	gameWindow.toggleFullscreen();
                     }
@@ -2230,6 +2230,7 @@ public class Minecraft
     {
         long start = System.nanoTime();
         LOGGER.info("FORCING RELOAD!");
+        I18n.getInstance().reload(gameSettings.language.value);
         sndManager = new SoundEngine();
         sndManager.init(gameSettings);
         texturePackList.refresh();
diff --git a/game/client/src/main/java/net/minecraft/client/gui/ScrolledSelectionList.java b/game/client/src/main/java/net/minecraft/client/gui/ScrolledSelectionList.java
index 0838f89de..18840ac6d 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/ScrolledSelectionList.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/ScrolledSelectionList.java
@@ -157,6 +157,8 @@ public abstract class ScrolledSelectionList {
         } else {
             yDrag = -1F;
         }
+        amountScrolled -= Mouse.getDWheel() * 20f;
+
         capYPosition();
         GL11.glDisable(GL11.GL_LIGHTING);
         GL11.glDisable(GL11.GL_FOG);
diff --git a/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentLog.java b/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentLog.java
index 559d1f4ef..42d081191 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentLog.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/hud/component/HudComponentLog.java
@@ -70,7 +70,7 @@ public class HudComponentLog extends HudComponentMovable {
 
     @Override
     public boolean isVisible(final @NotNull Minecraft mc) {
-        return true;
+        return mc.gameSettings.developerMode.value;
     }
 
     @Override
diff --git a/game/client/src/main/java/net/minecraft/client/gui/options/ScreenOptions.java b/game/client/src/main/java/net/minecraft/client/gui/options/ScreenOptions.java
index e8427f607..1edd03465 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/options/ScreenOptions.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/options/ScreenOptions.java
@@ -3,6 +3,7 @@ package net.minecraft.client.gui.options;
 import net.minecraft.client.gui.ButtonElement;
 import net.minecraft.client.gui.ItemElement;
 import net.minecraft.client.gui.Screen;
+import net.minecraft.client.gui.TooltipElement;
 import net.minecraft.client.gui.options.components.KeyBindingComponent;
 import net.minecraft.client.gui.options.components.OptionsComponent;
 import net.minecraft.client.gui.options.data.OptionsPage;
@@ -15,6 +16,8 @@ import net.minecraft.client.render.Scissor;
 import net.minecraft.client.render.tessellator.Tessellator;
 import net.minecraft.core.lang.I18n;
 import net.minecraft.core.sound.SoundCategory;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 import org.lwjgl.input.Keyboard;
 import org.lwjgl.input.Mouse;
 import org.lwjgl.opengl.GL11;
@@ -50,6 +53,8 @@ public class ScreenOptions
     private boolean doOptionsScroll = true;
     private boolean doPagesListScroll = true;
 
+    private @Nullable TooltipElement tooltip = null;
+
     public ScreenOptions(Screen parent)
     {
         this(parent, OptionsPages.GENERAL);
@@ -78,6 +83,8 @@ public class ScreenOptions
         this.renderItem = new ItemElement(mc);
 
         this.selectedPage.initComponents(mc);
+
+        this.tooltip = new TooltipElement(this.mc);
     }
 
     @Override
@@ -316,6 +323,21 @@ public class ScreenOptions
 
         super.render(mx, my, renderPartialTicks);
 
+        if (this.tooltip != null) {
+            @Nullable String tooltipTranslationKey = null;
+            for (OptionsComponent component : selectedPage.getComponents()) {
+                @Nullable String componentTooltip = component.getTooltipTranslationKey();
+                if (componentTooltip != null) {
+                    tooltipTranslationKey = componentTooltip;
+                    break;
+                }
+            }
+
+            if (tooltipTranslationKey != null) {
+                this.tooltip.render(i18n.translateKey(tooltipTranslationKey), mx, my, 0, 0);
+            }
+        }
+
         if (pickingKeyBinding != null && pickingKeyBindingComponent != null) {
             drawRect(0, 0, width, height, 0xA0000000);
             drawStringCenteredNoShadow(fontRenderer, i18n.translateKeyAndFormat("gui.options.label.picking_keybinding", i18n.translateKey(pickingKeyBinding.getId())), width / 2, height / 2 - 4, 0xffffff);
diff --git a/game/client/src/main/java/net/minecraft/client/gui/options/components/AvailableTexturePackListComponent.java b/game/client/src/main/java/net/minecraft/client/gui/options/components/AvailableTexturePackListComponent.java
index 5006b910c..f76a3193c 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/options/components/AvailableTexturePackListComponent.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/options/components/AvailableTexturePackListComponent.java
@@ -7,6 +7,7 @@ import java.util.List;
 
 import net.minecraft.client.gui.ButtonElement;
 import net.minecraft.core.sound.SoundCategory;
+import org.jetbrains.annotations.Nullable;
 import org.lwjgl.opengl.GL11;
 
 import net.minecraft.client.Minecraft;
@@ -34,7 +35,12 @@ public class AvailableTexturePackListComponent
     public void init(Minecraft mc) {
     	createTexturePackButtons();
     }
-    
+
+    @Override
+    public @Nullable String getTooltipTranslationKey() {
+        return null;
+    }
+
     @Override
     public void tick() {
         if (size != mc.texturePackList.selectedPacks.size()){
diff --git a/game/client/src/main/java/net/minecraft/client/gui/options/components/BooleanOptionComponent.java b/game/client/src/main/java/net/minecraft/client/gui/options/components/BooleanOptionComponent.java
index 52da09e07..85eeb0735 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/options/components/BooleanOptionComponent.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/options/components/BooleanOptionComponent.java
@@ -5,6 +5,7 @@ import net.minecraft.client.gui.SwitchElement;
 import net.minecraft.client.option.Option;
 import net.minecraft.client.option.OptionBoolean;
 import net.minecraft.client.render.window.CursorShape;
+import org.jetbrains.annotations.Nullable;
 
 public class BooleanOptionComponent
     extends ButtonComponent
@@ -12,9 +13,13 @@ public class BooleanOptionComponent
     private final OptionBoolean option;
     private final SwitchElement button;
 
-    public BooleanOptionComponent(OptionBoolean option)
+    public BooleanOptionComponent(OptionBoolean option) {
+        this(option, null);
+    }
+
+    public BooleanOptionComponent(OptionBoolean option, @Nullable String tooltipTranslationKey)
     {
-        super("options." + option.name);
+        super("options." + option.name, null);
 
         this.option = option;
         this.option.addCallback(__ -> onChange());
diff --git a/game/client/src/main/java/net/minecraft/client/gui/options/components/ButtonComponent.java b/game/client/src/main/java/net/minecraft/client/gui/options/components/ButtonComponent.java
index 76ae1a7aa..fd1831b52 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/options/components/ButtonComponent.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/options/components/ButtonComponent.java
@@ -6,6 +6,8 @@ import net.minecraft.client.render.tessellator.Tessellator;
 import net.minecraft.client.render.window.CursorShape;
 import net.minecraft.core.lang.I18n;
 import net.minecraft.core.sound.SoundCategory;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 import org.lwjgl.opengl.GL11;
 
 import static org.lwjgl.opengl.GL11.*;
@@ -17,13 +19,17 @@ public abstract class ButtonComponent
     protected static final int COMPONENT_SPACING = 2;
     private static final int BUTTON_WIDTH = 100;
 
-    private final String translationKey;
+    private final @NotNull String translationKey;
+    private final @Nullable String tooltipTranslationKey;
 
     protected final ButtonElement resetButton;
 
-    public ButtonComponent(String translationKey)
+    private boolean hovered = false;
+
+    public ButtonComponent(@NotNull String translationKey, @Nullable String tooltipTranslationKey)
     {
         this.translationKey = translationKey;
+        this.tooltipTranslationKey = tooltipTranslationKey;
         this.resetButton = new ButtonElement(0, 0, 0, 20, 20, "")
             .setTextures("minecraft:gui/misc/icon_reset", "minecraft:gui/misc/icon_reset_highlighted", "minecraft:gui/misc/icon_reset");
     }
@@ -38,6 +44,15 @@ public abstract class ButtonComponent
         return COMPONENT_SPACING + 20 + COMPONENT_SPACING;
     }
 
+    @Override
+    public @Nullable String getTooltipTranslationKey() {
+        if (!this.hovered) {
+            return null;
+        }
+
+        return this.tooltipTranslationKey;
+    }
+
     @Override
     public void render(int x, int y, int width, int relativeMouseX, int relativeMouseY)
     {
@@ -46,6 +61,13 @@ public abstract class ButtonComponent
         if (relativeMouseX >= 0 && relativeMouseX <= width && relativeMouseY >= COMPONENT_SPACING && relativeMouseY <= getHeight() - COMPONENT_SPACING) {
             textColor = 0xFFFFFFA0;
         }
+
+        if (relativeMouseX >= 0 && relativeMouseX <= width - BUTTON_WIDTH && relativeMouseY >= COMPONENT_SPACING && relativeMouseY <= getHeight() - COMPONENT_SPACING) {
+            this.hovered = true;
+        } else {
+            this.hovered = false;
+        }
+
         mc.font.render(Tessellator.instance, translated, x, y + (getHeight() / 2) - 4).setShadow().setColor(textColor).call();
         resetButton.enabled = !isDefault();
         renderButton(x, y, width - BUTTON_WIDTH, COMPONENT_SPACING, BUTTON_WIDTH, 20, relativeMouseX, relativeMouseY);
diff --git a/game/client/src/main/java/net/minecraft/client/gui/options/components/DifficultyOptionComponent.java b/game/client/src/main/java/net/minecraft/client/gui/options/components/DifficultyOptionComponent.java
index ef304cd73..5d8e9c990 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/options/components/DifficultyOptionComponent.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/options/components/DifficultyOptionComponent.java
@@ -6,6 +6,7 @@ import net.minecraft.client.option.OptionEnum;
 import net.minecraft.client.render.window.CursorShape;
 import net.minecraft.core.enums.Difficulty;
 import net.minecraft.core.lang.I18n;
+import org.jetbrains.annotations.Nullable;
 
 public class DifficultyOptionComponent extends ButtonComponent {
 
@@ -13,7 +14,11 @@ public class DifficultyOptionComponent extends ButtonComponent {
     protected final IntegerSliderElement slider;
 
     public DifficultyOptionComponent(OptionEnum<Difficulty> difficultyOption) {
-        super("options." + difficultyOption.name);
+        this(difficultyOption, null);
+    }
+
+    public DifficultyOptionComponent(OptionEnum<Difficulty> difficultyOption, @Nullable String tooltipTranslationKey) {
+        super("options." + difficultyOption.name, tooltipTranslationKey);
         option = difficultyOption;
         option.addCallback(__ -> onChanged());
         this.slider = new IntegerSliderElement(0, 0, 0, 150, 20, option);
diff --git a/game/client/src/main/java/net/minecraft/client/gui/options/components/FloatOptionComponent.java b/game/client/src/main/java/net/minecraft/client/gui/options/components/FloatOptionComponent.java
index 84253fd58..3b0599380 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/options/components/FloatOptionComponent.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/options/components/FloatOptionComponent.java
@@ -4,6 +4,7 @@ import net.minecraft.client.Minecraft;
 import net.minecraft.client.gui.SliderElement;
 import net.minecraft.client.option.OptionFloat;
 import net.minecraft.client.render.window.CursorShape;
+import org.jetbrains.annotations.Nullable;
 
 public class FloatOptionComponent
         extends ButtonComponent
@@ -13,7 +14,12 @@ public class FloatOptionComponent
 
     public FloatOptionComponent(OptionFloat option)
     {
-        super("options." + option.name);
+        this(option, null);
+    }
+
+    public FloatOptionComponent(OptionFloat option, @Nullable String tooltipTranslationKey)
+    {
+        super("options." + option.name, tooltipTranslationKey);
 
         this.option = option;
         this.option.addCallback(__ -> onChanged());
diff --git a/game/client/src/main/java/net/minecraft/client/gui/options/components/FramerateOptionComponent.java b/game/client/src/main/java/net/minecraft/client/gui/options/components/FramerateOptionComponent.java
index a16e96ec3..ea8bd9dd1 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/options/components/FramerateOptionComponent.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/options/components/FramerateOptionComponent.java
@@ -4,11 +4,17 @@ import net.minecraft.client.Minecraft;
 import net.minecraft.client.option.OptionRange;
 import net.minecraft.client.render.window.CursorShape;
 import net.minecraft.client.render.window.GameWindowGLFW;
+import org.jetbrains.annotations.Nullable;
 
 public class FramerateOptionComponent extends ToggleableOptionComponent<Integer>{
     public FramerateOptionComponent(OptionRange option) {
-        super(option);
+        this(option, null);
     }
+
+    public FramerateOptionComponent(OptionRange option, @Nullable String tooltipTranslationKey) {
+        super(option, tooltipTranslationKey);
+    }
+
     @Override
     protected void renderButton(int x, int y, int relativeButtonX, int relativeButtonY, int buttonWidth, int buttonHeight, int relativeMouseX, int relativeMouseY)
     {
diff --git a/game/client/src/main/java/net/minecraft/client/gui/options/components/IntegerOptionComponent.java b/game/client/src/main/java/net/minecraft/client/gui/options/components/IntegerOptionComponent.java
index 089d67cb0..250f06003 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/options/components/IntegerOptionComponent.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/options/components/IntegerOptionComponent.java
@@ -4,6 +4,7 @@ import net.minecraft.client.Minecraft;
 import net.minecraft.client.gui.ButtonElement;
 import net.minecraft.client.option.OptionInteger;
 import net.minecraft.client.render.window.CursorShape;
+import org.jetbrains.annotations.Nullable;
 
 public class IntegerOptionComponent
     extends ButtonComponent
@@ -13,7 +14,12 @@ public class IntegerOptionComponent
 
     public IntegerOptionComponent(OptionInteger option)
     {
-        super("options." + option.name);
+        this(option, null);
+    }
+
+    public IntegerOptionComponent(OptionInteger option, @Nullable String tooltipTranslationKey)
+    {
+        super("options." + option.name, tooltipTranslationKey);
 
         this.option = option;
         this.option.addCallback(__ -> onChanged());
diff --git a/game/client/src/main/java/net/minecraft/client/gui/options/components/KeyBindingComponent.java b/game/client/src/main/java/net/minecraft/client/gui/options/components/KeyBindingComponent.java
index 1706b7973..76e5084c4 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/options/components/KeyBindingComponent.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/options/components/KeyBindingComponent.java
@@ -4,6 +4,7 @@ import net.minecraft.client.gui.ButtonElement;
 import net.minecraft.client.gui.options.ScreenOptions;
 import net.minecraft.client.option.KeyBinding;
 import net.minecraft.client.render.window.CursorShape;
+import org.jetbrains.annotations.Nullable;
 
 public class KeyBindingComponent
         extends ButtonComponent
@@ -14,7 +15,12 @@ public class KeyBindingComponent
 
     public KeyBindingComponent(KeyBinding keyBinding)
     {
-        super(keyBinding.getId());
+        this(keyBinding, null);
+    }
+
+    public KeyBindingComponent(KeyBinding keyBinding, @Nullable String tooltipTranslationKey)
+    {
+        super(keyBinding.getId(), tooltipTranslationKey);
 
         this.keyBinding = keyBinding;
         this.keyButton = new ButtonElement(0, 0, 0, 150, 20, keyBinding.getKeyName());
diff --git a/game/client/src/main/java/net/minecraft/client/gui/options/components/LanguagePackListComponent.java b/game/client/src/main/java/net/minecraft/client/gui/options/components/LanguagePackListComponent.java
index bd31099ab..92e8fa27b 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/options/components/LanguagePackListComponent.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/options/components/LanguagePackListComponent.java
@@ -6,6 +6,7 @@ import net.minecraft.client.render.tessellator.Tessellator;
 import net.minecraft.core.lang.I18n;
 import net.minecraft.core.lang.Language;
 import net.minecraft.core.lang.LanguageSeeker;
+import org.jetbrains.annotations.Nullable;
 import org.lwjgl.opengl.GL11;
 import org.slf4j.Logger;
 
@@ -29,6 +30,11 @@ public class LanguagePackListComponent
 
     }
 
+    @Override
+    public @Nullable String getTooltipTranslationKey() {
+        return null;
+    }
+
     @Override
     public void init(Minecraft mc) {
         buttons.clear();
diff --git a/game/client/src/main/java/net/minecraft/client/gui/options/components/OptionsCategory.java b/game/client/src/main/java/net/minecraft/client/gui/options/components/OptionsCategory.java
index a4216a707..15babdda7 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/options/components/OptionsCategory.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/options/components/OptionsCategory.java
@@ -6,6 +6,7 @@ import net.minecraft.client.render.tessellator.Tessellator;
 import net.minecraft.core.item.ItemStack;
 import net.minecraft.core.lang.I18n;
 import net.minecraft.core.sound.SoundCategory;
+import org.jetbrains.annotations.Nullable;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -28,14 +29,23 @@ public class OptionsCategory
 
     private ItemStack icon = null;
 
+    private final @Nullable String tooltipTranslationKey;
+    private @Nullable String shownTooltipTranslationKey = null;
+
     public OptionsCategory(String translationKey)
     {
-        this.translationKey = translationKey;
+        this(translationKey, null);
     }
 
     public OptionsCategory(String translationKey, ItemStack icon)
+    {
+        this(translationKey, icon, null);
+    }
+
+    public OptionsCategory(String translationKey, ItemStack icon, @Nullable String tooltipTranslationKey)
     {
         this.translationKey = translationKey;
+        this.tooltipTranslationKey = tooltipTranslationKey;
         this.icon = icon;
     }
 
@@ -54,6 +64,11 @@ public class OptionsCategory
         }
     }
 
+    @Override
+    public @Nullable String getTooltipTranslationKey() {
+        return this.shownTooltipTranslationKey;
+    }
+
     @Override
     public void tick() {
         for (OptionsComponent component : components)
@@ -80,9 +95,12 @@ public class OptionsCategory
     {
         I18n i18n = I18n.getInstance();
         int textColor = 0x7F7F7F;
+        this.shownTooltipTranslationKey = null;
         if (relativeMouseX >= 0 && relativeMouseX < width && relativeMouseY >= 0 && relativeMouseY < TOP_SPACING)
         {
             textColor = 0xFFFFA0;
+            this.shownTooltipTranslationKey = this.tooltipTranslationKey;
+
         }
         mc.font.render(Tessellator.instance, (this.collapsed ? "▷" : "▽"), x, y + PADDING + 10).setShadow().setColor(textColor).call();
 
@@ -99,6 +117,11 @@ public class OptionsCategory
             {
                 component.render(x + COMPONENT_LEFT_MARGIN, y + componentY, width - COMPONENT_LEFT_MARGIN, relativeMouseX - COMPONENT_LEFT_MARGIN, relativeMouseY - componentY);
                 componentY += component.getHeight();
+
+                @Nullable String componentTooltipTranslationKey = component.getTooltipTranslationKey();
+                if (componentTooltipTranslationKey != null) {
+                    this.shownTooltipTranslationKey = componentTooltipTranslationKey;
+                }
             }
         }
     }
diff --git a/game/client/src/main/java/net/minecraft/client/gui/options/components/OptionsComponent.java b/game/client/src/main/java/net/minecraft/client/gui/options/components/OptionsComponent.java
index 642f206fa..96466a400 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/options/components/OptionsComponent.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/options/components/OptionsComponent.java
@@ -1,11 +1,14 @@
 package net.minecraft.client.gui.options.components;
 
 import net.minecraft.client.Minecraft;
+import org.jetbrains.annotations.Nullable;
 
 public interface OptionsComponent
 {
     default void init(Minecraft mc) { }
 
+    @Nullable String getTooltipTranslationKey();
+
     int getHeight();
 
     void render(int x, int y, int width, int relativeMouseX, int relativeMouseY);
diff --git a/game/client/src/main/java/net/minecraft/client/gui/options/components/SearchFieldComponent.java b/game/client/src/main/java/net/minecraft/client/gui/options/components/SearchFieldComponent.java
index 79f87af63..e6f0bd19b 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/options/components/SearchFieldComponent.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/options/components/SearchFieldComponent.java
@@ -7,6 +7,7 @@ import net.minecraft.client.gui.options.data.OptionsPage;
 import net.minecraft.client.gui.options.data.OptionsPageRegistry;
 import net.minecraft.client.gui.text.TextFieldEditor;
 import net.minecraft.core.sound.SoundCategory;
+import org.jetbrains.annotations.Nullable;
 import org.lwjgl.input.Keyboard;
 
 import java.util.ArrayList;
@@ -43,6 +44,11 @@ public class SearchFieldComponent
         textField.parent = mc.currentScreen;
     }
 
+    @Override
+    public @Nullable String getTooltipTranslationKey() {
+        return null;
+    }
+
     @Override
     public void tick() {
         textField.updateCursorCounter();
diff --git a/game/client/src/main/java/net/minecraft/client/gui/options/components/SelectedTexturePackListComponent.java b/game/client/src/main/java/net/minecraft/client/gui/options/components/SelectedTexturePackListComponent.java
index 92d63a059..3c1ae2367 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/options/components/SelectedTexturePackListComponent.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/options/components/SelectedTexturePackListComponent.java
@@ -9,6 +9,7 @@ import net.minecraft.core.Global;
 import net.minecraft.core.lang.I18n;
 import net.minecraft.core.net.command.TextFormatting;
 import net.minecraft.core.sound.SoundCategory;
+import org.jetbrains.annotations.Nullable;
 import org.lwjgl.opengl.GL11;
 
 import java.util.ArrayList;
@@ -41,6 +42,11 @@ public class SelectedTexturePackListComponent implements OptionsComponent {
         }
     }
 
+    @Override
+    public @Nullable String getTooltipTranslationKey() {
+        return null;
+    }
+
     public void createTexturePackButtons() {
         selectedPacks.clear();
 
diff --git a/game/client/src/main/java/net/minecraft/client/gui/options/components/ShortcutComponent.java b/game/client/src/main/java/net/minecraft/client/gui/options/components/ShortcutComponent.java
index a2a15891b..32a65fc02 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/options/components/ShortcutComponent.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/options/components/ShortcutComponent.java
@@ -4,6 +4,7 @@ import net.minecraft.client.Minecraft;
 import net.minecraft.client.gui.ButtonElement;
 import net.minecraft.core.lang.I18n;
 import net.minecraft.core.sound.SoundCategory;
+import org.jetbrains.annotations.Nullable;
 
 public class ShortcutComponent
     implements OptionsComponent {
@@ -21,6 +22,11 @@ public class ShortcutComponent
         this.button = new ButtonElement(0, 0, 0, BUTTON_WIDTH, 20, "");
     }
 
+    @Override
+    public @Nullable String getTooltipTranslationKey() {
+        return null;
+    }
+
     @Override
     public int getHeight() {
         return COMPONENT_SPACING + 20 + COMPONENT_SPACING;
diff --git a/game/client/src/main/java/net/minecraft/client/gui/options/components/ToggleableOptionComponent.java b/game/client/src/main/java/net/minecraft/client/gui/options/components/ToggleableOptionComponent.java
index 8bdc4fa36..130315d36 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/options/components/ToggleableOptionComponent.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/options/components/ToggleableOptionComponent.java
@@ -4,6 +4,7 @@ import net.minecraft.client.Minecraft;
 import net.minecraft.client.gui.IntegerSliderElement;
 import net.minecraft.client.option.OptionToggleable;
 import net.minecraft.client.render.window.CursorShape;
+import org.jetbrains.annotations.Nullable;
 
 public class ToggleableOptionComponent<E>
         extends ButtonComponent
@@ -13,7 +14,12 @@ public class ToggleableOptionComponent<E>
 
     public ToggleableOptionComponent(OptionToggleable<E> option)
     {
-        super("options." + option.name);
+        this(option, null);
+    }
+
+    public ToggleableOptionComponent(OptionToggleable<E> option, @Nullable String tooltipTranslationKey)
+    {
+        super("options." + option.name, tooltipTranslationKey);
 
         this.option = option;
         this.option.addCallback(__ -> onChanged());
diff --git a/game/client/src/main/java/net/minecraft/client/gui/options/components/TooltipPickerComponent.java b/game/client/src/main/java/net/minecraft/client/gui/options/components/TooltipPickerComponent.java
index 2794b2fb7..d9f17af7c 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/options/components/TooltipPickerComponent.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/options/components/TooltipPickerComponent.java
@@ -3,6 +3,7 @@ package net.minecraft.client.gui.options.components;
 import net.minecraft.client.gui.TooltipElement;
 import net.minecraft.client.option.OptionTooltip;
 import net.minecraft.client.render.window.CursorShape;
+import org.jetbrains.annotations.Nullable;
 import org.lwjgl.opengl.GL11;
 
 public class TooltipPickerComponent
@@ -11,7 +12,11 @@ public class TooltipPickerComponent
     private final TooltipElement tooltip;
 
     public TooltipPickerComponent(final OptionTooltip option) {
-        super(option);
+        this(option, null);
+    }
+
+    public TooltipPickerComponent(final OptionTooltip option, @Nullable String tooltipTranslationKey) {
+        super(option, tooltipTranslationKey);
 
         this.tooltip = new TooltipElement(mc);
         this.slider.renderText = false;
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 a09d46c54..7906d2861 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
@@ -268,6 +268,7 @@ public abstract class OptionsPages
                 .withComponent(new KeyBindingComponent(mc.gameSettings.keyLockRotation))
                 .withComponent(new KeyBindingComponent(mc.gameSettings.keyScreenshot))
                 .withComponent(new KeyBindingComponent(mc.gameSettings.keyShowConsole))
+                .withComponent(new KeyBindingComponent(mc.gameSettings.keyFullScreen))
             )
         );
 
@@ -408,7 +409,13 @@ public abstract class OptionsPages
                     for (final Stat stat : StatList.getAllStats()){
                         mc.thePlayer.addStat(stat, mc.thePlayer.getStat(stat) == 0 ? 1 : 0);
                     }
-                }))
+                }) {
+                    @Override
+                    public void render(int x, int y, int width, int relativeMouseX, int relativeMouseY) {
+                        this.button.enabled = (mc.thePlayer != null);
+                        super.render(x, y, width, relativeMouseX, relativeMouseY);
+                    }
+                })
                 .withComponent(new ShortcutComponent("gui.options.page.debug.button.crash.soft", () -> {
                     throw new RuntimeException("Test Soft Exception");
                 }))
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 655b580c6..ddd128978 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
@@ -217,6 +217,15 @@ public class GameSettings {
 	public KeyBinding keyHotBarRight = new KeyBinding("key.hotbar.right").setDefault(InputDevice.keyboard, KEY_PERIOD);
 	public KeyBinding keyHotBarSwitch = new KeyBinding("key.hotbar.switch").setDefault(InputDevice.keyboard, KEY_TAB);
 
+	public KeyBinding keyHideGui = new KeyBinding("key.immersiveMode").setDefault(InputDevice.keyboard, KEY_F1);
+	public KeyBinding keyScreenshot = new KeyBinding("key.screenshot").setDefault(InputDevice.keyboard, KEY_F2);
+	public KeyBinding keyToggleDebug = new KeyBinding("key.toggleDebug").setDefault(InputDevice.keyboard, KEY_F3);
+	public KeyBinding keyToggleOverlay = new KeyBinding("key.toggleOverlay").setDefault(InputDevice.keyboard, KEY_F4);
+	public KeyBinding keySwitchPerspective = new KeyBinding("key.switchPerspective").setDefault(InputDevice.keyboard, KEY_F5);
+	public KeyBinding keyCinematicCamera = new KeyBinding("key.cinematicCamera").setDefault(InputDevice.keyboard, KEY_F8);
+	public KeyBinding keyFullScreen = new KeyBinding("key.fullscreen").setDefault(InputDevice.keyboard, KEY_F11);
+	public KeyBinding keyReleaseCursor = new KeyBinding("key.debug.releasecursor").setDefault(InputDevice.keyboard, KEY_F12);
+
 	public KeyBinding keyHotBarSlot1 = new KeyBinding("key.hotbar.1").setDefault(InputDevice.keyboard, KEY_1);
 	public KeyBinding keyHotBarSlot2 = new KeyBinding("key.hotbar.2").setDefault(InputDevice.keyboard, KEY_2);
 	public KeyBinding keyHotBarSlot3 = new KeyBinding("key.hotbar.3").setDefault(InputDevice.keyboard, KEY_3);
@@ -236,19 +245,13 @@ public class GameSettings {
 	public KeyBinding keyPlayerList = new KeyBinding("key.playerList").setDefault(InputDevice.keyboard, KEY_L);
 	public KeyBinding keyPhotoMode = new KeyBinding("key.photoMode").setDefault(InputDevice.keyboard, KEY_P);
 	public KeyBinding keyLockRotation = new KeyBinding("key.lockRotation").setDefault(InputDevice.keyboard, KEY_Z);
-	public KeyBinding keyScreenshot = new KeyBinding("key.screenshot").setDefault(InputDevice.keyboard, KEY_F2);
-	public KeyBinding keyCinematicCamera = new KeyBinding("key.cinematicCamera").setDefault(InputDevice.keyboard, KEY_F8);
-	public KeyBinding keySwitchPerspective = new KeyBinding("key.switchPerspective").setDefault(InputDevice.keyboard, KEY_F5);
-	public KeyBinding keyHideGui = new KeyBinding("key.immersiveMode").setDefault(InputDevice.keyboard, KEY_F1);
 	public KeyBinding keyAutoWalk = new KeyBinding("key.autoWalk");
 	public KeyBinding keyEasyBridge = new KeyBinding("key.easyBridge");
 	public KeyBinding keySprint = new KeyBinding("key.sprint");
-	public KeyBinding keyToggleOverlay = new KeyBinding("key.toggleOverlay").setDefault(InputDevice.keyboard, KEY_F4);
 	public KeyBinding keyOpenAchievements = new KeyBinding("key.openAchievements");
 	public KeyBinding keyOpenStatistics = new KeyBinding("key.openStatistics");
 	public KeyBinding keyShowConsole = new KeyBinding("key.showConsole").setDefault(InputDevice.keyboard, KEY_GRAVE);
 	public List<KeyBinding> debugsKeys = new ArrayList<>();
-	public KeyBinding keyToggleDebug = new KeyBinding("key.toggleDebug").setDefault(InputDevice.keyboard, KEY_F3);
 	public KeyBinding keyReload = new KeyBinding("key.debug.reload").setDefault(InputDevice.keyboard, KEY_R);
 	public KeyBinding keySwitchGamemode = new KeyBinding("key.debug.gamemode").setDefault(InputDevice.keyboard, KEY_N);
 	public KeyBinding keyDumpTextures = new KeyBinding("key.debug.dumptextures").setDefault(InputDevice.keyboard, KEY_D);
@@ -261,7 +264,6 @@ public class GameSettings {
 	public KeyBinding keyToggleItemInfo = new KeyBinding("key.debug.iteminfo").setDefault(InputDevice.keyboard, KEY_H);
 	public KeyBinding keyReloadChunks = new KeyBinding("key.debug.reloadchunks").setDefault(InputDevice.keyboard, KEY_A);
 	public KeyBinding keySmoothLighting = new KeyBinding("key.debug.smoothlighting").setDefault(InputDevice.keyboard, KEY_S);
-	public KeyBinding keyReleaseCursor = new KeyBinding("key.debug.releasecursor").setDefault(InputDevice.keyboard, KEY_F12);
 	public KeyBinding keyShowShortcuts = new KeyBinding("key.debug.shortcuts").setDefault(InputDevice.keyboard, KEY_Q);
 	public KeyBinding keyToggleTextureAtlas = new KeyBinding("key.debug.textureAtlas");
 	public KeyBinding keyLoadChunksQuickly = new KeyBinding("key.debug.loadChunksQuickly");
diff --git a/game/client/src/main/java/net/minecraft/client/render/WorldRenderer.java b/game/client/src/main/java/net/minecraft/client/render/WorldRenderer.java
index b57d0b40c..1de314f21 100644
--- a/game/client/src/main/java/net/minecraft/client/render/WorldRenderer.java
+++ b/game/client/src/main/java/net/minecraft/client/render/WorldRenderer.java
@@ -163,7 +163,7 @@ public class WorldRenderer
 				continue;
 			}
 			
-			float hitRadius = entity.getPickRadius();
+			float hitRadius = entity == mc.thePlayer.vehicle ? 0 : entity.getPickRadius();
 			AABB hitBox = entity.bb.grow(hitRadius, hitRadius, hitRadius);
 			
 			if(hitBox.contains(pos1)) {
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 6e0880f0a..283c34a26 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
@@ -112,7 +112,7 @@ public class TileEntityRendererSign extends TileEntityRenderer<TileEntitySign>
         GL11.glTranslatef(0 * scale, height * scale, 1.05f * scale);
         GL11.glNormal3f(0, 0, 1);
         GL11.glDepthMask(false);
-        GL11.glColor3f(1, 1, 1);
+        GL11.glColor4f(1, 1, 1, 1);
         final EnumSignPicture picture = tileEntity.getPicture();
         if (picture != EnumSignPicture.NONE) drawTexturedModalRect(1, 0.5, colorSign,TextureRegistry.getTexture(picture.getTextureKey()));
         GL11.glDisable(GL11.GL_BLEND);
diff --git a/game/client/src/main/java/net/minecraft/client/sound/SoundRepository.java b/game/client/src/main/java/net/minecraft/client/sound/SoundRepository.java
index 37fd7f76e..d58bf7cd1 100644
--- a/game/client/src/main/java/net/minecraft/client/sound/SoundRepository.java
+++ b/game/client/src/main/java/net/minecraft/client/sound/SoundRepository.java
@@ -29,7 +29,7 @@ public class SoundRepository extends Registry<NamedSoundRepository> {
     }
 
     public @Nullable SoundEvent getSoundEvent(@Nullable String key) {
-        if (key == null) return null;
+        if (key == null || "".equals(key)) return null;
         return getSoundEvent(makeNamespaceFromKey(key));
     }
 
@@ -38,7 +38,7 @@ public class SoundRepository extends Registry<NamedSoundRepository> {
         NamedSoundRepository soundRepository = getItem(namespaceID.namespace());
         if (soundRepository == null) {
             if (!cachedRepositoryErrors.contains(namespaceID.namespace())) {
-                LOGGER.error("Could not find sound repository under namespace '{}'!", namespaceID.namespace());
+                LOGGER.error("Could not find sound repository under namespace '{}'!", namespaceID.namespace(), new RuntimeException());
                 cachedRepositoryErrors.add(namespaceID.namespace());
             }
             return null;
@@ -47,7 +47,7 @@ public class SoundRepository extends Registry<NamedSoundRepository> {
     }
 
     public @Nullable SoundEntry getSoundEntry(@Nullable String key) {
-        if (key == null) return null;
+        if (key == null || "".equals(key)) return null;
         return getSoundEntry(makeNamespaceFromKey(key));
     }
 
@@ -56,7 +56,7 @@ public class SoundRepository extends Registry<NamedSoundRepository> {
         SoundEvent event = getSoundEvent(namespaceID);
         if (event == null) {
             if (!cachedEntryErrors.contains(namespaceID)) {
-                LOGGER.error("Could not find sound event in namespace '{}' matching key '{}'!", namespaceID.namespace(), namespaceID.value());
+                LOGGER.error("Could not find sound event in namespace '{}' matching key '{}'!", namespaceID.namespace(), namespaceID.value(), new RuntimeException());
                 cachedEntryErrors.add(namespaceID.makePermanent());
             }
             return null;
diff --git a/game/client/src/main/resources/assets/minecraft/textures/entity/sign/green.png b/game/client/src/main/resources/assets/minecraft/textures/entity/sign/green.png
index bf8f431e8..9850eb31c 100644
Binary files a/game/client/src/main/resources/assets/minecraft/textures/entity/sign/green.png and b/game/client/src/main/resources/assets/minecraft/textures/entity/sign/green.png differ
diff --git a/game/core/src/main/java/net/minecraft/core/block/Block.java b/game/core/src/main/java/net/minecraft/core/block/Block.java
index d79b3b7dc..1fa72c0a0 100644
--- a/game/core/src/main/java/net/minecraft/core/block/Block.java
+++ b/game/core/src/main/java/net/minecraft/core/block/Block.java
@@ -619,6 +619,11 @@ public final class Block<T extends BlockLogic>
         return logic.isClimbable(world, x, y, z);
     }
 
+    @Override
+    public boolean isEquivalent(@NotNull World world, int thisX, int thisY, int thisZ, int otherX, int otherY, int otherZ) {
+        return logic.isEquivalent(world, thisX, thisY, thisZ, otherX, otherY, otherZ);
+    }
+
     public static boolean hasLogicClass(@Nullable Block<?> block, Class<?> logicClass) {
         return block != null && logicClass.isAssignableFrom(block.getLogic().getClass());
     }
diff --git a/game/core/src/main/java/net/minecraft/core/block/BlockInterface.java b/game/core/src/main/java/net/minecraft/core/block/BlockInterface.java
index 570450958..7e4b69a7b 100644
--- a/game/core/src/main/java/net/minecraft/core/block/BlockInterface.java
+++ b/game/core/src/main/java/net/minecraft/core/block/BlockInterface.java
@@ -106,4 +106,5 @@ interface BlockInterface {
     int getPistonPushReaction(World world, int x, int y, int z);
     int getPlacedBlockMetadata(@Nullable Player player, ItemStack stack, World world, int x, int y, int z, Side side, double xPlaced, double yPlaced);
     boolean isClimbable(World world, int x, int y, int z);
+    boolean isEquivalent(@NotNull World world, int thisX, int thisY, int thisZ, int otherX, int otherY, int otherZ);
 }
diff --git a/game/core/src/main/java/net/minecraft/core/block/BlockLogic.java b/game/core/src/main/java/net/minecraft/core/block/BlockLogic.java
index dded8a700..0c7fe8451 100644
--- a/game/core/src/main/java/net/minecraft/core/block/BlockLogic.java
+++ b/game/core/src/main/java/net/minecraft/core/block/BlockLogic.java
@@ -495,6 +495,16 @@ public class BlockLogic implements BlockInterface, IItemConvertible {
         return false;
     }
 
+    @Override
+    public boolean isEquivalent(@NotNull World world, int thisX, int thisY, int thisZ, int otherX, int otherY, int otherZ) {
+        Block<?> bBlock = world.getBlock(otherX, otherY, otherZ);
+
+        int thisMeta = world.getBlockMetadata(thisX, thisY, thisZ);
+        int otherMeta = world.getBlockMetadata(otherX, otherY, otherZ);
+
+        return this.block == bBlock && thisMeta == otherMeta;
+    }
+
     @Override
     public Item asItem() {
         return block.asItem();
diff --git a/game/core/src/main/java/net/minecraft/core/block/BlockLogicFenceGate.java b/game/core/src/main/java/net/minecraft/core/block/BlockLogicFenceGate.java
index 2a57af80c..a35edb2b4 100644
--- a/game/core/src/main/java/net/minecraft/core/block/BlockLogicFenceGate.java
+++ b/game/core/src/main/java/net/minecraft/core/block/BlockLogicFenceGate.java
@@ -102,6 +102,7 @@ public class BlockLogicFenceGate extends BlockLogic implements IPaintable
         if(isOpen(l))
         {
             world.setBlockMetadataWithNotify(x, y, z, l & ~MASK_OPEN);
+            playOpenSound(world, player, x, y, z, false);
         } else
         {
             int i1 = 0;
@@ -114,14 +115,9 @@ public class BlockLogicFenceGate extends BlockLogic implements IPaintable
                 l = (l & 0xF0) | i1;
             }
             world.setBlockMetadataWithNotify(x, y, z, l | MASK_OPEN);
+            playOpenSound(world, player, x, y, z, true);
         }
-        if(Math.random() < 0.5D)
-        {
-            world.playSoundEffect(player, SoundCategory.WORLD_SOUNDS, x, y, z, "random.door_open", 1.0F, 1.0F);
-        } else
-        {
-            world.playSoundEffect(player, SoundCategory.WORLD_SOUNDS, x, y, z, "random.door_close", 1.0F, 1.0F);
-        }
+
         return true;
     }
 
@@ -132,8 +128,20 @@ public class BlockLogicFenceGate extends BlockLogic implements IPaintable
         boolean hasPower = world.hasNeighborSignal(x, y, z) || world.hasDirectSignal(x, y, z);
         if (hasPower && !wasPowered) {
             world.setBlockMetadataWithNotify(x, y, z, meta | MASK_OPEN | MASK_POWERED);
-        } else {
+            playOpenSound(world, null, x, y, z, true);
+        } else if (!hasPower && wasPowered) {
             world.setBlockMetadataWithNotify(x, y, z, meta & ~(MASK_POWERED | MASK_OPEN));
+            playOpenSound(world, null, x, y, z, false);
+        }
+    }
+
+    public void playOpenSound(@NotNull World world, @Nullable Player player, int x, int y, int z, boolean opening) {
+        if(Math.random() < 0.5D)
+        {
+            world.playSoundEffect(player, SoundCategory.WORLD_SOUNDS, x, y, z, "random.door_open", 1.0F, 1.0F);
+        } else
+        {
+            world.playSoundEffect(player, SoundCategory.WORLD_SOUNDS, x, y, z, "random.door_close", 1.0F, 1.0F);
         }
     }
 
diff --git a/game/core/src/main/java/net/minecraft/core/block/BlockLogicFluid.java b/game/core/src/main/java/net/minecraft/core/block/BlockLogicFluid.java
index 6453b91a7..085493472 100644
--- a/game/core/src/main/java/net/minecraft/core/block/BlockLogicFluid.java
+++ b/game/core/src/main/java/net/minecraft/core/block/BlockLogicFluid.java
@@ -12,6 +12,7 @@ import net.minecraft.core.util.phys.Vec3;
 import net.minecraft.core.world.Dimension;
 import net.minecraft.core.world.World;
 import net.minecraft.core.world.WorldSource;
+import org.jetbrains.annotations.NotNull;
 
 import java.util.Random;
 
@@ -361,4 +362,18 @@ public abstract class BlockLogicFluid extends BlockLogic
 
         return Math.max(1.0F - f / (float)l, 0.015F);
     }
+
+    @Override
+    public boolean isEquivalent(@NotNull World world, int thisX, int thisY, int thisZ, int otherX, int otherY, int otherZ) {
+        Block<?> bBlock = world.getBlock(otherX, otherY, otherZ);
+
+        int thisMeta = world.getBlockMetadata(thisX, thisY, thisZ);
+        int otherMeta = world.getBlockMetadata(otherX, otherY, otherZ);
+
+        if (Block.hasLogicClass(bBlock, BlockLogicFluid.class)) {
+            return this.material == bBlock.getMaterial() && thisMeta == otherMeta;
+        }
+
+        return false;
+    }
 }
diff --git a/game/core/src/main/java/net/minecraft/core/block/BlockLogicLeavesBase.java b/game/core/src/main/java/net/minecraft/core/block/BlockLogicLeavesBase.java
index df990ed28..2d263ad95 100644
--- a/game/core/src/main/java/net/minecraft/core/block/BlockLogicLeavesBase.java
+++ b/game/core/src/main/java/net/minecraft/core/block/BlockLogicLeavesBase.java
@@ -12,6 +12,7 @@ import net.minecraft.core.util.helper.Side;
 import net.minecraft.core.world.World;
 import net.minecraft.core.world.WorldSource;
 import net.minecraft.core.world.season.Season;
+import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 public class BlockLogicLeavesBase extends BlockLogic
@@ -224,6 +225,16 @@ public class BlockLogicLeavesBase extends BlockLogic
     	return enableTreeShadowing ? 0.8f : 0.0f;
     }
 
+    @Override
+    public boolean isEquivalent(@NotNull World world, int thisX, int thisY, int thisZ, int otherX, int otherY, int otherZ) {
+        Block<?> bBlock = world.getBlock(otherX, otherY, otherZ);
+
+        int thisMeta = world.getBlockMetadata(thisX, thisY, thisZ) & ~MASK_DECAY_DATA;
+        int otherMeta = world.getBlockMetadata(otherX, otherY, otherZ) & ~MASK_DECAY_DATA;
+
+        return this.block == bBlock && thisMeta == otherMeta;
+    }
+
     public static boolean enableTreeShadowing = false;
     public static boolean enableDecay = true;
     int[] adjacentTreeBlocks;
diff --git a/game/core/src/main/java/net/minecraft/core/block/BlockLogicMatcher.java b/game/core/src/main/java/net/minecraft/core/block/BlockLogicMatcher.java
index 3554328b0..4ecea2b67 100644
--- a/game/core/src/main/java/net/minecraft/core/block/BlockLogicMatcher.java
+++ b/game/core/src/main/java/net/minecraft/core/block/BlockLogicMatcher.java
@@ -85,13 +85,13 @@ public class BlockLogicMatcher extends BlockLogicAxisAligned {
 
 
 
+        boolean backMatchesFront;
         Block<?> fBlock = world.getBlock(fx, fy, fz);
-        Block<?> bBlock = world.getBlock(bx, by, bz);
-
-        int fMeta = world.getBlockMetadata(fx, fy, fz);
-        int bMeta = world.getBlockMetadata(bx, by, bz);
-
-        boolean backMatchesFront = fBlock == bBlock && fMeta == bMeta;
+        if (fBlock == null) {
+            backMatchesFront = world.getBlock(bx, by, bz) == null;
+        } else {
+            backMatchesFront = fBlock.isEquivalent(world, fx, fy, fz, bx, by, bz);
+        }
 
         if (!isActive && backMatchesFront) {
             world.setBlockAndMetadataWithNotify(x, y, z, Blocks.MATCHER_ACTIVE.id(), world.getBlockMetadata(x, y, z));
diff --git a/game/core/src/main/java/net/minecraft/core/block/BlockLogicRepeater.java b/game/core/src/main/java/net/minecraft/core/block/BlockLogicRepeater.java
index 09e969d47..4e291dcc7 100644
--- a/game/core/src/main/java/net/minecraft/core/block/BlockLogicRepeater.java
+++ b/game/core/src/main/java/net/minecraft/core/block/BlockLogicRepeater.java
@@ -101,10 +101,9 @@ public class BlockLogicRepeater extends BlockLogic
     // which prevents redstone components on the other wide of the target block from staying powered.
     @Override
     public void onBlockRemoved(World world, int x, int y, int z, int data) {
-        Side front = getSideFromMeta(data);
+        Side back = getSideFromMeta(data);
+        Side front = back.getOpposite();
         world.notifyBlocksOfNeighborChange(x + front.getOffsetX(), y + front.getOffsetY(), z + front.getOffsetZ(), id());
-        Side back = front.getOpposite();
-        world.notifyBlocksOfNeighborChange(x + back.getOffsetX(), y + back.getOffsetY(), z + back.getOffsetZ(), id());
     }
 
 
@@ -211,17 +210,6 @@ public class BlockLogicRepeater extends BlockLogic
         }
     }
 
-    @Override
-	public void onBlockPlacedByWorld(World world, int x, int y, int z)
-    {
-        world.notifyBlocksOfNeighborChange(x + 1, y, z, id());
-        world.notifyBlocksOfNeighborChange(x - 1, y, z, id());
-        world.notifyBlocksOfNeighborChange(x, y, z + 1, id());
-        world.notifyBlocksOfNeighborChange(x, y, z - 1, id());
-        world.notifyBlocksOfNeighborChange(x, y - 1, z, id());
-        world.notifyBlocksOfNeighborChange(x, y + 1, z, id());
-    }
-
     @Override
 	public boolean isSolidRender()
     {
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 3da68ee16..1d8c8058c 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
@@ -1179,7 +1179,7 @@ public final class Blocks {
 
     public static final Block<?> OVERLAY_PEBBLES = register("overlay.pebbles", "minecraft:block/overlay_pebbles", 880, (b) -> new BlockLogicOverlayPebbles(b, Material.decoration))
         .withSound(BlockSounds.STONE).withHardness(0.0F).withDisabledNeighborNotifyOnMetadataChange().setStatParent(() -> Items.AMMO_PEBBLE)
-        .withTags(BlockTags.BROKEN_BY_FLUIDS, BlockTags.NOT_IN_CREATIVE_MENU);
+        .withTags(BlockTags.BROKEN_BY_FLUIDS, BlockTags.NOT_IN_CREATIVE_MENU, BlockTags.OVERRIDE_FRICTION);
 
     public static final Block<BlockLogicFenceThin> FENCE_CHAINLINK = register("fence.chainlink", "minecraft:block/fence_chainlink", 890, (BlockLogicSupplier<BlockLogicFenceThin>)b -> new BlockLogicFenceChainlink(b, Material.metal))
         .withSound(BlockSounds.METAL).withHardness(5F)
diff --git a/game/core/src/main/java/net/minecraft/core/block/piston/BlockLogicPistonMoving.java b/game/core/src/main/java/net/minecraft/core/block/piston/BlockLogicPistonMoving.java
index 1bb4fb32a..0ecafdb2f 100644
--- a/game/core/src/main/java/net/minecraft/core/block/piston/BlockLogicPistonMoving.java
+++ b/game/core/src/main/java/net/minecraft/core/block/piston/BlockLogicPistonMoving.java
@@ -142,7 +142,7 @@ public class BlockLogicPistonMoving extends BlockLogic
             Block<?> block = Blocks.blocksList[pistonTileEntity.getMovedId()];
             if(block == null || block == this.block)
             {
-                return getBlockBoundsFromState(world, x, y, z);
+                return super.getBlockBoundsFromState(world, x, y, z);
             }
             float f = pistonTileEntity.getProgress(0.0F);
             if(pistonTileEntity.isExtending())
diff --git a/game/core/src/main/java/net/minecraft/core/block/tag/BlockTags.java b/game/core/src/main/java/net/minecraft/core/block/tag/BlockTags.java
index 5b4bd4295..9b7f2e1b7 100644
--- a/game/core/src/main/java/net/minecraft/core/block/tag/BlockTags.java
+++ b/game/core/src/main/java/net/minecraft/core/block/tag/BlockTags.java
@@ -180,6 +180,10 @@ public abstract class BlockTags
      * Will drop block when piston extends into it
      */
     public static final Tag<Block<?>> PISTON_CRUSHING = Tag.of("piston_crushing");
+    /**
+     * If a normally nonsolid block overrides the friction below it, used to have pebbles increase friction on ice
+     */
+    public static final Tag<Block<?>> OVERRIDE_FRICTION = Tag.of("override_friction");
 
     public static List<Tag<Block<?>>> TAG_LIST;
 
diff --git a/game/core/src/main/java/net/minecraft/core/current/wire/WireNode.java b/game/core/src/main/java/net/minecraft/core/current/wire/WireNode.java
index 3813cb851..d510153a7 100644
--- a/game/core/src/main/java/net/minecraft/core/current/wire/WireNode.java
+++ b/game/core/src/main/java/net/minecraft/core/current/wire/WireNode.java
@@ -1,11 +1,13 @@
 package net.minecraft.core.current.wire;
 
+import com.mojang.logging.LogUtils;
 import net.minecraft.core.current.util.BlockPos;
 import net.minecraft.core.current.util.BlockState;
 import net.minecraft.core.current.util.Mth;
 import net.minecraft.core.current.util.Redstone;
 import net.minecraft.core.block.Blocks;
 import net.minecraft.core.world.World;
+import org.slf4j.Logger;
 
 /**
  * A WireNode is a Node that represents a wire in the world. It stores all the
@@ -15,6 +17,7 @@ import net.minecraft.core.world.World;
  * @author Space Walker
  */
 public class WireNode extends Node {
+	private static final Logger LOGGER = LogUtils.getLogger();
 
 	final WireConnectionManager connections;
 
@@ -102,6 +105,7 @@ public class WireNode extends Node {
 		state = WorldHelper.getBlockState(world, pos);
 
 		if (!state.is(Blocks.WIRE_REDSTONE)) {
+			LOGGER.warn("Tried to update power of non redstone dust! This should never happen!", new RuntimeException());
 			return false; // we should never get here
 		}
 
diff --git a/game/core/src/main/java/net/minecraft/core/entity/EntityFallingBlock.java b/game/core/src/main/java/net/minecraft/core/entity/EntityFallingBlock.java
index aff3a6fc2..cb0ad6c84 100644
--- a/game/core/src/main/java/net/minecraft/core/entity/EntityFallingBlock.java
+++ b/game/core/src/main/java/net/minecraft/core/entity/EntityFallingBlock.java
@@ -6,6 +6,7 @@ import net.minecraft.core.block.Blocks;
 import net.minecraft.core.block.entity.TileEntity;
 import net.minecraft.core.block.entity.TileEntityDispatcher;
 import net.minecraft.core.block.motion.CarriedBlock;
+import net.minecraft.core.block.tag.BlockTags;
 import net.minecraft.core.enums.EnumDropCause;
 import net.minecraft.core.item.ItemStack;
 import net.minecraft.core.util.helper.MathHelper;
@@ -113,8 +114,15 @@ public class EntityFallingBlock extends Entity
         if (onGround) {
             Block<?> selfBlock = carriedBlock.block();
             Block<?> blockBelow = world.getBlock(x, y - 1, z);
+            Block<?> blockAt = world.getBlock(x, y, z);
             double friction = selfBlock.friction;
-            friction *= blockBelow == null ? 0.98 : blockBelow.friction * 0.91f;
+            if (BlockTags.OVERRIDE_FRICTION.appliesTo(blockAt)) {
+                friction *= blockAt.friction * 0.91f;
+            } else if (blockBelow != null) {
+                friction *= blockBelow.friction * 0.91f;
+            } else {
+                friction *= 0.98;
+            }
             xd *= friction;
             zd *= friction;
             yd *= -0.5D;
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 ed375d1ba..7f7b511b8 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
@@ -601,7 +601,7 @@ public abstract class Mob extends Entity
         return 1.0F;
     }
 
-    public String getLivingSound()
+    public @Nullable String getLivingSound()
     {
         return null;
     }
@@ -739,7 +739,8 @@ public abstract class Mob extends Entity
 
     public void moveEntityWithHeading(float moveStrafing, float moveForward)
     {
-        int floorBlockId = world.getBlockId(MathHelper.floor(x), MathHelper.floor(bb.minY) - 1, MathHelper.floor(z));
+        Block<?> floorBlock = world.getBlock(MathHelper.floor(x), MathHelper.floor(bb.minY) - 1, MathHelper.floor(z));
+        Block<?> floorAboveBlock = world.getBlock(MathHelper.floor(x), MathHelper.floor(bb.minY), MathHelper.floor(z));
         this.muteStepSounds = false;
         this.isSkating = false;
 
@@ -811,7 +812,7 @@ public abstract class Mob extends Entity
                 yd = 0.3D;
             }
         }
-        else if (canSkate() && Blocks.blocksList[floorBlockId] != null && Blocks.blocksList[floorBlockId].hasTag(BlockTags.SKATEABLE))
+        else if (canSkate() && BlockTags.SKATEABLE.appliesTo(floorBlock))
         {
             muteStepSounds = true;
             isSkating = true;
@@ -853,9 +854,10 @@ public abstract class Mob extends Entity
             if(onGround)
             {
                 movementScale = 0.5460001F;
-                if(floorBlockId > 0)
-                {
-                    movementScale = Blocks.blocksList[floorBlockId].friction * 0.91F;
+                if (BlockTags.OVERRIDE_FRICTION.appliesTo(floorAboveBlock)) {
+                    movementScale = floorAboveBlock.friction * 0.91F;
+                } else if (floorBlock != null) {
+                    movementScale = floorBlock.friction * 0.91F;
                 }
             }
 
diff --git a/game/core/src/main/java/net/minecraft/core/entity/player/Player.java b/game/core/src/main/java/net/minecraft/core/entity/player/Player.java
index 57a47f75f..c99403814 100644
--- a/game/core/src/main/java/net/minecraft/core/entity/player/Player.java
+++ b/game/core/src/main/java/net/minecraft/core/entity/player/Player.java
@@ -489,7 +489,7 @@ public abstract class Player
         }
         else if (UUID_SHIGN.equals(uuid) || username.equals("ShignBright"))
         {
-            dropPlayerItemWithRandomChoice(new ItemStack(Items.SIGN, 1), true);
+            dropPlayerItemWithRandomChoice(new ItemStack(Items.SIGN_PAINTED, 1, DyeColor.PINK.itemMeta), true);
         }
         else if((UUID_MELON.equals(uuid) || username.equals("MelonMojito")) || (UUID_WATERMELON.equals(uuid) || username.equals("WatermelonMojito")))
         {
@@ -789,7 +789,7 @@ public abstract class Player
         {
             f /= 5F;
         }
-        if(!onGround)
+        if(!onGround && !canClimb() && vehicle == null)
         {
             f /= 5F;
         }
diff --git a/game/core/src/main/java/net/minecraft/core/item/ItemPaintBrush.java b/game/core/src/main/java/net/minecraft/core/item/ItemPaintBrush.java
index cf66fac7b..091c83f79 100644
--- a/game/core/src/main/java/net/minecraft/core/item/ItemPaintBrush.java
+++ b/game/core/src/main/java/net/minecraft/core/item/ItemPaintBrush.java
@@ -8,16 +8,20 @@ import net.minecraft.core.block.IPaintable;
 import net.minecraft.core.block.IPainted;
 import net.minecraft.core.block.entity.TileEntityActivator;
 import net.minecraft.core.block.entity.TileEntitySign;
+import net.minecraft.core.entity.Mob;
+import net.minecraft.core.entity.animal.MobSheep;
 import net.minecraft.core.entity.player.Player;
 import net.minecraft.core.net.command.TextFormatting;
 import net.minecraft.core.player.inventory.slot.Slot;
 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 org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
+import java.util.List;
 import java.util.Random;
 
 public class ItemPaintBrush extends Item {
@@ -37,10 +41,10 @@ public class ItemPaintBrush extends Item {
         if (Block.hasLogicClass(block, BlockLogicSign.class) && (player != null && player.isSneaking())) {
             TileEntitySign sign = (TileEntitySign) world.getTileEntity(blockX, blockY, blockZ);
             final DyeColor color = getColor(itemstack);
-            if (color.blockMeta != sign.getColor().id)
+            if (color != null && color.blockMeta != sign.getColor().id)
             {
                 sign.setColor(TextFormatting.get(color.blockMeta));
-                itemstack.damageItem(1, player);
+                consumePaint(itemstack, player);
                 return true;
             }
         } else if (Block.hasLogicClass(block, IPaintable.class)) {
@@ -52,12 +56,7 @@ public class ItemPaintBrush extends Item {
                     return false;
                 }
                 paintable.setColor(world, blockX, blockY, blockZ, color);
-                if (itemstack.getMetadata() >= itemstack.getMaxDamage()) {
-                    setColor(itemstack, null);
-                    itemstack.setMetadata(getMaxDamage());
-                } else {
-                    itemstack.damageItem(1, player);
-                }
+                consumePaint(itemstack, player);
                 return true;
             }
         }
@@ -70,7 +69,48 @@ public class ItemPaintBrush extends Item {
         int x = blockX + direction.getOffsetX();
         int y = blockY + direction.getOffsetY();
         int z = blockZ + direction.getOffsetZ();
-        onUseItemOnBlock(itemStack, null, world, x, y, z, direction.getSide(), 0.5, 0.5);
+        if (!onUseItemOnBlock(itemStack, null, world, x, y, z, direction.getSide(), 0.5, 0.5)) {
+            blockX += direction.getOffsetX();
+            blockY += direction.getOffsetY();
+            blockZ += direction.getOffsetZ();
+            AABB box = AABB.getTemporaryBB(blockX, blockY, blockZ, blockX + 1, blockY + 1, blockZ + 1);
+            List<MobSheep> entities = world.getEntitiesWithinAABB(MobSheep.class, box);
+            if (!entities.isEmpty()) {
+                MobSheep sheep = entities.get(0);
+                DyeColor woolColor = getColor(itemStack);
+                if(sheep.getFleeceColor() != woolColor)
+                {
+                    sheep.setFleeceColor(woolColor);
+                    consumePaint(itemStack, null);
+                }
+            }
+        }
+    }
+
+    public void consumePaint(@NotNull ItemStack itemstack, @Nullable Player player) {
+        if (itemstack.getMetadata() >= itemstack.getMaxDamage()) {
+            setColor(itemstack, null);
+            itemstack.setMetadata(getMaxDamage());
+        } else {
+            itemstack.damageItem(1, player);
+        }
+    }
+
+    @Override
+    public boolean useItemOnEntity(ItemStack itemstack, Mob mob, Player player)
+    {
+        if(mob instanceof MobSheep)
+        {
+            MobSheep mobSheep = (MobSheep)mob;
+            DyeColor woolColor = getColor(itemstack);
+            if(mobSheep.getFleeceColor() != woolColor)
+            {
+                mobSheep.setFleeceColor(woolColor);
+                consumePaint(itemstack, player);
+                return true;
+            }
+        }
+        return false;
     }
 
     @Override
@@ -124,7 +164,7 @@ public class ItemPaintBrush extends Item {
         stack.setMetadata(stack.getMaxDamage() - count);
     }
 
-    public static DyeColor getColor(@NotNull final ItemStack stack) {
+    public static @Nullable DyeColor getColor(@NotNull final ItemStack stack) {
         final CompoundTag tag = stack.getData();
         if (tag.containsKey("Color")) {
             final int color = tag.getInteger("Color");
diff --git a/game/core/src/main/java/net/minecraft/core/item/ItemPebble.java b/game/core/src/main/java/net/minecraft/core/item/ItemPebble.java
index 53dc3bb0e..e9a861bc4 100644
--- a/game/core/src/main/java/net/minecraft/core/item/ItemPebble.java
+++ b/game/core/src/main/java/net/minecraft/core/item/ItemPebble.java
@@ -62,7 +62,7 @@ public class ItemPebble extends Item implements IDispensable
         {
 
             final int newMeta = meta + 1;
-            if (!world.isBlockOpaqueCube(blockX, blockY - 1, blockZ))
+            if (!world.canPlaceOnSurfaceOfBlock(blockX, blockY - 1, blockZ))
             {
                 return false;
             }
@@ -94,7 +94,7 @@ public class ItemPebble extends Item implements IDispensable
             final int newMeta = meta + 1;
 
             final AABB bbBox = AABB.getTemporaryBB(blockX, blockY, blockZ, blockX + 1f, blockY + (2 * (newMeta + 1)) / 16.0f, blockZ + 1f);
-            if (!world.checkIfAABBIsClear(bbBox) || !world.isBlockOpaqueCube(blockX, blockY - 1, blockZ))
+            if (!world.checkIfAABBIsClear(bbBox) || !world.canPlaceOnSurfaceOfBlock(blockX, blockY - 1, blockZ))
             {
                 return false;
             }
@@ -110,7 +110,7 @@ public class ItemPebble extends Item implements IDispensable
         }
 
         // Regular placement, as all else fails
-        if (world.canBlockBePlacedAt(Blocks.OVERLAY_PEBBLES.id(), blockX, blockY, blockZ, false, side) && world.isBlockOpaqueCube(blockX, blockY - 1, blockZ))
+        if (world.canBlockBePlacedAt(Blocks.OVERLAY_PEBBLES.id(), blockX, blockY, blockZ, false, side) && world.canPlaceOnSurfaceOfBlock(blockX, blockY - 1, blockZ))
         {
             if (world.setBlockAndMetadataWithNotify(blockX, blockY, blockZ, Blocks.OVERLAY_PEBBLES.id(), 0))
             {
diff --git a/game/core/src/main/java/net/minecraft/core/player/inventory/slot/SlotArmor.java b/game/core/src/main/java/net/minecraft/core/player/inventory/slot/SlotArmor.java
index 36d7b358c..f0562c8fd 100644
--- a/game/core/src/main/java/net/minecraft/core/player/inventory/slot/SlotArmor.java
+++ b/game/core/src/main/java/net/minecraft/core/player/inventory/slot/SlotArmor.java
@@ -65,7 +65,7 @@ public class SlotArmor extends Slot
         	if (menu.slots.get(i) instanceof SlotArmor)
         	{
         		ItemStack stack = menu.slots.get(i).getItemStack();
-        		if (stack != null && (stack.itemID == Items.ARMOR_BOOTS_CHAINMAIL.id || stack.itemID == Items.ARMOR_HELMET_CHAINMAIL.id || stack.itemID == Items.ARMOR_CHESTPLATE_CHAINMAIL.id || stack.itemID == Items.ARMOR_LEGGINGS_CHAINMAIL.id))
+        		if (stack != null && (stack.itemID == Items.ARMOR_BOOTS_CHAINMAIL.id || stack.itemID == Items.ARMOR_HELMET_CHAINMAIL.id || stack.itemID == Items.ARMOR_CHESTPLATE_CHAINMAIL.id || stack.itemID == Items.ARMOR_LEGGINGS_CHAINMAIL.id) && stack.getMetadata() == 0)
 				{
         			count++;
 				}
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 175826e55..fcc26648c 100644
--- a/game/core/src/main/resources/lang/en_US/options.lang
+++ b/game/core/src/main/resources/lang/en_US/options.lang
@@ -265,6 +265,7 @@ key.immersiveMode=Immersive Mode
 key.switchPerspective=Switch Perspective
 key.screenshot=Take Screenshot
 key.cinematicCamera=Toggle Cinematic Camera
+key.fullscreen=Toggle Fullscreen
 key.autoWalk=Auto Walk
 key.easyBridge=Toggle Easy Bridge
 key.sprint=Sprint (if enabled)
