diff --git a/src/common/resources/lang/en_US/options.lang b/src/common/resources/lang/en_US/options.lang
index 0f60ee1ae..f41aabcf1 100644
--- a/src/common/resources/lang/en_US/options.lang
+++ b/src/common/resources/lang/en_US/options.lang
@@ -32,6 +32,7 @@ options.enableVsync=Enable VSync
 options.customFpsLimit.unlimited=Unlimited
 options.customFpsLimit=FPS Limit
 options.difficulty=Difficulty
+options.difficulty.locked=Locked
 options.difficulty.peaceful=Peaceful
 options.difficulty.easy=Easy
 options.difficulty.normal=Normal
diff --git a/src/main/java/net/minecraft/client/Minecraft.java b/src/main/java/net/minecraft/client/Minecraft.java
index 6ad3ac2e5..b8ad20dc3 100644
--- a/src/main/java/net/minecraft/client/Minecraft.java
+++ b/src/main/java/net/minecraft/client/Minecraft.java
@@ -1,5 +1,8 @@
 package net.minecraft.client;
 
+import java.awt.*;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -11,12 +14,14 @@ import java.util.Random;
 import com.mojang.nbt.CompoundTag;
 import net.minecraft.client.entity.ClientSkinVariantList;
 import net.minecraft.client.gui.toasts.GuiHudToasts;
+import net.minecraft.client.option.enums.Difficulty;
 import net.minecraft.core.achievement.Achievements;
 import net.minecraft.core.world.SpawnerMobs;
 import net.minecraft.core.world.save.*;
 import net.minecraft.core.world.type.WorldType;
 import net.minecraft.core.world.type.WorldTypeGroups;
 import org.lwjgl.LWJGLException;
+import org.lwjgl.Version;
 import org.lwjgl.input.Keyboard;
 import org.lwjgl.input.Mouse;
 import org.lwjgl.opengl.Display;
@@ -136,6 +141,7 @@ import net.minecraft.core.world.chunk.provider.IChunkProvider;
 import net.minecraft.core.world.save.mcregion.SaveFormat19134;
 import org.lwjgl.opengl.GL11;
 
+import javax.swing.*;
 import java.net.HttpURLConnection;
 import java.net.URL;
 
@@ -359,6 +365,33 @@ public class Minecraft
     
     public void startGame() throws LWJGLException
     {
+        System.out.println("LWJGL version = " + org.lwjgl.Sys.getVersion());
+        if (Integer.parseInt(org.lwjgl.Sys.getVersion().substring(0, 1)) < 3) {
+            JOptionPane.showMessageDialog(null,
+                new Object[] {
+                    "Better than Adventure! requires LWJGL 3 to run.",
+                    "If you're using MultiMC or Prism Launcher to run BTA!,",
+                    "please review the installation guide at",
+                    new JLabel("<html><a href=\"\">betterthanadventure.net</a>.</html>") {
+                        {
+                            setCursor(new Cursor(Cursor.HAND_CURSOR));
+                            addMouseListener(new MouseAdapter() {
+                                @Override
+                                public void mouseClicked(MouseEvent e) {
+                                    try {
+                                        Desktop.getDesktop().browse(new URL("https://www.betterthanadventure.net/installation-guide/").toURI());
+                                    } catch (Exception ex) {
+                                        ex.printStackTrace();
+                                    }
+                                }
+                            });
+                        }
+                    }
+                }
+                , "LWJGL Version Error", JOptionPane.ERROR_MESSAGE);
+            System.exit(1);
+        }
+
 		mcDataDir = getMinecraftDir();
 
         // Manually initialize blocks and items
@@ -1610,11 +1643,6 @@ public class Minecraft
                 thePlayer.setFlightSmoothness(gameSettings.flightSmoothness.value);
                 thePlayer.turnWithVehicle = gameSettings.turnWithVehicle.value;
             }
