diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 000000000..9b6d6732a
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,39 @@
+# Changelog
+
+## [8.1]
+### Features
+
+### Fixes
+
+## [8.0.2]
+### Features
+
+### Fixes
+- Properly masked axis bits when calculating axis in BlockLogicAxisAligned
+- Fixed an NPE with item entities
+- Fixed firefly capture returning current count instead of captured amount
+- Fixed guidebook index not un-hiding mob entries
+- Fixed statue swap deleting armor
+- Fixed fog not being applied in font shaders
+- Fixed fire extinguish removing the wrong block
+- Fixed incorrect indexing in font renderer
+- Fixed cocoa beans not growing
+- Fixed cocoa beans being harvestable when not fully grown
+- Fixed NPE in EntityCameraThirdPersonRear
+- Fixed ghast, pigman and wolf attacking player in creative mode
+- Fixed being able to pick blocks while holding an object
+- Fixed autosave timer using float and defaulting to OFF when reset
+- Fixed shading applying to map render
+- Fixed sign consuming glowstone dust twice
+- Fixed off-by-one error in findTopSolidBlock
+- Fixed carried block/tile entity placement behavior
+- Fixed cacao leaves dropping 2 cocoa beans at growth stage 3
+- Fixed a credits screen crash
+- Fixed some tile entities wiping item NBT when broken
+  - These tile entities no longer wipe item nbt when broken:
+    - Furnaces
+    - Blast Furnaces
+    - Trommels
+    - Dispensers
+- Fix unban command banning player instead
+- Fix fence bounding box not being offset
\ No newline at end of file
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 66b997a24..b90454d22 100644
--- a/game/client/src/main/java/net/minecraft/client/Minecraft.java
+++ b/game/client/src/main/java/net/minecraft/client/Minecraft.java
@@ -2618,9 +2618,13 @@ public class Minecraft
         return GameSettings.SHOW_DEBUG_SCREEN.value && this.thePlayer != null && this.thePlayer.getGamemode() == Gamemodes.CREATIVE;
     }
 
+
+    /**
+     * @return The time between auto saves in seconds.
+     */
     public int getAutosaveTimer() {
 
-        return (int) (GameSettings.AUTOSAVE_TIMER.value * 24) * 5;
+        return GameSettings.AUTOSAVE_TIMER.value * 5;
     }
 
     @Override
diff --git a/game/client/src/main/java/net/minecraft/client/gui/ScreenCredits.java b/game/client/src/main/java/net/minecraft/client/gui/ScreenCredits.java
index 723b1c19a..cfa5c71b0 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/ScreenCredits.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/ScreenCredits.java
@@ -18,6 +18,7 @@ import org.jetbrains.annotations.Nullable;
 import org.lwjgl.input.Keyboard;
 import org.lwjgl.input.Mouse;
 import org.slf4j.Logger;
+import paulscode.sound.SoundSystem;
 
 import java.awt.*;
 import java.io.BufferedReader;
@@ -132,6 +133,7 @@ public class ScreenCredits extends Screen {
 
     @Override
     public void tick() {
+        if (!SoundSystem.initialized()) return;
         if (!this.playing || !SoundEngine.getSoundSystem().playing(SoundEngine.STREAMING)) {
             this.mc.sndManager.playMusic(SONGS[(int) (Math.random() * SONGS.length)], 0, 0, 0, 1.0f, 1.0f);
             this.playing = true;
diff --git a/game/client/src/main/java/net/minecraft/client/gui/guidebook/mobs/GuidebookSectionMob.java b/game/client/src/main/java/net/minecraft/client/gui/guidebook/mobs/GuidebookSectionMob.java
index 20ab809d3..3a64cd0a5 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/guidebook/mobs/GuidebookSectionMob.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/guidebook/mobs/GuidebookSectionMob.java
@@ -4,6 +4,7 @@ import net.minecraft.client.Minecraft;
 import net.minecraft.client.gui.guidebook.GuidebookPage;
 import net.minecraft.client.gui.guidebook.GuidebookSection;
 import net.minecraft.client.gui.guidebook.SearchableGuidebookSection;
+import net.minecraft.client.option.GameSettings;
 import net.minecraft.core.data.registry.recipe.SearchQuery;
 import net.minecraft.core.achievement.stat.StatList;
 import net.minecraft.core.entity.EntityDispatcher;
@@ -92,7 +93,7 @@ public class GuidebookSectionMob
             boolean discovered = mc.statsCounter.readStat(StatList.mobEncounterStats.get(entry != null ? entry.namespaceID : null)) > 0;
             if (mc.thePlayer.getGamemode() == Gamemodes.CREATIVE) discovered = true;
 
-            if (discovered) {
+            if (discovered || !GameSettings.HIDE_UNDISCOVERED_ITEMS.value) {
                 return I18n.getInstance().translateKey(mobInfo.getNameTranslationKey());
             } else {
                 return unknownName;
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 a31e8bfa7..90f30cf3f 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
@@ -103,7 +103,7 @@ public abstract class OptionsPages {
             .withComponent(new OptionsCategory("gui.options.page.general.category.miscellaneous")
                 .withComponent(new BooleanOptionComponent(GameSettings.DISCORD_RICH_PRESENCE))
                 .withComponent(new BooleanOptionComponent(GameSettings.ENABLE_ITEM_CLUMPING))
-                .withComponent(new FloatOptionComponent(GameSettings.AUTOSAVE_TIMER))
+                .withComponent(new ToggleableOptionComponent<>(GameSettings.AUTOSAVE_TIMER))
             )
             .withComponent(new ShortcutComponent("gui.options.page.general.button.open_worlds_folder", () -> FileOpener.open(new File(mc.getMinecraftDir(), "saves"))))
             .withComponent(new ShortcutComponent("gui.options.page.general.button.open_screenshots_folder", () -> {
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 74705fb43..03761aeb1 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
@@ -303,17 +303,13 @@ public final class GameSettings {
     public static final @NotNull OptionFloat COLOR_CORRECTION_OVERWORLD = register(new OptionFloat("colorCorrectionOverworld", 0.5f));
     public static final @NotNull OptionFloat COLOR_CORRECTION_NETHER = register(new OptionFloat("colorCorrectionNether", 0.5f));
     public static final @NotNull OptionBoolean SNEAK_TOGGLE = register(new OptionBoolean("sneakToggle", false));
-    public static final @NotNull OptionFloat AUTOSAVE_TIMER = register(new OptionFloat("autosaveTimer", 0.04166666666F)
-        .addOnChangeCallback((mc, option) -> {
-            int max = 24;
-            option.value = (option.value + max) % max;
-        })
+    public static final @NotNull OptionRange AUTOSAVE_TIMER = register(new OptionRange("autosaveTimer", 1, 25)
         .withDisplayStringProvider((mc, i18n, option) -> {
-            int timer = (int) (option.value * 24);
-            if (timer == 0) {
+            final int value = option.value;
+            if (value == 0) {
                 return i18n.translateKey("options.autosaveTimer.off");
             } else {
-                return (timer * 5) + " seconds";
+                return (value * 5) + " seconds";
             }
         }));
     public static final @NotNull OptionBoolean ENABLE_ITEM_DRAGGING = register(new OptionBoolean("enableItemDragging", true));
diff --git a/game/client/src/main/java/net/minecraft/client/render/camera/EntityCameraThirdPersonRear.java b/game/client/src/main/java/net/minecraft/client/render/camera/EntityCameraThirdPersonRear.java
index ae0757a95..0acb9e21d 100644
--- a/game/client/src/main/java/net/minecraft/client/render/camera/EntityCameraThirdPersonRear.java
+++ b/game/client/src/main/java/net/minecraft/client/render/camera/EntityCameraThirdPersonRear.java
@@ -43,6 +43,7 @@ public class EntityCameraThirdPersonRear
 
         Vector3dc start = new Vector3d(x, y, z);
         double cameraDistance = start.distance(x - numSteps * dx, y - numSteps * dy, z - numSteps * dz) - offset;
+        if (this.mc.currentWorld == null) return cameraDistance;
 
         for (int i = 0; i <= numSteps; i++) {
             HitResult hitResult = this.mc.currentWorld.checkBlockCollisionBetweenPoints(
diff --git a/game/client/src/main/java/net/minecraft/client/render/font/FontRendererDefault.java b/game/client/src/main/java/net/minecraft/client/render/font/FontRendererDefault.java
index 1033abc2b..1e89a348f 100644
--- a/game/client/src/main/java/net/minecraft/client/render/font/FontRendererDefault.java
+++ b/game/client/src/main/java/net/minecraft/client/render/font/FontRendererDefault.java
@@ -94,9 +94,9 @@ public class FontRendererDefault extends FontRenderer {
                     this.renderStringInternal_colorValue.setLength(0);
                     boolean ended = false;
                     if (i + 2 < charLength) {
-                        for (int j = 0, end = Math.min(charLength - 2, 8); j < end; j++) { // Maximum of 8 chars allowed in argb color code
+                        for (int j = i + 2, end = Math.min(charLength, i + 10); j < end; j++) { // Maximum of 8 chars allowed in argb color code
                             offAmount++;
-                            final char pChar = chars.charAt(i + 2 + j);
+                            final char pChar = chars.charAt(j);
                             if (pChar == '>'){
                                 ended = true;
                                 break;
@@ -438,9 +438,9 @@ public class FontRendererDefault extends FontRenderer {
                 if (cN == '<') {
                     int offAmount = 1;
                     boolean ended = false;
-                    for (int j = 0; j < 8; j++) { // Maximum of 8 chars allowed in argb color code
+                    for (int j = i + 2; j < Math.min(length, i + 10); j++) { // Maximum of 8 chars allowed in argb color code
                         offAmount++;
-                        if (line.charAt(j + i + 2) == '>'){
+                        if (line.charAt(j) == '>'){
                             ended = true;
                             break;
                         }
diff --git a/game/client/src/main/java/net/minecraft/client/render/item/model/ItemModelMap.java b/game/client/src/main/java/net/minecraft/client/render/item/model/ItemModelMap.java
index b69ecdb1f..349645142 100644
--- a/game/client/src/main/java/net/minecraft/client/render/item/model/ItemModelMap.java
+++ b/game/client/src/main/java/net/minecraft/client/render/item/model/ItemModelMap.java
@@ -102,6 +102,8 @@ public class ItemModelMap extends ItemModelStandard {
             final float scale2 = 0.015625F;
             GLRenderer.modelM4f().scale(scale2, scale2, scale2);
 
+            // Disable shading of map
+            GLRenderer.globalSetLightEnabled(false);
             // Render paper background of map
             this.mc.textureManager.bindTexture(this.mc.textureManager.loadTexture("/assets/minecraft/textures/misc/mapbg.png"));
             tessellator.startDrawingQuads();
diff --git a/game/client/src/main/resources/shaders/font.fsh b/game/client/src/main/resources/shaders/font.fsh
index ead383197..ee302af81 100644
--- a/game/client/src/main/resources/shaders/font.fsh
+++ b/game/client/src/main/resources/shaders/font.fsh
@@ -25,9 +25,62 @@ uniform float uAlphaTest;
 uniform float uLightMapStrength;
 uniform vec4 uColor;
 
+uniform vec2 uViewPortSize;
+layout (std140) uniform FogState {
+    vec4 color;
+    int mode;
+    float start;
+    float end;
+    float density;
+} fogState;
+
+layout (std140) uniform Matrices {
+    mat4 projection;
+    mat4 projectionInv;
+    mat4 view;
+    mat4 viewInv;
+} matrices;
+
 uniform sampler2D colortex;
 uniform sampler2D lighttex;
 
+float distance() {
+    vec4 fragPos = matrices.projectionInv * vec4((gl_FragCoord.xy / uViewPortSize) * 2.0 - 1.0, gl_FragCoord.z * 2.0 - 1.0, 1.0);
+    fragPos /= fragPos.w;
+
+    return length(fragPos.xyz);
+}
+
+// Taken from https://github.com/hughsk/glsl-fog
+float fogFactorLinear() {
+    return 1.0 - clamp(((fogState.end - (distance())) / (fogState.end - fogState.start)), 0.0, 1.0);
+}
+
+float fogFactorExp() {
+    return 1.0 - clamp(exp(-fogState.density * (distance())), 0.0, 1.0);
+}
+
+float fogFactorExp2() {
+    float LOG2 = -1.442695;
+    float d = fogState.density * (distance());
+    return 1.0 - clamp(exp2(d * d * LOG2), 0.0, 1.0);
+}
+
+
+vec4 computeFog(vec4 color) {
+    float f = 0;
+    if (fogState.mode == 0) { // Linear
+        f = fogFactorLinear();
+    }
+    else if (fogState.mode == 1) { // EXP
+        f = fogFactorExp();
+    }
+    else if (fogState.mode == 2) { // EXP2
+        f = fogFactorExp2();
+    }
+    return vec4(mix(color.rgb, fogState.color.rgb, f), color.a);
+}
+
 // min limit is a bandaid solution for unicode or other similar x256 fonts that arent double thick so that it actually works with bold and outline -PedroThePanda64
 ivec2 r = max(ivec2(512), textureSize(colortex, 0));
 ivec2 rh = r / 2;
@@ -105,6 +158,9 @@ void main() {
             }
         }
     }
+
+    FragColor = computeFog(FragColor);
+
     if (FragColor.a < uAlphaTest) {
         discard;
     }
diff --git a/game/core/src/main/java/net/minecraft/core/block/BlockLogicAxisAligned.java b/game/core/src/main/java/net/minecraft/core/block/BlockLogicAxisAligned.java
index ec2265d18..fc19d76c8 100644
--- a/game/core/src/main/java/net/minecraft/core/block/BlockLogicAxisAligned.java
+++ b/game/core/src/main/java/net/minecraft/core/block/BlockLogicAxisAligned.java
@@ -32,14 +32,18 @@ public class BlockLogicAxisAligned
     }
 
     public static int axisToMeta(final @NotNull Axis axis) {
-        if (axis == Axis.X) return 2;
-        if (axis == Axis.Z) return 1;
-        return 0;
+        return switch (axis) {
+            case X -> 2;
+            case Z -> 1;
+            default -> 0;
+        };
     }
 
     public static @NotNull Axis metaToAxis(final int meta) {
-        if (meta == 2) return Axis.X;
-        if (meta == 1) return Axis.Z;
-        return Axis.Y;
+        return switch (meta & 0b11) {
+            case 2 -> Axis.X;
+            case 1 -> Axis.Z;
+            default -> Axis.Y;
+        };
     }
 }
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 4525dde8e..4c9f96156 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
@@ -37,7 +37,7 @@ public class BlockLogicFence
 
     @Override
     public @Nullable AABBdc getCollisionAABB(final @NotNull WorldSource source, final @NotNull TilePosc tilePos) {
-        return MathHelper.aabbExpand(super.getBoundsFromState(source, tilePos), 0, 0.5, 0, new AABBd());
+        return MathHelper.aabbExpand(this.getBoundsFromState(source, tilePos), 0, 0.5, 0, new AABBd()).translate(tilePos.x(), tilePos.y(), tilePos.z());
     }
 
     @Override
diff --git a/game/core/src/main/java/net/minecraft/core/block/BlockLogicLeavesCacao.java b/game/core/src/main/java/net/minecraft/core/block/BlockLogicLeavesCacao.java
index e672a9731..09c4e73a6 100644
--- a/game/core/src/main/java/net/minecraft/core/block/BlockLogicLeavesCacao.java
+++ b/game/core/src/main/java/net/minecraft/core/block/BlockLogicLeavesCacao.java
@@ -26,6 +26,7 @@ public class BlockLogicLeavesCacao extends BlockLogicLeavesBase implements IBone
     public static final int MASK_GROWTH_DATA = 0b1111_0000;
     public static final int MAX_GROWTH_STATE = 4;
     static final int BEAN_GROWTH_RATE = 50;
+    private static final int MIN_GROWTH_HARVESTABLE = 2;
 
     public BlockLogicLeavesCacao(@NotNull Block<?> block) {
         super(block, Materials.LEAVES, Blocks.SAPLING_CACAO);
@@ -34,9 +35,9 @@ public class BlockLogicLeavesCacao extends BlockLogicLeavesBase implements IBone
 
     public boolean canBeansGrow(@NotNull World world, @NotNull TilePosc tilePos) {
         TilePos queryPos = new TilePos(tilePos);
-        for (int x = tilePos.x() - 1; x < tilePos.x() + 1; x++) {
-            for (int z = tilePos.z() - 1; z < tilePos.z() + 1; z++) {
-                if (world.getBlockType(queryPos.set(x, tilePos.y(), z)) == null) {
+        for (int x = tilePos.x() - 1; x <= tilePos.x() + 1; x++) {
+            for (int z = tilePos.z() - 1; z <= tilePos.z() + 1; z++) {
+                if (world.getBlockType(queryPos.set(x, tilePos.y(), z)) == Blocks.AIR) {
                     return true;
                 }
             }
@@ -50,8 +51,8 @@ public class BlockLogicLeavesCacao extends BlockLogicLeavesBase implements IBone
             return new ItemStack[]{new ItemStack(this)};
 
         int growthRate = getGrowthRate(data);
-        if (growthRate > 1) {
-            return new ItemStack[]{new ItemStack(Items.DYE, MathHelper.ceil((double) growthRate / 2), DyeColor.BROWN.itemMeta)};
+        if (growthRate >= MIN_GROWTH_HARVESTABLE) {
+            return new ItemStack[]{new ItemStack(Items.DYE, MathHelper.ceil(growthRate / 3.0F), DyeColor.BROWN.itemMeta)};
         }
 
         return super.getBreakResult(world, dropCause, data, tileEntity);
@@ -75,7 +76,7 @@ public class BlockLogicLeavesCacao extends BlockLogicLeavesBase implements IBone
     public boolean harvest(@NotNull World world, @NotNull TilePosc tilePos, @Nullable Player player) {
         int meta = world.getBlockData(tilePos);
         int growthRate = getGrowthRate(meta);
-        if (growthRate > 0) {
+        if (growthRate >= MIN_GROWTH_HARVESTABLE) {
             if (player != null) {
                 world.playSoundAtEntity(player, player, "item.pickup", 1, 1);
             }
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 8bcc92e0d..6a1a58549 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
@@ -167,9 +167,6 @@ implements
             ItemStack heldItem = player.getHeldItem();
             if (heldItem != null && heldItem.itemID == Items.DUST_GLOWSTONE.id && !signEntity.isGlowing() && heldItem.consumeItem(player)) {
                 signEntity.setGlowing(true);
-                if (player.getGamemode().hasBlockConsumption()) {
-                    heldItem.stackSize--;
-                }
                 player.addStat(Achievements.LIGHT_SIGN, 1);
                 return true;
             } else if (heldItem != null && heldItem.itemID == Items.SLIMEBALL.id && heldItem.consumeItem(player)) {
diff --git a/game/core/src/main/java/net/minecraft/core/block/BlockLogicStatue.java b/game/core/src/main/java/net/minecraft/core/block/BlockLogicStatue.java
index 6b542f61f..e0a12173d 100644
--- a/game/core/src/main/java/net/minecraft/core/block/BlockLogicStatue.java
+++ b/game/core/src/main/java/net/minecraft/core/block/BlockLogicStatue.java
@@ -92,7 +92,7 @@ public class BlockLogicStatue extends BlockLogic implements BlockLogic.MatcherEn
         final boolean shouldTakeItem = !isStatueHandEmpty && !isPlayerHoldingArmor;
         final boolean shouldInsertItem = !isPlayerHandEmpty && !isPlayerHoldingArmor;
         final boolean shouldTakeArmor = interactSlot != null && !isStatueArmorEmpty && isStatueHandEmpty;
-        final boolean shouldInsertArmor = interactSlot != null && isPlayerHoldingArmor;
+        final boolean shouldInsertArmor = interactSlot != null && (isStatueArmorEmpty || shouldTakeArmor) && isPlayerHoldingArmor;
 
         // Handle item interaction
         if (shouldTakeItem && shouldInsertItem) {
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 bd6f2b385..8c0ce33e2 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
@@ -25,7 +25,7 @@ public abstract class TileEntity implements ICarriable
 {
     public @Nullable World worldObj; // Null when loading data from nbt and null when held by entity
     public @Nullable CarriedBlock carriedBlock;
-    public TilePos tilePos = new TilePos();
+    public final @NotNull TilePos tilePos = new TilePos();
     protected boolean tileEntityInvalid;
 
     public TileEntity()
@@ -135,26 +135,26 @@ public abstract class TileEntity implements ICarriable
 
     @Override
     public boolean tryPlace(World world, Entity holder, int blockX, int blockY, int blockZ, Side side, double xPlaced, double yPlaced) {
-        CarriedBlock carriedBlock = this.carriedBlock;
-        tilePos.x = blockX + side.offsetX();
-        tilePos.y = blockY + side.offsetY();
-        tilePos.z = blockZ + side.offsetZ();
-
-        Block<?> currentBlock = world.getBlockType(tilePos);
-        if (currentBlock != null && !currentBlock.hasTag(BlockTags.PLACE_OVERWRITES)) return false;
-        world.setBlockTypeData(tilePos, carriedBlock.block(), carriedBlock.metadata);
-        world.markBlockNeedsUpdate(tilePos);
-
-        worldObj = world;
+        final TilePos blockPosCopy = new TilePos(blockX, blockY, blockZ);
+        final Block<?> directBlock = world.getBlockType(blockPosCopy);
+        if (!directBlock.hasTag(BlockTags.PLACE_OVERWRITES)) blockPosCopy.add(side);
+        // directBlock is irrelevant past this point and blockPosCopy is properly mutated
+
+        if (this.carriedBlock == null
+                || !world.canBlockIdBePlacedAt(this.carriedBlock.block().id(), blockPosCopy, false, Side.NONE)) return false;
+        final Block<?> placedBlock = this.carriedBlock.block();
+        this.tilePos.set(blockPosCopy);
+        this.worldObj = world;
+
+        world.setBlockTypeDataNotify(this.tilePos, placedBlock, this.carriedBlock.metadata);
         this.validate();
-        world.removeTileEntity(tilePos);
-        world.setTileEntity(tilePos, this);
-        Block<?> b = world.getBlockType(tilePos);
-        if (b != null && holder instanceof Mob mob) {
-            b.onPlacedByMob(world, tilePos, side, mob, xPlaced, yPlaced);
+        world.removeTileEntity(this.tilePos);
+        world.setTileEntity(this.tilePos, this);
+        if (holder instanceof Mob mob) {
+            placedBlock.onPlacedByMob(world, this.tilePos, side, mob, xPlaced, yPlaced);
         }
-        final boolean isSignal = carriedBlock.block().isSignalSource();
-        world.notifyBlocksInRadiusOfNeighborChange((isSignal) ? 2 : 1, tilePos, currentBlock);
+        final boolean isSignal = placedBlock.isSignalSource();
+        world.notifyBlocksInRadiusOfNeighborChange((isSignal) ? 2 : 1, this.tilePos, placedBlock);
         return true;
     }
 
diff --git a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityDispenser.java b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityDispenser.java
index 4cfc1c4f7..17493d8fe 100644
--- a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityDispenser.java
+++ b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityDispenser.java
@@ -208,7 +208,9 @@ public class TileEntityDispenser extends TileEntity
                     i1 = itemstack.stackSize;
                 }
                 itemstack.stackSize -= i1;
-                EntityItem entityitem = new EntityItem(world, (float) x + f, (float) y + f1, (float) z + f2, new ItemStack(itemstack.itemID, i1, itemstack.getMetadata()));
+                var dropStack = new ItemStack(itemstack);
+                dropStack.stackSize = i1;
+                EntityItem entityitem = new EntityItem(world, (float) x + f, (float) y + f1, (float) z + f2, dropStack);
                 float f3 = 0.05F;
                 entityitem.xd = (float) random.nextGaussian() * f3;
                 entityitem.yd = (float) random.nextGaussian() * f3 + 0.2F;
diff --git a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityFurnace.java b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityFurnace.java
index 23006d561..e1321a6b4 100644
--- a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityFurnace.java
+++ b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityFurnace.java
@@ -358,7 +358,9 @@ public class TileEntityFurnace extends TileEntity
                     i1 = itemstack.stackSize;
                 }
                 itemstack.stackSize -= i1;
-                EntityItem entityItem = new EntityItem(world, (float) x + f, (float) y + f1, (float) z + f2, new ItemStack(itemstack.itemID, i1, itemstack.getMetadata()));
+                var dropStack = new ItemStack(itemstack);
+                dropStack.stackSize = i1;
+                EntityItem entityItem = new EntityItem(world, (float) x + f, (float) y + f1, (float) z + f2, dropStack);
                 float f3 = 0.05F;
                 entityItem.xd = (float)random.nextGaussian() * f3;
                 entityItem.yd = (float)random.nextGaussian() * f3 + 0.2F;
diff --git a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityFurnaceBlast.java b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityFurnaceBlast.java
index e2bb6252c..f3612cbc6 100644
--- a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityFurnaceBlast.java
+++ b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityFurnaceBlast.java
@@ -316,7 +316,9 @@ public class TileEntityFurnaceBlast extends TileEntity
                     stackSize = item.stackSize;
                 }
                 item.stackSize -= stackSize;
-                final EntityItem entityItem = new EntityItem(world, (float) x + rx, (float) y + ry, (float) z + rz, new ItemStack(item.itemID, stackSize, item.getMetadata()));
+                var dropStack = new ItemStack(item);
+                item.stackSize = stackSize;
+                final EntityItem entityItem = new EntityItem(world, (float) x + rx, (float) y + ry, (float) z + rz, dropStack);
                 final float velocityScale = 0.05F;
                 entityItem.xd = (float) this.random.nextGaussian() * velocityScale;
                 entityItem.yd = (float) this.random.nextGaussian() * velocityScale + 0.2F;
diff --git a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityTrommel.java b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityTrommel.java
index d29136e9a..7a79b911f 100644
--- a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityTrommel.java
+++ b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntityTrommel.java
@@ -457,7 +457,9 @@ public class TileEntityTrommel extends TileEntity
                         i1 = itemstack.stackSize;
                     }
                     itemstack.stackSize -= i1;
-                    EntityItem item = new EntityItem(world, (float) x + f, (float) y + f1, (float) z + f2, new ItemStack(itemstack.itemID, i1, itemstack.getMetadata()));
+                    var dropStack = new ItemStack(itemstack);
+                    dropStack.stackSize = i1;
+                    EntityItem item = new EntityItem(world, (float) x + f, (float) y + f1, (float) z + f2, dropStack);
                     float f3 = 0.05F;
                     item.xd = (float) this.random.nextGaussian() * f3;
                     item.yd = (float) this.random.nextGaussian() * f3 + 0.2F;
diff --git a/game/core/src/main/java/net/minecraft/core/block/motion/CarriedBlock.java b/game/core/src/main/java/net/minecraft/core/block/motion/CarriedBlock.java
index 66612a564..e442d6bb6 100644
--- a/game/core/src/main/java/net/minecraft/core/block/motion/CarriedBlock.java
+++ b/game/core/src/main/java/net/minecraft/core/block/motion/CarriedBlock.java
@@ -14,6 +14,7 @@ import net.minecraft.core.util.helper.Side;
 import net.minecraft.core.world.ICarriable;
 import net.minecraft.core.world.World;
 
+import net.minecraft.core.world.pos.TilePos;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
@@ -59,19 +60,19 @@ public class CarriedBlock implements ICarriable, ICarriedBlock {
         if (entity != null) {
             return entity.tryPlace(world, holder, blockX, blockY, blockZ, side, xPlaced, yPlaced);
         } else {
-            int x = blockX + side.offsetX();
-            int y = blockY + side.offsetY();
-            int z = blockZ + side.offsetZ();
+            final TilePos blockPosCopy = new TilePos(blockX, blockY, blockZ);
+            final Block<?> directBlock = world.getBlockType(blockPosCopy);
+            if (!directBlock.hasTag(BlockTags.PLACE_OVERWRITES)) blockPosCopy.add(side);
+            // directBlock is irrelevant past this point and blockPosCopy is properly mutated
 
-            Block<?> currentBlock = world.getBlock(x, y, z);
-            if (currentBlock != null && !currentBlock.hasTag(BlockTags.PLACE_OVERWRITES)) return false;
-            world.setBlockAndMetadata(x, y, z, blockId, metadata);
+            final Block<?> placedBlock = Blocks.getBlock(blockId);
+            if (!world.canBlockIdBePlacedAt(placedBlock.id(), blockPosCopy, false, Side.NONE)) return false;
 
-            Block<?> b = world.getBlock(x, y, z);
-            if (b != null && holder instanceof Mob) {
-                b.onBlockPlacedByMob(world, x, y, z, side, (Mob) holder, xPlaced, yPlaced);
+            world.setBlockTypeData(blockPosCopy, placedBlock, metadata);
+            if (holder instanceof Mob mob) {
+                placedBlock.onPlacedByMob(world, blockPosCopy, side, mob, xPlaced, yPlaced);
             }
-            world.notifyBlockChange(x, y, z, blockId);
+            world.notifyBlockChange(blockPosCopy, placedBlock);
             return true;
         }
     }
diff --git a/game/core/src/main/java/net/minecraft/core/entity/EntityItem.java b/game/core/src/main/java/net/minecraft/core/entity/EntityItem.java
index e4b4dd745..06c2c4f2f 100644
--- a/game/core/src/main/java/net/minecraft/core/entity/EntityItem.java
+++ b/game/core/src/main/java/net/minecraft/core/entity/EntityItem.java
@@ -33,7 +33,7 @@ import org.joml.primitives.AABBd;
 
 public class EntityItem extends Entity
 {
-    public ItemStack item;
+    public @Nullable ItemStack item;
     public int lifetime;
     public int age;
     public @Nullable Direction siphonDirection = null;
@@ -187,7 +187,7 @@ public class EntityItem extends Entity
     @Override
 	protected void burn(int damage)
     {
-        if (!this.item.getItem().hasTag(ItemTags.IS_FIRE_PROOF)) {
+        if (this.item != null && !this.item.getItem().hasTag(ItemTags.IS_FIRE_PROOF)) {
             hurt(null, damage, DamageType.FIRE);
         }
     }
@@ -199,13 +199,14 @@ public class EntityItem extends Entity
     @Override
     public boolean hurt(Entity entity, int i, DamageType type)
     {
-        if (this.item.getItem().hasTag(ItemTags.IS_INDESTRUCTIBLE)) {
-            return false;
-        }
-        else if (type == DamageType.FIRE && this.item.getItem().hasTag(ItemTags.IS_FIRE_PROOF)) {
-            return false;
-        } else if (type == DamageType.BLAST && this.item.getItem().hasTag(ItemTags.IS_BLAST_PROOF)) {
-            return false;
+        if (this.item != null) {
+            if (this.item.getItem().hasTag(ItemTags.IS_INDESTRUCTIBLE)) {
+                return false;
+            } else if (type == DamageType.FIRE && this.item.getItem().hasTag(ItemTags.IS_FIRE_PROOF)) {
+                return false;
+            } else if (type == DamageType.BLAST && this.item.getItem().hasTag(ItemTags.IS_BLAST_PROOF)) {
+                return false;
+            }
         }
         markHurt();
         this.health -= i;
@@ -240,7 +241,7 @@ public class EntityItem extends Entity
 
 	@Override
 	public void playerTouch(Player player) {
-		if(this.world.isClientSide) {
+		if(this.world.isClientSide || this.item == null) {
 			return;
 		}
 		if(this.pickupDelay == 0) {
@@ -322,6 +323,8 @@ public class EntityItem extends Entity
 	}
 
     public void clumpToNearbyStack() {
+        if (this.item == null) return;
+
     	AABBd boundingBox = MathHelper.aabbGrow(this.bb, 0.5, 0.0, 0.5, new AABBd());
     	List<@NotNull EntityItem> entities = this.world.getEntitiesWithinAABB(EntityItem.class, boundingBox);
     	
@@ -332,6 +335,7 @@ public class EntityItem extends Entity
 
             ItemStack thisItemStack = this.item;
     		ItemStack otherItemStack = otherEntity.item;
+            if (otherItemStack == null) continue;
     		
     		if(this.item.canStackWith(otherEntity.item)) {
     			if(thisItemStack.stackSize + otherItemStack.stackSize > 64) {
@@ -423,7 +427,7 @@ public class EntityItem extends Entity
             {
                 ItemStack filterItem = ((TileEntityMeshGold) Objects.requireNonNull(world.getTileEntity(tilePosUnderItem))).filterItem;
                 if(filterItem != null){
-                    if (this.item.getItem() == filterItem.getItem() && this.item.getMetadata() == filterItem.getMetadata()){
+                    if (this.item != null && this.item.getItem() == filterItem.getItem() && this.item.getMetadata() == filterItem.getMetadata()){
                         this.yd -= 0.02D;
                     }
                 }
@@ -485,6 +489,8 @@ public class EntityItem extends Entity
     }
 
     private static void combineItems(EntityItem entityItem1, EntityItem entityItem2) {
+        if (entityItem1.item == null || entityItem2.item == null) return;
+
     	entityItem1.item.stackSize += entityItem2.item.stackSize;
     	entityItem2.item.stackSize = 0;
     	entityItem1.age = Math.min(entityItem1.age, entityItem2.age);
diff --git a/game/core/src/main/java/net/minecraft/core/entity/animal/MobFireflyCluster.java b/game/core/src/main/java/net/minecraft/core/entity/animal/MobFireflyCluster.java
index 325eec7b6..a67cf515c 100644
--- a/game/core/src/main/java/net/minecraft/core/entity/animal/MobFireflyCluster.java
+++ b/game/core/src/main/java/net/minecraft/core/entity/animal/MobFireflyCluster.java
@@ -427,12 +427,13 @@ public class MobFireflyCluster extends MobFlying
 
         int count = this.getFireflyCount();
         if (!acceptPartial && amount > count) return 0;
-        
-        count -= amount;
-        this.setFireflyCount(count);
-        if (count == 0) this.remove();
 
-        return count;
+        final int capturedAmount = Math.min(amount, count);
+        final int newCount = count - capturedAmount;
+        this.setFireflyCount(newCount);
+        if (newCount == 0) this.remove();
+
+        return capturedAmount;
     }
 
     public static class FireflyColor {
diff --git a/game/core/src/main/java/net/minecraft/core/entity/animal/MobWolf.java b/game/core/src/main/java/net/minecraft/core/entity/animal/MobWolf.java
index 0cab7a6ba..822c41ae6 100644
--- a/game/core/src/main/java/net/minecraft/core/entity/animal/MobWolf.java
+++ b/game/core/src/main/java/net/minecraft/core/entity/animal/MobWolf.java
@@ -524,6 +524,8 @@ public class MobWolf
     @Override
     public boolean hurt(Entity attacker, int i, DamageType type) {
         setWolfSitting(false);
+        if (attacker instanceof Player p && !p.getGamemode().hasHostileMobs()) return super.hurt(attacker, i, type);
+
         if (attacker != null && !(attacker instanceof Player) && !(attacker instanceof ProjectileArrow)) {
             i = (i + 1) / 2;
         }
diff --git a/game/core/src/main/java/net/minecraft/core/entity/monster/MobGhast.java b/game/core/src/main/java/net/minecraft/core/entity/monster/MobGhast.java
index fffb18866..37c4b8f6c 100644
--- a/game/core/src/main/java/net/minecraft/core/entity/monster/MobGhast.java
+++ b/game/core/src/main/java/net/minecraft/core/entity/monster/MobGhast.java
@@ -125,7 +125,7 @@ public class MobGhast extends MobFlying implements Enemy {
         if (this.targetedEntity == null || this.aggroCooldown-- <= 0) {
             Entity potentialTarget = this.world.getClosestPlayerToEntity(this, 100.0D);
 
-            if (potentialTarget instanceof Player p && p.gamemode != Gamemodes.CREATIVE && p.gamemode != Gamemodes.SPECTATOR) {
+            if (potentialTarget instanceof Player p && p.getGamemode().hasHostileMobs()) {
                 this.targetedEntity = potentialTarget;
             }
 
@@ -211,7 +211,7 @@ public class MobGhast extends MobFlying implements Enemy {
             if (this.passenger == attacker || this.vehicle == attacker) {
                 return true;
             }
-            if (attacker != this) {
+            if (attacker != this && !(attacker instanceof Player p && !p.getGamemode().hasHostileMobs())) {
                 this.targetedEntity = attacker;
                 this.aggroCooldown = 60;
             }
diff --git a/game/core/src/main/java/net/minecraft/core/entity/monster/MobZombiePig.java b/game/core/src/main/java/net/minecraft/core/entity/monster/MobZombiePig.java
index 795fc27fd..70764d6d1 100644
--- a/game/core/src/main/java/net/minecraft/core/entity/monster/MobZombiePig.java
+++ b/game/core/src/main/java/net/minecraft/core/entity/monster/MobZombiePig.java
@@ -101,7 +101,7 @@ public class MobZombiePig extends MobZombie {
         if (type == DamageType.FIRE) {
             return false;
         }
-        if (attacker instanceof Player) {
+        if (attacker instanceof Player p && p.getGamemode().hasHostileMobs()) {
             List<Entity> list = this.world.getEntitiesWithinAABBExcludingEntity(this, MathHelper.aabbGrow(this.bb, 32D, 32D, 32D, new AABBd()));
             for (int j = 0; j < list.size(); j++) {
                 Entity e = list.get(j);
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 d77fea5ab..3efdef043 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
@@ -1754,12 +1754,12 @@ public abstract class Player
     }
 
     public void pickBlock(int x, int y, int z, boolean pickFully) {
+        if (this.heldObject != null) return;
+
         Block<?> block = this.world.getBlock(x, y, z);
         int meta = this.world.getBlockMetadata(x, y, z);
 
-        if (block == null) {
-            return;
-        }
+        if (block == Blocks.AIR) return;
 
         TileEntity tileEntity = this.world.getTileEntity(x, y, z);
         ItemStack[] result = block.getBreakResult(this.world, EnumDropCause.PICK_BLOCK, x, y, z, meta, tileEntity);
diff --git a/game/core/src/main/java/net/minecraft/core/world/World.java b/game/core/src/main/java/net/minecraft/core/world/World.java
index 8c866a257..6386362a1 100644
--- a/game/core/src/main/java/net/minecraft/core/world/World.java
+++ b/game/core/src/main/java/net/minecraft/core/world/World.java
@@ -1523,7 +1523,7 @@ public abstract class World implements MutableWorldSource {
         int y = this.getHeightBlocks() - 1;
         x &= (Chunk.CHUNK_SIZE_X - 1);
         z &= (Chunk.CHUNK_SIZE_Z - 1);
-        while (y > 0) {
+        while (y >= 0) {
             final int id = chunk.getBlockID(x, y, z);
             final Material material = id != 0 ? Blocks.blocksList[id].getMaterial() : Materials.AIR;
             if (!material.blocksMotion() && !material.isLiquid()) {
@@ -1978,7 +1978,7 @@ public abstract class World implements MutableWorldSource {
 
         if (block == Blocks.FIRE || block == Blocks.FIRE_COLD) {
             this.playBlockEvent(player, offsetPos, LevelListener.EVENT_FIRE_EXTINGUISH, 0);
-            this.setBlockTypeNotify(tilePos, Blocks.AIR);
+            this.setBlockTypeNotify(offsetPos, Blocks.AIR);
         }
     }
 
@@ -2468,6 +2468,7 @@ public abstract class World implements MutableWorldSource {
     }
 
     public boolean canBlockIdBePlacedAt(final int id, final @NotNull TilePosc tilePos, final boolean ignoreCollision, final @NotNull Side side) {
+        // TODO Rework this method to accept Block and make side nullable
         if (tilePos.y() < 0 || tilePos.y() >= this.getHeightBlocks()) {
             return false;
         }
diff --git a/game/server/src/main/java/net/minecraft/server/net/command/commands/CommandUnban.java b/game/server/src/main/java/net/minecraft/server/net/command/commands/CommandUnban.java
index 07a382de5..175ffc8cf 100644
--- a/game/server/src/main/java/net/minecraft/server/net/command/commands/CommandUnban.java
+++ b/game/server/src/main/java/net/minecraft/server/net/command/commands/CommandUnban.java
@@ -33,7 +33,7 @@ public class CommandUnban implements CommandManager.CommandRegistry {
 
                             String nameToUnban = c.getArgument("name", String.class);
                             UUIDHelper.runConversionAction(nameToUnban, (uuid) -> {
-                                server.playerList.banPlayer(uuid);
+                                server.playerList.pardonPlayer(uuid);
                                 c.getSource().sendTranslatableMessage("command.commands.unban.username.success", nameToUnban);
                             }, (username) -> {
                                 c.getSource().sendTranslatableMessage("command.commands.unban.username.fail.wrong_name", nameToUnban);