-            theWorld.difficultySetting = gameSettings.difficulty.value.id();
-            if(theWorld.isClientSide)
-            {
-                theWorld.difficultySetting = 3;
-            }
             if(!isGamePaused)
             {
                 worldRenderer.updateRenderer();
@@ -2149,11 +2177,13 @@ public class Minecraft
         {
             theWorld.saveWorldIndirectly(loadingScreen);
             IChunkLoader loader = theWorld.saveHandler.getChunkLoader(theWorld.dimension);
-            while (loader.isSaving()) {
-                try {
-                    Thread.sleep(1);
-                } catch (InterruptedException e) {
-                    throw new RuntimeException(e);
+            if (loader != null) {
+                while (loader.isSaving()) {
+                    try {
+                        Thread.sleep(1);
+                    } catch (InterruptedException e) {
+                        throw new RuntimeException(e);
+                    }
                 }
             }
             theWorld.getChunkProvider().unloadAllChunks();
diff --git a/src/main/java/net/minecraft/client/gui/options/components/DifficultyOptionComponent.java b/src/main/java/net/minecraft/client/gui/options/components/DifficultyOptionComponent.java
new file mode 100644
index 000000000..b592a19c0
--- /dev/null
+++ b/src/main/java/net/minecraft/client/gui/options/components/DifficultyOptionComponent.java
@@ -0,0 +1,89 @@
+package net.minecraft.client.gui.options.components;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.GuiSliderInteger;
+import net.minecraft.client.option.EnumOption;
+import net.minecraft.client.option.enums.Difficulty;
+import net.minecraft.core.lang.I18n;
+
+public class DifficultyOptionComponent extends ButtonComponent {
+
+    protected final EnumOption<Difficulty> option;
+    protected final GuiSliderInteger slider;
+
+    public DifficultyOptionComponent(EnumOption<Difficulty> difficultyOption) {
+        super("options." + difficultyOption.name);
+        option = difficultyOption;
+        this.slider = new GuiSliderInteger(0, 0, 0, 150, 20, option);
+    }
+
+    @Override
+    protected void buttonClicked(int mouseButton, int x, int y, int width, int height, int relativeMouseX, int relativeMouseY) {
+        this.slider.mouseClicked(mc, this.slider.xPosition + relativeMouseX, this.slider.yPosition + relativeMouseY);
+        this.slider.displayString = getDisplayString();
+        this.option.onUpdate();
+    }
+
+    @Override
+    protected void buttonDragged(int x, int y, int width, int height, int relativeMouseX, int relativeMouseY) {
+        this.slider.displayString = getDisplayString();
+        this.option.onUpdate();
+    }
+
+    @Override
+    protected void buttonReleased(int mouseButton, int x, int y, int width, int height, int relativeMouseX, int relativeMouseY) {
+        this.slider.mouseReleased(this.slider.xPosition + relativeMouseX, this.slider.yPosition + relativeMouseY);
+        this.slider.displayString = getDisplayString();
+        this.option.onUpdate();
+    }
+
+    @Override
+    protected void renderButton(int x, int y, int relativeButtonX, int relativeButtonY, int buttonWidth, int buttonHeight, int relativeMouseX, int relativeMouseY)
+    {
+        slider.enabled = canSetDifficulty();
+        super.renderButton(x, y, relativeButtonX, relativeButtonY, buttonWidth, buttonHeight, relativeMouseX, relativeMouseY);
+
+        this.slider.xPosition = x + relativeButtonX;
+        this.slider.yPosition = y + relativeButtonY;
+        this.slider.width = buttonWidth;
+        this.slider.height = buttonHeight;
+
+        this.slider.drawButton(mc, x + relativeMouseX, y + relativeMouseY);
+    }
+    @Override
+    public void init(Minecraft mc){
+        if (!canSetDifficulty()) {
+            slider.enabled = false;
+            slider.sliderValue = Difficulty.NORMAL.id();
+            this.slider.displayString = getDisplayString();
+            return;
+        }
+        slider.enabled = true;
+        option.set(mc.theWorld.getDifficulty());
+        slider.sliderValue = option.value.id();
+        this.slider.displayString = getDisplayString();
+    }
+
+    @Override
+    public void resetValue() {
+        if (canSetDifficulty()) {
+            mc.theWorld.setDifficulty(Difficulty.NORMAL, false);
+        }
+    }
+
+    @Override
+    public boolean isDefault() {
+        boolean canSet = canSetDifficulty();
+        return !canSet || mc.theWorld.getDifficulty() == Difficulty.NORMAL;
+    }
+    protected String getDisplayString() {
+        if (!canSetDifficulty()) {
+            return I18n.getInstance().translateKey("options.difficulty.locked");
+        }
+        return option.getDisplayStringValue();
+    }
+
+    protected boolean canSetDifficulty() {
+        return mc.theWorld != null && !mc.theWorld.getLevelData().getDifficultyLocked() && !mc.theWorld.isClientSide;
+    }
+}
diff --git a/src/main/java/net/minecraft/client/gui/options/data/OptionsPages.java b/src/main/java/net/minecraft/client/gui/options/data/OptionsPages.java
index c7ded604a..a5bd35a0e 100644
--- a/src/main/java/net/minecraft/client/gui/options/data/OptionsPages.java
+++ b/src/main/java/net/minecraft/client/gui/options/data/OptionsPages.java
@@ -11,6 +11,7 @@ import net.minecraft.client.gui.achievements.GuiAchievements;
 import net.minecraft.client.gui.modelviewer.GuiModelViewer;
 import net.minecraft.client.gui.options.components.AvailableTexturePackListComponent;
 import net.minecraft.client.gui.options.components.BooleanOptionComponent;
+import net.minecraft.client.gui.options.components.DifficultyOptionComponent;
 import net.minecraft.client.gui.options.components.FloatOptionComponent;
 import net.minecraft.client.gui.options.components.FramerateOptionComponent;
 import net.minecraft.client.gui.options.components.IntegerOptionComponent;
@@ -43,7 +44,7 @@ public abstract class OptionsPages
     public static final OptionsPage GENERAL = register(new OptionsPage("gui.options.page.general.title", new ItemStack(Blocks.GRASS_RETRO))
         .withComponent(new OptionsCategory("gui.options.page.general.category.gameplay")
             .withComponent(new FloatOptionComponent(mc.gameSettings.FoV))
-            .withComponent(new ToggleableOptionComponent<>(mc.gameSettings.difficulty))
+            .withComponent(new DifficultyOptionComponent(mc.gameSettings.difficulty))
             .withComponent(new ToggleableOptionComponent<>(mc.gameSettings.placementMode))
             .withComponent(new BooleanOptionComponent(mc.gameSettings.autoHit))
             .withComponent(new BooleanOptionComponent(mc.gameSettings.easyBridge))
diff --git a/src/main/java/net/minecraft/client/option/GameSettings.java b/src/main/java/net/minecraft/client/option/GameSettings.java
index a1cf6e215..088b94af0 100644
--- a/src/main/java/net/minecraft/client/option/GameSettings.java
+++ b/src/main/java/net/minecraft/client/option/GameSettings.java
@@ -106,7 +106,7 @@ public class GameSettings {
 	public BooleanOption discordRichPresence = new BooleanOption(this, "discordRichPresence", false);
 	public ImmersiveModeOption immersiveMode = new ImmersiveModeOption(this);
 	public ColorOption guiBackgroundColor = new ColorOption(this, "guiBackgroundColor", new Color().setARGB(0xc0101010));
-	public EnumOption<Difficulty> difficulty = new EnumOption<>(this, "difficulty", Difficulty.class, Difficulty.NORMAL);
+	public EnumOption<Difficulty> difficulty = new EnumOption<>(this, "difficulty", Difficulty.class, Difficulty.NORMAL); // Stub for local difficulty setting
 	public IntegerOption thirdPersonView = new IntegerOption(this, "thirdPersonView", 0);
 	public EnumOption<RenderDistance> renderDistance = new EnumOption<>(this, "renderDistance", RenderDistance.class, RenderDistance.FAR);
 	public BooleanOption enableVsync = new BooleanOption(this, "enableVsync", false);
@@ -414,6 +414,11 @@ public class GameSettings {
 	}
 
 	public void optionChanged(Option<?> option) {
+		if (option == difficulty) {
+			if (mc.theWorld != null) {
+				mc.theWorld.setDifficulty(difficulty.value, false);
+			}
+		}
 		if(option == enableVsync) Display.setVSyncEnabled(enableVsync.value);
 		if(option == fancyGraphics) mc.renderGlobal.loadRenderers();
 		if(option == ambientOcclusion) mc.renderGlobal.loadRenderers();
diff --git a/src/main/java/net/minecraft/client/option/Option.java b/src/main/java/net/minecraft/client/option/Option.java
index 6b197e4b7..bc82d5762 100644
--- a/src/main/java/net/minecraft/client/option/Option.java
+++ b/src/main/java/net/minecraft/client/option/Option.java
@@ -24,16 +24,16 @@ public abstract class Option<E> {
 	public abstract void parse(String string);
 
 	public void onUpdate() {
-		gameSettings.optionChanged(this);
-	}
+        gameSettings.optionChanged(this);
+    }
 
 	public String getDisplayString() {
-		return gameSettings.getDisplayString(this);
-	}
+        return gameSettings.getDisplayString(this);
+    }
 
 	public String getDisplayStringValue() {
-		return gameSettings.getDisplayString(this);
-	}
+        return gameSettings.getDisplayString(this);
+    }
 
 	public String getDisplayStringName() {
 		return I18n.getInstance().translateKey("options." + this.name);
diff --git a/src/main/java/net/minecraft/client/option/VolumeOption.java b/src/main/java/net/minecraft/client/option/VolumeOption.java
index 4f0974e19..c49b1d156 100644
--- a/src/main/java/net/minecraft/client/option/VolumeOption.java
+++ b/src/main/java/net/minecraft/client/option/VolumeOption.java
@@ -3,12 +3,13 @@ package net.minecraft.client.option;
 import net.minecraft.client.sound.SoundCategoryHelper;
 import net.minecraft.client.sound.SoundEngine;
 import net.minecraft.core.sound.SoundCategory;
+import org.jetbrains.annotations.NotNull;
 import paulscode.sound.SoundSystem;
 
 public class VolumeOption extends FloatOption{
 	public final SoundCategory soundCategory;
 
-	public VolumeOption(GameSettings gameSettings, String name, float value, SoundCategory soundCategory) {
+	public VolumeOption(@NotNull GameSettings gameSettings, String name, float value, SoundCategory soundCategory) {
 		super(gameSettings, name, value);
 		this.soundCategory = soundCategory;
 	}
diff --git a/src/main/java/net/minecraft/client/option/enums/Difficulty.java b/src/main/java/net/minecraft/client/option/enums/Difficulty.java
index 19dc769e3..7330795f4 100644
--- a/src/main/java/net/minecraft/client/option/enums/Difficulty.java
+++ b/src/main/java/net/minecraft/client/option/enums/Difficulty.java
@@ -1,21 +1,28 @@
 package net.minecraft.client.option.enums;
 
+import org.jetbrains.annotations.NotNull;
+
 public enum Difficulty{
+	//Any difficulty where mobs can spawn must have an id greater than 0, or there will be divide by zero issues
+	PEACEFUL(0, "peaceful"), EASY(1, "easy"), NORMAL(2, "normal"), HARD(3, "hard");
 	
-	PEACEFUL(0), EASY(1), NORMAL(2), HARD(3);
-	
-	private int id;
-	
-	private Difficulty(int id) {
+	private final int id;
+	private final @NotNull String key;
+
+	Difficulty(int id, @NotNull String key) {
 		this.id = id;
+		this.key = key;
 	}
 	
-	public boolean canMobsSpawn() {
+	public boolean canHostileMobsSpawn() {
 		return this != PEACEFUL;
 	}
 	
 	public int id() {
 		return id;
 	}
-	
+
+	public String getTranslationKey() {
+		return "difficulty." + key;
+	}
 }
diff --git a/src/main/java/net/minecraft/core/block/entity/MobSpawnerBlockEntity.java b/src/main/java/net/minecraft/core/block/entity/MobSpawnerBlockEntity.java
index 7780f22de..13826753b 100644
--- a/src/main/java/net/minecraft/core/block/entity/MobSpawnerBlockEntity.java
+++ b/src/main/java/net/minecraft/core/block/entity/MobSpawnerBlockEntity.java
@@ -69,7 +69,7 @@ public class MobSpawnerBlockEntity extends BlockEntity
 
         if(!worldObj.isClientSide)
         {
-        	if(worldObj.difficultySetting == 0) {
+        	if(!worldObj.getDifficulty().canHostileMobsSpawn()) {
         		return;
         	}
         	if(mobId == null || mobId.equalsIgnoreCase("none")) {
diff --git a/src/main/java/net/minecraft/core/entity/Entity.java b/src/main/java/net/minecraft/core/entity/Entity.java
index 2db366c6d..697ebb05d 100644
--- a/src/main/java/net/minecraft/core/entity/Entity.java
+++ b/src/main/java/net/minecraft/core/entity/Entity.java
@@ -503,7 +503,7 @@ public abstract class Entity
             zd = oldZd;
             AABB axisalignedbb1 = bb.copy();
             bb.setBB(bbCopy);
-            List<AABB> list1 = world.getCubes(this, bb.expand(xd, yd, zd));
+            List<AABB> list1 = world.getCubes(this, bb.expand(xd, 0.0, zd));
             for (AABB aabb : list1) {
                 yd = aabb.clipYCollide(bb, yd);
             }
diff --git a/src/main/java/net/minecraft/core/entity/LightningEntity.java b/src/main/java/net/minecraft/core/entity/LightningEntity.java
index 11df05144..55cfd57ef 100644
--- a/src/main/java/net/minecraft/core/entity/LightningEntity.java
+++ b/src/main/java/net/minecraft/core/entity/LightningEntity.java
@@ -1,5 +1,6 @@
 package net.minecraft.core.entity;
 
+import net.minecraft.client.option.enums.Difficulty;
 import net.minecraft.core.block.Blocks;
 import net.minecraft.core.sound.SoundCategory;
 import net.minecraft.core.util.phys.AABB;
@@ -87,7 +88,7 @@ public class LightningEntity extends Entity
     @Override
     public void spawnInit()
     {
-        if(world.difficultySetting >= 2 && world.areBlocksLoaded(MathHelper.floor(x), MathHelper.floor(y), MathHelper.floor(z), 10))
+        if(world.getDifficulty().id() >= Difficulty.NORMAL.id() && world.areBlocksLoaded(MathHelper.floor(x), MathHelper.floor(y), MathHelper.floor(z), 10))
         {
             int i = MathHelper.floor(x);
             int k = MathHelper.floor(y);
diff --git a/src/main/java/net/minecraft/core/entity/monster/GhastMob.java b/src/main/java/net/minecraft/core/entity/monster/GhastMob.java
index 90fee0c59..8b06efdf9 100644
--- a/src/main/java/net/minecraft/core/entity/monster/GhastMob.java
+++ b/src/main/java/net/minecraft/core/entity/monster/GhastMob.java
@@ -110,7 +110,7 @@ public class GhastMob extends FlyingMob
     @Override
 	protected void updateAI()
     {
-        if(!world.isClientSide && world.difficultySetting == 0)
+        if(!world.isClientSide && !world.getDifficulty().canHostileMobsSpawn())
         {
             remove();
         }
@@ -278,7 +278,7 @@ public class GhastMob extends FlyingMob
     @Override
 	public boolean canSpawnHere()
     {
-        return random.nextInt(20) == 0 && super.canSpawnHere() && world.difficultySetting > 0;
+        return random.nextInt(20) == 0 && super.canSpawnHere() && world.getDifficulty().canHostileMobsSpawn();
     }
 
     @Override
diff --git a/src/main/java/net/minecraft/core/entity/monster/Monster.java b/src/main/java/net/minecraft/core/entity/monster/Monster.java
index d36825af4..66e6525f9 100644
--- a/src/main/java/net/minecraft/core/entity/monster/Monster.java
+++ b/src/main/java/net/minecraft/core/entity/monster/Monster.java
@@ -67,7 +67,7 @@ public abstract class Monster extends PathfinderMob
 	public void tick()
     {
         super.tick();
-        if(!world.isClientSide && world.difficultySetting == 0)
+        if(!world.isClientSide && !world.getDifficulty().canHostileMobsSpawn())
         {
             remove();
         }
diff --git a/src/main/java/net/minecraft/core/entity/monster/ScorpionMob.java b/src/main/java/net/minecraft/core/entity/monster/ScorpionMob.java
index 175288368..2589bf1a2 100644
--- a/src/main/java/net/minecraft/core/entity/monster/ScorpionMob.java
+++ b/src/main/java/net/minecraft/core/entity/monster/ScorpionMob.java
@@ -37,7 +37,7 @@ public class ScorpionMob extends Monster
 	public void spawnInit()
     {
         super.defineSynchedData();
-        if(world.difficultySetting != 0 && random.nextInt(100 / world.difficultySetting) == 0) {
+        if(world.getDifficulty().canHostileMobsSpawn() && random.nextInt(100 / world.getDifficulty().id()) == 0) {
             SkeletonMob entityskeleton = new SkeletonMob(world);
             entityskeleton.moveTo(x, y, z, yRot, 0.0F);
             world.entityJoinedWorld(entityskeleton);
diff --git a/src/main/java/net/minecraft/core/entity/monster/SlimeMob.java b/src/main/java/net/minecraft/core/entity/monster/SlimeMob.java
index efa4bb154..da9858725 100644
--- a/src/main/java/net/minecraft/core/entity/monster/SlimeMob.java
+++ b/src/main/java/net/minecraft/core/entity/monster/SlimeMob.java
@@ -123,7 +123,7 @@ public class SlimeMob extends Mob
             }
             squish = -0.5F;
         }
-        if (!world.isClientSide && world.difficultySetting == 0)
+        if (!world.isClientSide && !world.getDifficulty().canHostileMobsSpawn())
         {
         	remove();
         }
@@ -232,7 +232,7 @@ public class SlimeMob extends Mob
             return false;
         if(chunk.getChunkRandom(0x3AD8025FL).nextInt(10) != 0)
             return false;
-        if(world.difficultySetting == 0)
+        if(!world.getDifficulty().canHostileMobsSpawn())
             return false;
         return super.canSpawnHere();
     }
diff --git a/src/main/java/net/minecraft/core/entity/monster/SpiderMob.java b/src/main/java/net/minecraft/core/entity/monster/SpiderMob.java
index 85cafede5..069672ce7 100644
--- a/src/main/java/net/minecraft/core/entity/monster/SpiderMob.java
+++ b/src/main/java/net/minecraft/core/entity/monster/SpiderMob.java
@@ -37,11 +37,11 @@ public class SpiderMob extends Monster
 	public void spawnInit()
     {
         super.defineSynchedData();
-        if(world.difficultySetting != 0 && random.nextInt(100 / world.difficultySetting) == 0) {
-            SkeletonMob entityskeleton = new SkeletonMob(world);
-            entityskeleton.moveTo(x, y, z, yRot, 0.0F);
-            world.entityJoinedWorld(entityskeleton);
-            entityskeleton.startRiding(this);
+        if (world.getDifficulty().canHostileMobsSpawn() && random.nextInt(100 / world.getDifficulty().id()) == 0) {
+            SkeletonMob skeletonMob = new SkeletonMob(world);
+            skeletonMob.moveTo(x, y, z, yRot, 0.0F);
+            world.entityJoinedWorld(skeletonMob);
+            skeletonMob.startRiding(this);
         }
     }
 
diff --git a/src/main/java/net/minecraft/core/entity/monster/ZombiePigMob.java b/src/main/java/net/minecraft/core/entity/monster/ZombiePigMob.java
index 92627d569..c761c4cbe 100644
--- a/src/main/java/net/minecraft/core/entity/monster/ZombiePigMob.java
+++ b/src/main/java/net/minecraft/core/entity/monster/ZombiePigMob.java
@@ -68,7 +68,7 @@ public class ZombiePigMob extends ZombieMob
     	if(world.getBlockId(x, y, z) != 0) return false;
     	int blockLight = world.getSavedLightValue(LightLayer.Block, x, y, z);
     	if(blockLight > 7) return false;
-        return world.difficultySetting > 0 && world.checkIfAABBIsClear(bb) && world.getCubes(this, bb).size() == 0 && !world.getIsAnyLiquid(bb);
+        return world.getDifficulty().canHostileMobsSpawn() && world.checkIfAABBIsClear(bb) && world.getCubes(this, bb).size() == 0 && !world.getIsAnyLiquid(bb);
     }
 
     @Override
diff --git a/src/main/java/net/minecraft/core/entity/player/Player.java b/src/main/java/net/minecraft/core/entity/player/Player.java
index 0abe40af6..26f1b0456 100644
--- a/src/main/java/net/minecraft/core/entity/player/Player.java
+++ b/src/main/java/net/minecraft/core/entity/player/Player.java
@@ -2,6 +2,7 @@ package net.minecraft.core.entity.player;
 
 import com.mojang.nbt.CompoundTag;
 import com.mojang.nbt.ListTag;
+import net.minecraft.client.option.enums.Difficulty;
 import net.minecraft.core.achievement.Achievements;
 import net.minecraft.core.achievement.stat.Stat;
 import net.minecraft.core.achievement.stat.StatList;
@@ -302,7 +303,7 @@ public abstract class Player extends Mob
     @Override
     public void onLivingUpdate()
     {
-        if(world.difficultySetting == 0 && getHealth() < getMaxHealth() && (tickCount % 20) * 12 == 0)
+        if(world.getDifficulty() == Difficulty.PEACEFUL && getHealth() < getMaxHealth() && (tickCount % 20) * 12 == 0)
         {
             heal(1);
         }
@@ -828,31 +829,33 @@ public abstract class Player extends Mob
         }
         if((attacker instanceof Monster) || (attacker instanceof ArrowProjectile))
         {
-            if(world.difficultySetting == 0)
-            {
-                damage = 0;
-            }
-            if(world.difficultySetting == 1)
-            {
-                damage = damage / 3 + 1;
-            }
-            if(world.difficultySetting == 3)
-            {
-                damage = (damage * 3) / 2;
+            switch (world.getDifficulty()) {
+                case PEACEFUL: {
+                    damage = 0;
+                    break;
+                }
+                case EASY: {
+                    damage = damage / 3 + 1;
+                    break;
+                }
+                case HARD: {
+                    damage = (damage * 3) / 2;
+                    break;
+                }
             }
         }
-        Object obj = attacker;
+        Entity blamedAttacker = attacker;
         if (damage == 0 && !(attacker instanceof SnowmanMob))
         {
             return false;
         }
-        if((obj instanceof ArrowProjectile) && ((ArrowProjectile)obj).owner != null)
+        if((blamedAttacker instanceof ArrowProjectile) && ((ArrowProjectile)blamedAttacker).owner != null)
         {
-            obj = ((ArrowProjectile)obj).owner;
+            blamedAttacker = ((ArrowProjectile)blamedAttacker).owner;
         }
-        if(obj instanceof Mob)
+        if(blamedAttacker instanceof Mob)
         {
-            alertWolves((Mob)obj, false);
+            alertWolves((Mob)blamedAttacker, false);
         }
         addStat(StatList.damageTakenStat, damage);
         if (attacker != null) {
diff --git a/src/main/java/net/minecraft/core/net/command/servercommands/DifficultyCommand.java b/src/main/java/net/minecraft/core/net/command/servercommands/DifficultyCommand.java
index 323fd18be..10a0b7dfe 100644
--- a/src/main/java/net/minecraft/core/net/command/servercommands/DifficultyCommand.java
+++ b/src/main/java/net/minecraft/core/net/command/servercommands/DifficultyCommand.java
@@ -18,7 +18,7 @@ public class DifficultyCommand extends ServerCommand{
 	@Override
 	public boolean execute(CommandHandler handler, CommandSender sender, String[] args) {
 		if(args.length == 0) {
-			sender.sendMessage("The difficulty is "+diffString[handler.asServer().getWorld(0).difficultySetting]);
+			sender.sendMessage("The difficulty is "+diffString[handler.asServer().getWorld(0).getDifficulty().id()]);
 			return true;
 		}
 		if(args.length == 1) {
@@ -27,7 +27,7 @@ public class DifficultyCommand extends ServerCommand{
 			if(diff < 0 || diff > 3) throw new CommandError("Invalid Difficulty!");
 			
 			for(World world : server.dimensionWorlds) {
-				world.difficultySetting = diff;
+				world.setDifficulty(diff, true);
 			}
 			server.difficulty = diff;
 			
diff --git a/src/main/java/net/minecraft/core/world/SpawnerMobs.java b/src/main/java/net/minecraft/core/world/SpawnerMobs.java
index e2f6065d4..e76a443a7 100644
--- a/src/main/java/net/minecraft/core/world/SpawnerMobs.java
+++ b/src/main/java/net/minecraft/core/world/SpawnerMobs.java
@@ -321,7 +321,7 @@ public final class SpawnerMobs
         }
 
         public boolean canHostileSpawn(World world) {
-            return allowHostile && world.difficultySetting > 0;
+            return allowHostile && world.getDifficulty().canHostileMobsSpawn();
         }
 
         public void setHostileSpawning(boolean flag) {
diff --git a/src/main/java/net/minecraft/core/world/World.java b/src/main/java/net/minecraft/core/world/World.java
index 29ae30407..5d9430b20 100644
--- a/src/main/java/net/minecraft/core/world/World.java
+++ b/src/main/java/net/minecraft/core/world/World.java
@@ -14,6 +14,7 @@ import java.util.TreeSet;
 
 import com.mojang.nbt.CompoundTag;
 
+import net.minecraft.client.option.enums.Difficulty;
 import net.minecraft.client.render.LightmapHelper;
 import net.minecraft.client.render.camera.ICamera;
 import net.minecraft.client.render.colorizer.Colorizers;
@@ -99,7 +100,6 @@ public class World implements WorldSource {
     public boolean editingBlocks = false;
     private long lockTimestamp = System.currentTimeMillis();
     //protected int autosavePeriod = 40;
-    public int difficultySetting;
     public Random rand = new Random();
     public boolean isNewWorld = false;
     public final Dimension dimension;
@@ -2462,12 +2462,6 @@ public class World implements WorldSource {
         }
     }
 
-    public void setAllowedMobSpawns(boolean spawnHostile, boolean spawnPassive)
-    {
-        spawningConfig.setPassiveSpawning(spawnPassive);
-        spawningConfig.setHostileSpawning(spawnHostile);
-    }
-
     public void allChanged(boolean lightChanged, boolean seasonChanged) {
 		for(int i= 0; i < listeners.size(); i++) {
             listeners.get(i).allChanged(lightChanged, seasonChanged);
@@ -3052,6 +3046,17 @@ public class World implements WorldSource {
         return levelData.getWorldTime();
     }
 
+    public Difficulty getDifficulty() {
+        return levelData.getDifficulty();
+    }
+
+    public void setDifficulty(Difficulty difficulty, boolean overrideLock) {
+        levelData.setDifficulty(difficulty, overrideLock);
+    }
+    public void setDifficulty(int difficulty, boolean overrideLock) {
+        levelData.setDifficulty(difficulty, overrideLock);
+    }
+
     public ChunkCoordinates getSpawnPoint()
     {
         return new ChunkCoordinates(levelData.getSpawnX(), levelData.getSpawnY(), levelData.getSpawnZ());
diff --git a/src/main/java/net/minecraft/core/world/save/LevelData.java b/src/main/java/net/minecraft/core/world/save/LevelData.java
index e8b96c26b..d4f38340b 100644
--- a/src/main/java/net/minecraft/core/world/save/LevelData.java
+++ b/src/main/java/net/minecraft/core/world/save/LevelData.java
@@ -1,9 +1,12 @@
 package net.minecraft.core.world.save;
 
+import net.minecraft.client.option.enums.Difficulty;
 import net.minecraft.core.data.gamerule.GameRuleCollection;
 import net.minecraft.core.entity.player.Player;
 import com.mojang.nbt.CompoundTag;
 import com.mojang.nbt.NbtIo;
+import net.minecraft.core.world.Dimension;
+import org.jetbrains.annotations.NotNull;
 
 import java.io.File;
 import java.io.IOException;
@@ -21,14 +24,14 @@ public class LevelData
         readFromCompoundTag(tag);
     }
 
-    public LevelData(long seed, String name)
+    public LevelData(long seed, @NotNull String name)
     {
         randomSeed = seed;
         levelName = name;
         gameRules = new GameRuleCollection();
     }
 
-    public LevelData(long seed, String name, GameRuleCollection gameRules)
+    public LevelData(long seed, @NotNull String name, GameRuleCollection gameRules)
     {
         randomSeed = seed;
         levelName = name;
@@ -51,6 +54,8 @@ public class LevelData
         cheatsEnabled = levelData.cheatsEnabled;
         gamemode = levelData.gamemode;
         gameRules = levelData.gameRules.copy();
+        difficulty = levelData.difficulty;
+        difficultyLocked = levelData.difficultyLocked;
     }
 
     public LevelData(File worldDir)
@@ -130,6 +135,8 @@ public class LevelData
         levelTag.putString("LevelName", levelName);
         levelTag.putInt("version", saveVersion);
         levelTag.putBoolean("CheatsEnabled", cheatsEnabled);
+        levelTag.putInt("Difficulty", difficulty.id());
+        levelTag.putBoolean("DifficultyLock", difficultyLocked);
         if(playerTag != null)
         {
             levelTag.putCompound("Player", playerTag);
@@ -152,6 +159,8 @@ public class LevelData
         levelName = tag.getString("LevelName");
         saveVersion = tag.getInteger("version");
         cheatsEnabled = tag.getBoolean("CheatsEnabled");
+        setDifficulty(tag.getIntegerOrDefault("Difficulty", Difficulty.NORMAL.id()), true);
+        difficultyLocked = tag.getBooleanOrDefault("DifficultyLock", false);
         if (tag.containsKey("Player"))
         {
             playerTag = tag.getCompound("Player");
@@ -209,29 +218,29 @@ public class LevelData
         return dimension;
     }
 
-    public void setSpawnX(int i)
+    public void setSpawnX(int x)
     {
-        spawnX = i;
+        spawnX = x;
     }
 
-    public void setSpawnY(int i)
+    public void setSpawnY(int y)
     {
-        spawnY = i;
+        spawnY = y;
     }
 
-    public void setSpawnZ(int i)
+    public void setSpawnZ(int z)
     {
-        spawnZ = i;
+        spawnZ = z;
     }
 
-    public void setWorldTime(long l)
+    public void setWorldTime(long time)
     {
-        worldTime = l;
+        worldTime = time;
     }
 
-    public void setSizeOnDisk(long l)
+    public void setSizeOnDisk(long size)
     {
-        sizeOnDisk = l;
+        sizeOnDisk = size;
     }
 
     public void setPlayerNBTTagCompound(CompoundTag nbttagcompound)
@@ -239,11 +248,11 @@ public class LevelData
         playerTag = nbttagcompound;
     }
 
-    public void setSpawn(int i, int j, int k)
+    public void setSpawn(int x, int y, int z)
     {
-        spawnX = i;
-        spawnY = j;
-        spawnZ = k;
+        spawnX = x;
+        spawnY = y;
+        spawnZ = z;
     }
 
     public String getWorldName()
@@ -296,6 +305,30 @@ public class LevelData
     	this.cheatsEnabled = enabled;
     }
 
+    public void setDifficulty(Difficulty difficulty, boolean overrideLock) {
+        if (!overrideLock && difficultyLocked) return;
+        this.difficulty = difficulty;
+    }
+
+    public void setDifficulty(int difficulty, boolean overrideLock) {
+        if (!overrideLock && difficultyLocked) return;
+        if (difficulty < 0) new RuntimeException("Attempted to set difficulty to '" + difficulty + "' which is less then 0!").printStackTrace();
+        if (difficulty >= Difficulty.values().length) new RuntimeException("Attempted to set difficulty to '" + difficulty + "' which is more then the '" + Difficulty.values().length + "' difficulties!").printStackTrace();
+        setDifficulty(Difficulty.values()[difficulty], overrideLock);
+    }
+
+    public Difficulty getDifficulty() {
+        return difficulty;
+    }
+
+    public void setDifficultyLocked() {
+        this.difficultyLocked = true;
+    }
+
+    public boolean getDifficultyLocked() {
+        return difficultyLocked;
+    }
+
     private long randomSeed;
     private int spawnX;
     private int spawnY;
@@ -306,8 +339,10 @@ public class LevelData
     private CompoundTag playerTag;
     private int dimension;
     private int gamemode;
-    private String levelName;
+    private @NotNull String levelName = "DEFAULT NAME";
     private int saveVersion;
     private boolean cheatsEnabled;
+    private @NotNull Difficulty difficulty = Difficulty.NORMAL;
+    private boolean difficultyLocked;
     private GameRuleCollection gameRules;
 }
diff --git a/src/main/java/net/minecraft/core/world/type/WorldType.java b/src/main/java/net/minecraft/core/world/type/WorldType.java
index ca65452c2..f7abede76 100644
--- a/src/main/java/net/minecraft/core/world/type/WorldType.java
+++ b/src/main/java/net/minecraft/core/world/type/WorldType.java
@@ -149,7 +149,14 @@ public abstract class WorldType {
         int x = 0;
         int y = 0;
         int z = 0;
+        int attemptsRemaining = 10000;
         spawnLoop:  while (true) {
+            if (attemptsRemaining <= 0) {
+                x = 0;
+                z = 0;
+                y = world.getHeightValue(x, z);
+                break spawnLoop;
+            }
             x += world.rand.nextInt(64) - world.rand.nextInt(64);
             z += world.rand.nextInt(64) - world.rand.nextInt(64);
             for (y = getMaxY(); y >= getMinY(); y--) {
@@ -157,6 +164,7 @@ public abstract class WorldType {
                     break spawnLoop;
                 }
             }
+            attemptsRemaining--;
         }
         world.getLevelData().setSpawn(x, y, z);
     }
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index 3a4b91f02..a4708ee33 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -106,6 +106,13 @@ public class MinecraftServer
             logger.warn("**** NOT ENOUGH RAM!");
             logger.warn("To start the server with more ram, launch it as \"java -Xmx1024M -Xms1024M -jar minecraft_server.jar\"");
         }
+
+        // Manually initialize blocks and items
+        Blocks.init();
+        Items.init();
+
+        Dimension.init();
+
         logger.info("Loading properties");
         propertyManager = new PropertyManager(new File("server.properties"));
         String s = propertyManager.getStringProperty("server-ip", "");
@@ -171,12 +178,6 @@ public class MinecraftServer
             }
         }
 
-        // Manually initialize blocks and items
-        Blocks.init();
-        Items.init();
-
-        Dimension.init();
-
         entityTracker = new EntityTracker[Dimension.getDimensionList().size()];
 
         new Registries();
@@ -355,7 +356,7 @@ public class MinecraftServer
                 dimensionWorlds[i] = new WorldServerMulti(this, saveHandler, worldDirName, i, Dimension.getDimensionList().get(i).defaultWorldType, l, dimensionWorlds[0], spawnerConfig);
             }
             dimensionWorlds[i].addListener(new WorldManager(this, dimensionWorlds[i]));
-            dimensionWorlds[i].difficultySetting = difficulty;
+            dimensionWorlds[i].setDifficulty(difficulty, true);
             dimensionWorlds[i].sleepPercent = sleepPercentage;
             playerList.setPlayerManager(dimensionWorlds);
         }
@@ -774,7 +775,6 @@ public class MinecraftServer
     private final List<ServerCommandEntry> commands;
     public EntityTracker[] entityTracker;
     public boolean onlineMode;
-    public boolean spawnPeacefulMobs;
     public boolean pvpOn;
     public boolean allowFlight;
     public int spawnProtectionRange;
diff --git a/src/main/java/net/minecraft/server/world/WorldServer.java b/src/main/java/net/minecraft/server/world/WorldServer.java
index b0b9c8a24..29cc2d08a 100644
--- a/src/main/java/net/minecraft/server/world/WorldServer.java
+++ b/src/main/java/net/minecraft/server/world/WorldServer.java
@@ -43,10 +43,6 @@ public class WorldServer extends World
     @Override
     public void updateEntityWithOptionalForce(Entity entity, boolean flag)
     {
-        if(!mcServer.spawnPeacefulMobs && ((entity instanceof Animal) || (entity instanceof WaterAnimal)))
-        {
-            entity.remove();
-        }
         if(!(entity.passenger instanceof Player))
         {
             super.updateEntityWithOptionalForce(entity, flag);
