From 3124a708cc78a06a5c0f1178732e5e6b3596583a Mon Sep 17 00:00:00 2001
From: Daniel Saukel <sa.da@unitybox.de>
Date: Tue, 26 Jul 2016 20:50:18 +0200
Subject: [PATCH 1/5] #116: Added enhanced game rules for block breaking

Still requires implementation!
---
 .../dre2n/dungeonsxl/config/WorldConfig.java  | 52 ++++++++++--
 .../dre2n/dungeonsxl/game/GameRules.java      | 81 ++++++++++++++++---
 .../dre2n/dungeonsxl/game/GameType.java       | 32 ++++++--
 .../dungeonsxl/game/GameTypeDefault.java      | 70 +++++++++++-----
 .../dungeonsxl/listener/BlockListener.java    |  2 +-
 .../dre2n/dungeonsxl/sign/PlaceSign.java      |  2 +-
 .../dre2n/dungeonsxl/world/DGameWorld.java    |  1 -
 .../{game => world}/GamePlaceableBlock.java   |  3 +-
 .../dre2n/dungeonsxl/game/CustomGameType.java | 41 ++++++++--
 9 files changed, 229 insertions(+), 55 deletions(-)
 rename core/src/main/java/io/github/dre2n/dungeonsxl/{game => world}/GamePlaceableBlock.java (98%)

diff --git a/core/src/main/java/io/github/dre2n/dungeonsxl/config/WorldConfig.java b/core/src/main/java/io/github/dre2n/dungeonsxl/config/WorldConfig.java
index d9e8888b..d1f7b65c 100644
--- a/core/src/main/java/io/github/dre2n/dungeonsxl/config/WorldConfig.java
+++ b/core/src/main/java/io/github/dre2n/dungeonsxl/config/WorldConfig.java
@@ -30,10 +30,14 @@
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Map.Entry;
 import java.util.Set;
 import java.util.concurrent.CopyOnWriteArrayList;
 import org.bukkit.GameMode;
+import org.bukkit.Material;
 import org.bukkit.configuration.ConfigurationSection;
 import org.bukkit.configuration.file.FileConfiguration;
 import org.bukkit.configuration.file.YamlConfiguration;
@@ -121,12 +125,7 @@ public void load(ConfigurationSection configFile) {
             keepInventoryOnDeath = configFile.getBoolean("keepInventoryOnDeath");
         }
 
-        /* Build */
-        if (configFile.contains("build")) {
-            build = configFile.getBoolean("build");
-        }
-
-        /* GameMode */
+        /* World interaction */
         if (configFile.contains("gameMode")) {
             if (EnumUtil.isValidEnum(GameMode.class, configFile.getString("gameMode").toUpperCase())) {
                 gameMode = GameMode.valueOf(configFile.getString("gameMode"));
@@ -135,6 +134,47 @@ public void load(ConfigurationSection configFile) {
             }
         }
 
+        if (configFile.contains("breakBlocks")) {
+            breakBlocks = configFile.getBoolean("breakBlocks");
+        }
+
+        if (configFile.contains("breakPlacedBlocks")) {
+            breakPlacedBlocks = configFile.getBoolean("breakPlacedBlocks");
+        }
+
+        if (configFile.contains("breakWhitelist")) {
+            breakWhitelist = new HashMap<>();
+            for (Entry<String, Object> entry : configFile.getConfigurationSection("breakWhitelist").getValues(true).entrySet()) {
+                Material breakable = Material.matchMaterial(entry.getKey());
+
+                HashSet<Material> tools = new HashSet<>();
+                if (entry.getValue() instanceof List) {
+                    for (String materialString : (List<String>) entry.getValue()) {
+                        Material tool = Material.matchMaterial(materialString);
+                        if (tool != null) {
+                            tools.add(tool);
+                        }
+                    }
+                }
+
+                breakWhitelist.put(breakable, tools);
+            }
+        }
+
+        if (configFile.contains("placeBlocks")) {
+            placeBlocks = configFile.getBoolean("placeBlocks");
+        }
+
+        if (configFile.contains("placeWhitelist")) {
+            placeWhitelist = new HashSet<>();
+            for (String materialString : configFile.getStringList("placeWhitelist")) {
+                Material material = Material.matchMaterial(materialString);
+                if (material != null) {
+                    placeWhitelist.add(material);
+                }
+            }
+        }
+
         /* PvP */
         if (configFile.contains("playerVersusPlayer")) {
             playerVersusPlayer = configFile.getBoolean("playerVersusPlayer");
diff --git a/core/src/main/java/io/github/dre2n/dungeonsxl/game/GameRules.java b/core/src/main/java/io/github/dre2n/dungeonsxl/game/GameRules.java
index 98dc7654..4cbb0fb3 100644
--- a/core/src/main/java/io/github/dre2n/dungeonsxl/game/GameRules.java
+++ b/core/src/main/java/io/github/dre2n/dungeonsxl/game/GameRules.java
@@ -20,9 +20,12 @@
 import io.github.dre2n.dungeonsxl.reward.Reward;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import org.bukkit.GameMode;
+import org.bukkit.Material;
 import org.bukkit.inventory.ItemStack;
 
 /**
@@ -42,7 +45,11 @@ public class GameRules {
 
         /* World interaction */
         DEFAULT_VALUES.gameMode = GameMode.SURVIVAL;
-        DEFAULT_VALUES.build = false;
+        DEFAULT_VALUES.breakBlocks = false;
+        DEFAULT_VALUES.breakPlacedBlocks = false;
+        DEFAULT_VALUES.breakWhitelist = null;
+        DEFAULT_VALUES.placeBlocks = false;
+        DEFAULT_VALUES.placeWhitelist = null;
 
         /* Fighting */
         DEFAULT_VALUES.playerVersusPlayer = false;
@@ -81,7 +88,11 @@ public class GameRules {
 
     /* World interaction */
     protected GameMode gameMode;
-    protected Boolean build;
+    protected Boolean breakBlocks;
+    protected Boolean breakPlacedBlocks;
+    protected Map<Material, HashSet<Material>> breakWhitelist;
+    protected Boolean placeBlocks;
+    protected Set<Material> placeWhitelist;
 
     /* Fighting */
     protected Boolean playerVersusPlayer;
@@ -156,10 +167,38 @@ public GameMode getGameMode() {
     }
 
     /**
-     * @return if players may build
+     * @return if all blocks may be destroyed
      */
-    public boolean canBuild() {
-        return build;
+    public boolean canBreakBlocks() {
+        return breakBlocks;
+    }
+
+    /**
+     * @return if blocks placed in game may be destroyed
+     */
+    public boolean canBreakPlacedBlocks() {
+        return breakPlacedBlocks;
+    }
+
+    /**
+     * @return the destroyable materials and the materials that may be used to break them or null if any
+     */
+    public Map<Material, HashSet<Material>> getBreakWhitelist() {
+        return breakWhitelist;
+    }
+
+    /**
+     * @return if blocks may be placed
+     */
+    public boolean canPlaceBlocks() {
+        return placeBlocks;
+    }
+
+    /**
+     * @return the placeable materials
+     */
+    public Set<Material> getPlaceWhitelist() {
+        return placeWhitelist;
     }
 
     // Fight
@@ -357,8 +396,16 @@ public void apply(GameType defaultValues) {
             timeToFinish = defaultValues.getShowTime() ? null : -1;
         }
 
-        if (build == null) {
-            build = defaultValues.canBuild();
+        if (breakBlocks == null) {
+            breakBlocks = defaultValues.canBreakBlocks();
+        }
+
+        if (breakPlacedBlocks == null) {
+            breakPlacedBlocks = defaultValues.canBreakPlacedBlocks();
+        }
+
+        if (placeBlocks == null) {
+            placeBlocks = defaultValues.canPlaceBlocks();
         }
 
         if (gameMode == null) {
@@ -397,8 +444,24 @@ public void apply(GameRules defaultValues) {
             gameMode = defaultValues.gameMode;
         }
 
-        if (build == null) {
-            build = defaultValues.build;
+        if (breakBlocks == null) {
+            breakBlocks = defaultValues.breakBlocks;
+        }
+
+        if (breakPlacedBlocks == null) {
+            breakPlacedBlocks = defaultValues.breakPlacedBlocks;
+        }
+
+        if (breakWhitelist == null) {
+            breakWhitelist = defaultValues.breakWhitelist;
+        }
+
+        if (placeBlocks == null) {
+            placeBlocks = defaultValues.placeBlocks;
+        }
+
+        if (placeWhitelist == null) {
+            placeWhitelist = defaultValues.placeWhitelist;
         }
 
         /* Fighting */
diff --git a/core/src/main/java/io/github/dre2n/dungeonsxl/game/GameType.java b/core/src/main/java/io/github/dre2n/dungeonsxl/game/GameType.java
index f5f84333..4a7e843c 100644
--- a/core/src/main/java/io/github/dre2n/dungeonsxl/game/GameType.java
+++ b/core/src/main/java/io/github/dre2n/dungeonsxl/game/GameType.java
@@ -101,15 +101,37 @@ public interface GameType {
     public void setShowTime(boolean showTime);
 
     /**
-     * @return if players can build
+     * @return if all blocks may be destroyed
      */
-    public boolean canBuild();
+    public boolean canBreakBlocks();
 
     /**
-     * @param build
-     * enable / disable building
+     * @param breakBlocks
+     * if blocks may be destroyed
      */
-    public void setBuild(boolean build);
+    public void setBreakBlocks(boolean breakBlocks);
+
+    /**
+     * @return if blocks placed in game may be destroyed
+     */
+    public boolean canBreakPlacedBlocks();
+
+    /**
+     * @param breakPlacedBlocks
+     * if placed blocks may be destroyed
+     */
+    public void setBreakPlacedBlocks(boolean breakPlacedBlocks);
+
+    /**
+     * @return if blocks may be placed
+     */
+    public boolean canPlaceBlocks();
+
+    /**
+     * @param placeBlocks
+     * if blocks may be placed
+     */
+    public void setPlaceBlocks(boolean placeBlocks);
 
     /**
      * @return the gameMode
diff --git a/core/src/main/java/io/github/dre2n/dungeonsxl/game/GameTypeDefault.java b/core/src/main/java/io/github/dre2n/dungeonsxl/game/GameTypeDefault.java
index ab131494..4fddf370 100644
--- a/core/src/main/java/io/github/dre2n/dungeonsxl/game/GameTypeDefault.java
+++ b/core/src/main/java/io/github/dre2n/dungeonsxl/game/GameTypeDefault.java
@@ -23,21 +23,22 @@
  */
 public enum GameTypeDefault implements GameType {
 
-    ADVENTURE("Adventure", "Adventure", false, false, false, true, false, true, GameMode.ADVENTURE, true),
-    ADVENTURE_TIME_IS_RUNNING("Adventure - Time is Running", "Adventure TiR", false, false, false, true, true, true, GameMode.ADVENTURE, true),
-    APOCALYPSE_LAST_MAN_STANDING("Apocalypse", "Apocalypse LMS", true, true, true, true, false, false, GameMode.SURVIVAL, true),
-    APOCALYPSE_LIMITED_MOBS("Apocalypse - Limited Mobs", "Apc Limited", true, true, true, true, false, false, GameMode.SURVIVAL, true),
-    APOCALYPSE_TIME_IS_RUNNING("Apocalypse - Time is Running", "Apocalypse TiR", true, true, true, true, true, false, GameMode.SURVIVAL, true),
-    PVE_LAST_MAN_STANDING("Player versus Environment - Last Man Standing", "PvE LMS", false, false, true, true, false, false, GameMode.SURVIVAL, true),
-    PVE_LIMITED_MOBS("Player versus Environment - Limited Mobs", "PvE Limited", false, false, true, true, false, false, GameMode.SURVIVAL, true),
-    PVE_TIME_IS_RUNNING("Player versus Environment - Time is Running", "PvE TiR", false, false, true, true, true, false, GameMode.SURVIVAL, true),
-    PVP_FACTIONS_BATTLEFIELD("Player versus Player - Factions Battlefield", "FactionsPvP", true, false, false, false, false, false, GameMode.SURVIVAL, true),
-    PVP_LAST_MAN_STANDING("Player versus Player - Last Man Standing", "PvP LMS", true, false, false, false, false, false, GameMode.SURVIVAL, true),
-    QUEST("Quest", "Quest", false, false, false, true, false, false, GameMode.SURVIVAL, true),
-    QUEST_TIME_IS_RUNNING("Quest - Time is Running", "Quest TiR", false, false, false, true, true, false, GameMode.SURVIVAL, true),
-    TEST("Test", "Test", false, false, false, false, true, true, GameMode.SURVIVAL, false),
-    TUTORIAL("Tutorial", "Tutorial", false, false, false, true, false, false, GameMode.SURVIVAL, false),
-    DEFAULT("Default", "Default", false, false, false, true, false, false, GameMode.SURVIVAL, true);
+    ADVENTURE("Adventure", "Adventure", false, false, false, true, false, true, true, true, GameMode.ADVENTURE, true),
+    ADVENTURE_TIME_IS_RUNNING("Adventure - Time is Running", "Adventure TiR", false, false, false, true, true, true, true, true, GameMode.ADVENTURE, true),
+    APOCALYPSE_LAST_MAN_STANDING("Apocalypse", "Apocalypse LMS", true, true, true, true, false, false, false, false, GameMode.SURVIVAL, true),
+    APOCALYPSE_LIMITED_MOBS("Apocalypse - Limited Mobs", "Apc Limited", true, true, true, true, false, false, false, false, GameMode.SURVIVAL, true),
+    APOCALYPSE_TIME_IS_RUNNING("Apocalypse - Time is Running", "Apocalypse TiR", true, true, true, true, true, false, false, false, GameMode.SURVIVAL, true),
+    BEDWARS("Bedwars", "Bedwars", true, false, false, false, false, false, true, true, GameMode.SURVIVAL, true),
+    PVE_LAST_MAN_STANDING("Player versus Environment - Last Man Standing", "PvE LMS", false, false, true, true, false, false, false, false, GameMode.SURVIVAL, true),
+    PVE_LIMITED_MOBS("Player versus Environment - Limited Mobs", "PvE Limited", false, false, true, true, false, false, false, false, GameMode.SURVIVAL, true),
+    PVE_TIME_IS_RUNNING("Player versus Environment - Time is Running", "PvE TiR", false, false, true, true, true, false, false, false, GameMode.SURVIVAL, true),
+    PVP_FACTIONS_BATTLEFIELD("Player versus Player - Factions Battlefield", "FactionsPvP", true, false, false, false, false, false, false, false, GameMode.SURVIVAL, true),
+    PVP_LAST_MAN_STANDING("Player versus Player - Last Man Standing", "PvP LMS", true, false, false, false, false, false, false, false, GameMode.SURVIVAL, true),
+    QUEST("Quest", "Quest", false, false, false, true, false, false, false, false, GameMode.SURVIVAL, true),
+    QUEST_TIME_IS_RUNNING("Quest - Time is Running", "Quest TiR", false, false, false, true, true, false, false, false, GameMode.SURVIVAL, true),
+    TEST("Test", "Test", false, false, false, false, true, true, true, true, GameMode.SURVIVAL, false),
+    TUTORIAL("Tutorial", "Tutorial", false, false, false, true, false, false, false, false, GameMode.SURVIVAL, false),
+    DEFAULT("Default", "Default", false, false, false, true, false, false, false, false, GameMode.SURVIVAL, true);
 
     private String displayName;
     private String signName;
@@ -46,11 +47,14 @@ public enum GameTypeDefault implements GameType {
     private boolean mobWaves;
     private boolean rewards;
     private boolean showTime;
-    private boolean build;
+    private boolean breakBlocks;
+    private boolean breakPlacedBlocks;
+    private boolean placeBlocks;
     private GameMode gameMode;
     private boolean lives;
 
-    GameTypeDefault(String displayName, String signName, boolean playerVersusPlayer, boolean friendlyFire, boolean mobWaves, boolean rewards, boolean showTime, boolean build, GameMode gameMode, boolean lives) {
+    GameTypeDefault(String displayName, String signName, boolean playerVersusPlayer, boolean friendlyFire, boolean mobWaves, boolean rewards,
+            boolean showTime, boolean breakBlocks, boolean breakPlacedBlocks, boolean placeBlocks, GameMode gameMode, boolean lives) {
         this.displayName = displayName;
         this.signName = signName;
         this.playerVersusPlayer = playerVersusPlayer;
@@ -58,7 +62,9 @@ public enum GameTypeDefault implements GameType {
         this.mobWaves = mobWaves;
         this.rewards = rewards;
         this.showTime = showTime;
-        this.build = build;
+        this.breakBlocks = breakBlocks;
+        this.breakPlacedBlocks = breakPlacedBlocks;
+        this.placeBlocks = placeBlocks;
         this.gameMode = gameMode;
         this.lives = lives;
     }
@@ -134,13 +140,33 @@ public void setShowTime(boolean showTime) {
     }
 
     @Override
-    public boolean canBuild() {
-        return build;
+    public boolean canBreakBlocks() {
+        return breakBlocks;
     }
 
     @Override
-    public void setBuild(boolean build) {
-        this.build = build;
+    public void setBreakBlocks(boolean breakBlocks) {
+        this.breakBlocks = breakBlocks;
+    }
+
+    @Override
+    public boolean canBreakPlacedBlocks() {
+        return breakPlacedBlocks;
+    }
+
+    @Override
+    public void setBreakPlacedBlocks(boolean breakPlacedBlocks) {
+        this.breakPlacedBlocks = breakPlacedBlocks;
+    }
+
+    @Override
+    public boolean canPlaceBlocks() {
+        return placeBlocks;
+    }
+
+    @Override
+    public void setPlaceBlocks(boolean placeBlocks) {
+        this.placeBlocks = placeBlocks;
     }
 
     @Override
diff --git a/core/src/main/java/io/github/dre2n/dungeonsxl/listener/BlockListener.java b/core/src/main/java/io/github/dre2n/dungeonsxl/listener/BlockListener.java
index cdc945fb..763726f7 100644
--- a/core/src/main/java/io/github/dre2n/dungeonsxl/listener/BlockListener.java
+++ b/core/src/main/java/io/github/dre2n/dungeonsxl/listener/BlockListener.java
@@ -21,7 +21,6 @@
 import io.github.dre2n.dungeonsxl.DungeonsXL;
 import io.github.dre2n.dungeonsxl.config.DMessages;
 import io.github.dre2n.dungeonsxl.game.Game;
-import io.github.dre2n.dungeonsxl.game.GamePlaceableBlock;
 import io.github.dre2n.dungeonsxl.game.GameType;
 import io.github.dre2n.dungeonsxl.game.GameTypeDefault;
 import io.github.dre2n.dungeonsxl.global.DPortal;
@@ -36,6 +35,7 @@
 import io.github.dre2n.dungeonsxl.task.RedstoneEventTask;
 import io.github.dre2n.dungeonsxl.world.DEditWorld;
 import io.github.dre2n.dungeonsxl.world.DGameWorld;
+import io.github.dre2n.dungeonsxl.world.GamePlaceableBlock;
 import org.bukkit.Location;
 import org.bukkit.Material;
 import org.bukkit.block.Block;
diff --git a/core/src/main/java/io/github/dre2n/dungeonsxl/sign/PlaceSign.java b/core/src/main/java/io/github/dre2n/dungeonsxl/sign/PlaceSign.java
index a79527d6..e7a13ae0 100644
--- a/core/src/main/java/io/github/dre2n/dungeonsxl/sign/PlaceSign.java
+++ b/core/src/main/java/io/github/dre2n/dungeonsxl/sign/PlaceSign.java
@@ -16,8 +16,8 @@
  */
 package io.github.dre2n.dungeonsxl.sign;
 
-import io.github.dre2n.dungeonsxl.game.GamePlaceableBlock;
 import io.github.dre2n.dungeonsxl.world.DGameWorld;
+import io.github.dre2n.dungeonsxl.world.GamePlaceableBlock;
 import org.bukkit.Material;
 import org.bukkit.block.Sign;
 
diff --git a/core/src/main/java/io/github/dre2n/dungeonsxl/world/DGameWorld.java b/core/src/main/java/io/github/dre2n/dungeonsxl/world/DGameWorld.java
index c0cadd6a..c5f98275 100644
--- a/core/src/main/java/io/github/dre2n/dungeonsxl/world/DGameWorld.java
+++ b/core/src/main/java/io/github/dre2n/dungeonsxl/world/DGameWorld.java
@@ -21,7 +21,6 @@
 import io.github.dre2n.dungeonsxl.event.gameworld.GameWorldStartGameEvent;
 import io.github.dre2n.dungeonsxl.event.gameworld.GameWorldUnloadEvent;
 import io.github.dre2n.dungeonsxl.game.Game;
-import io.github.dre2n.dungeonsxl.game.GamePlaceableBlock;
 import io.github.dre2n.dungeonsxl.mob.DMob;
 import io.github.dre2n.dungeonsxl.player.DGroup;
 import io.github.dre2n.dungeonsxl.reward.RewardChest;
diff --git a/core/src/main/java/io/github/dre2n/dungeonsxl/game/GamePlaceableBlock.java b/core/src/main/java/io/github/dre2n/dungeonsxl/world/GamePlaceableBlock.java
similarity index 98%
rename from core/src/main/java/io/github/dre2n/dungeonsxl/game/GamePlaceableBlock.java
rename to core/src/main/java/io/github/dre2n/dungeonsxl/world/GamePlaceableBlock.java
index f2fa393b..5f1f324d 100644
--- a/core/src/main/java/io/github/dre2n/dungeonsxl/game/GamePlaceableBlock.java
+++ b/core/src/main/java/io/github/dre2n/dungeonsxl/world/GamePlaceableBlock.java
@@ -14,10 +14,9 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
-package io.github.dre2n.dungeonsxl.game;
+package io.github.dre2n.dungeonsxl.world;
 
 import io.github.dre2n.commons.util.NumberUtil;
-import io.github.dre2n.dungeonsxl.world.DGameWorld;
 import java.util.HashSet;
 import java.util.Set;
 import org.bukkit.Material;
diff --git a/core/src/test/java/io/github/dre2n/dungeonsxl/game/CustomGameType.java b/core/src/test/java/io/github/dre2n/dungeonsxl/game/CustomGameType.java
index da570b5a..0c79e280 100644
--- a/core/src/test/java/io/github/dre2n/dungeonsxl/game/CustomGameType.java
+++ b/core/src/test/java/io/github/dre2n/dungeonsxl/game/CustomGameType.java
@@ -23,7 +23,7 @@
  */
 public enum CustomGameType implements GameType {
 
-    GHOST("My awesome game type", "Identifier", false, false, false, false, false, false, GameMode.SPECTATOR, false);
+    GHOST("My awesome game type", "Identifier", false, false, false, false, false, false, false, false, GameMode.SPECTATOR, false);
 
     private String displayName;
     private String signName;
@@ -32,11 +32,14 @@ public enum CustomGameType implements GameType {
     private boolean mobWaves;
     private boolean rewards;
     private boolean showTime;
-    private boolean build;
+    private boolean breakBlocks;
+    private boolean breakPlacedBlocks;
+    private boolean placeBlocks;
     private GameMode gameMode;
     private boolean lives;
 
-    CustomGameType(String displayName, String signName, boolean playerVersusPlayer, boolean friendlyFire, boolean mobWaves, boolean rewards, boolean showTime, boolean build, GameMode gameMode, boolean lives) {
+    CustomGameType(String displayName, String signName, boolean playerVersusPlayer, boolean friendlyFire, boolean mobWaves, boolean rewards,
+            boolean showTime, boolean breakBlocks, boolean breakPlacedBlocks, boolean placeBlocks, GameMode gameMode, boolean lives) {
         this.displayName = displayName;
         this.signName = signName;
         this.playerVersusPlayer = playerVersusPlayer;
@@ -44,7 +47,9 @@ public enum CustomGameType implements GameType {
         this.mobWaves = mobWaves;
         this.rewards = rewards;
         this.showTime = showTime;
-        this.build = build;
+        this.breakBlocks = breakBlocks;
+        this.breakPlacedBlocks = breakPlacedBlocks;
+        this.placeBlocks = placeBlocks;
         this.gameMode = gameMode;
         this.lives = lives;
     }
@@ -120,13 +125,33 @@ public void setShowTime(boolean showTime) {
     }
 
     @Override
-    public boolean canBuild() {
-        return build;
+    public boolean canBreakBlocks() {
+        return breakBlocks;
     }
 
     @Override
-    public void setBuild(boolean build) {
-        this.build = build;
+    public void setBreakBlocks(boolean breakBlocks) {
+        this.breakBlocks = breakBlocks;
+    }
+
+    @Override
+    public boolean canBreakPlacedBlocks() {
+        return breakPlacedBlocks;
+    }
+
+    @Override
+    public void setBreakPlacedBlocks(boolean breakPlacedBlocks) {
+        this.breakPlacedBlocks = breakPlacedBlocks;
+    }
+
+    @Override
+    public boolean canPlaceBlocks() {
+        return placeBlocks;
+    }
+
+    @Override
+    public void setPlaceBlocks(boolean placeBlocks) {
+        this.placeBlocks = placeBlocks;
     }
 
     @Override

From bdf5b54975863fd47957d4859f5cde88564a3790 Mon Sep 17 00:00:00 2001
From: Daniel Saukel <sa.da@unitybox.de>
Date: Thu, 28 Jul 2016 16:13:01 +0200
Subject: [PATCH 2/5] Don't enable performance tweaks if server version is not
 supported

---
 .../io/github/dre2n/dungeonsxl/config/DMessages.java   |  1 +
 .../io/github/dre2n/dungeonsxl/config/MainConfig.java  | 10 +++++++++-
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/core/src/main/java/io/github/dre2n/dungeonsxl/config/DMessages.java b/core/src/main/java/io/github/dre2n/dungeonsxl/config/DMessages.java
index 47f3445c..da5c433e 100644
--- a/core/src/main/java/io/github/dre2n/dungeonsxl/config/DMessages.java
+++ b/core/src/main/java/io/github/dre2n/dungeonsxl/config/DMessages.java
@@ -134,6 +134,7 @@ public enum DMessages implements Messages {
     GROUP_KICKED_PLAYER("Group_KickedPlayer", "&4&v1&6 kicked the player &4&v2&6 from the group &4&v3&6."),
     GROUP_PLAYER_JOINED("Group_PlayerJoined", "&6Player &4&v1&6 has joined the group!"),
     GROUP_WAVE_FINISHED("Group_WaveFinished", "&6Your group finished wave no. &4&v1&6. The next one is going to start in &4&v2&6 seconds."),
+    LOG_DISABLED_TWEAKS("Log_DisabledTweaks", "&4Disabled performance tweaks because there is no support for this server software."),
     LOG_ERROR_MOB_ENCHANTMENT("Log_Error_MobEnchantment", "&4Error at loading mob.yml: Enchantment &6&v1&4 doesn't exist!"),
     LOG_ERROR_MOBTYPE("Log_Error_MobType", "&4Error at loading mob.yml: Mob &6&v1&4 doesn't exist!"),
     LOG_ERROR_NO_CONSOLE_COMMAND("Log_Error_NoConsoleCommand", "&6/dxl &v1&4 can not be executed as console!"),
diff --git a/core/src/main/java/io/github/dre2n/dungeonsxl/config/MainConfig.java b/core/src/main/java/io/github/dre2n/dungeonsxl/config/MainConfig.java
index a16ba0f1..320d4f3f 100644
--- a/core/src/main/java/io/github/dre2n/dungeonsxl/config/MainConfig.java
+++ b/core/src/main/java/io/github/dre2n/dungeonsxl/config/MainConfig.java
@@ -16,8 +16,11 @@
  */
 package io.github.dre2n.dungeonsxl.config;
 
+import io.github.dre2n.commons.compatibility.CompatibilityHandler;
+import io.github.dre2n.commons.compatibility.Internals;
 import io.github.dre2n.commons.config.BRConfig;
 import io.github.dre2n.commons.util.EnumUtil;
+import io.github.dre2n.commons.util.messageutil.MessageUtil;
 import java.io.File;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -496,7 +499,12 @@ public void load() {
         }
 
         if (config.contains("tweaksEnabled")) {
-            tweaksEnabled = config.getBoolean("tweaksEnabled");
+            if (Internals.andHigher(Internals.v1_9_R1).contains(CompatibilityHandler.getInstance().getInternals())) {
+                tweaksEnabled = config.getBoolean("tweaksEnabled");
+            } else {
+                tweaksEnabled = false;
+                MessageUtil.log(DMessages.LOG_DISABLED_TWEAKS.getMessage());
+            }
         }
 
         if (config.contains("secureMode.enabled")) {

From 5b77ab4e8c615e039c58622aaa8460f092489cf1 Mon Sep 17 00:00:00 2001
From: Daniel Saukel <sa.da@unitybox.de>
Date: Thu, 28 Jul 2016 16:13:37 +0200
Subject: [PATCH 3/5] Fix NPE if class sign uses an unknown class

---
 .../dre2n/dungeonsxl/sign/ClassesSign.java       | 16 ++++++++++------
 1 file changed, 10 insertions(+), 6 deletions(-)

diff --git a/core/src/main/java/io/github/dre2n/dungeonsxl/sign/ClassesSign.java b/core/src/main/java/io/github/dre2n/dungeonsxl/sign/ClassesSign.java
index 13eba0ff..63b8831b 100644
--- a/core/src/main/java/io/github/dre2n/dungeonsxl/sign/ClassesSign.java
+++ b/core/src/main/java/io/github/dre2n/dungeonsxl/sign/ClassesSign.java
@@ -59,13 +59,17 @@ public boolean check() {
 
     @Override
     public void onInit() {
-        getSign().setLine(0, ChatColor.DARK_BLUE + "############");
-        getSign().setLine(1, ChatColor.DARK_GREEN + dClass.getName());
-        getSign().setLine(2, "");
-        getSign().setLine(3, ChatColor.DARK_BLUE + "############");
-        getSign().update();
+        if (dClass != null) {
+            getSign().setLine(0, ChatColor.DARK_BLUE + "############");
+            getSign().setLine(1, ChatColor.DARK_GREEN + dClass.getName());
+            getSign().setLine(2, "");
+            getSign().setLine(3, ChatColor.DARK_BLUE + "############");
+            getSign().update();
+            getGameWorld().getClassesSigns().add(getSign());
 
-        getGameWorld().getClassesSigns().add(getSign());
+        } else {
+            markAsErroneous();
+        }
     }
 
     @Override

From 80ceb79b5d9084a911838529ea2fdb7b4d90eac6 Mon Sep 17 00:00:00 2001
From: Daniel Saukel <sa.da@unitybox.de>
Date: Thu, 28 Jul 2016 16:14:50 +0200
Subject: [PATCH 4/5] Implement simple break / place game rules

---
 .../dre2n/dungeonsxl/game/GameRules.java      |  2 +-
 .../dre2n/dungeonsxl/game/GameType.java       | 36 +++++-----
 .../dungeonsxl/game/GameTypeDefault.java      | 66 ++++++++++---------
 .../dungeonsxl/global/GlobalProtection.java   | 19 +++++-
 .../dungeonsxl/listener/BlockListener.java    | 55 ++--------------
 .../dre2n/dungeonsxl/player/DGroup.java       | 15 +----
 .../dre2n/dungeonsxl/sign/ReadySign.java      |  2 +-
 .../dre2n/dungeonsxl/world/DGameWorld.java    | 58 ++++++++++++++++
 .../dre2n/dungeonsxl/game/CustomGameType.java | 58 ++++++++--------
 9 files changed, 168 insertions(+), 143 deletions(-)

diff --git a/core/src/main/java/io/github/dre2n/dungeonsxl/game/GameRules.java b/core/src/main/java/io/github/dre2n/dungeonsxl/game/GameRules.java
index 4cbb0fb3..a211ad58 100644
--- a/core/src/main/java/io/github/dre2n/dungeonsxl/game/GameRules.java
+++ b/core/src/main/java/io/github/dre2n/dungeonsxl/game/GameRules.java
@@ -392,7 +392,7 @@ public void apply(GameType defaultValues) {
             friendlyFire = defaultValues.isFriendlyFire();
         }
 
-        if (timeToFinish == null) {
+        if (timeToFinish == null && defaultValues.getShowTime() != null) {
             timeToFinish = defaultValues.getShowTime() ? null : -1;
         }
 
diff --git a/core/src/main/java/io/github/dre2n/dungeonsxl/game/GameType.java b/core/src/main/java/io/github/dre2n/dungeonsxl/game/GameType.java
index 4a7e843c..cef39160 100644
--- a/core/src/main/java/io/github/dre2n/dungeonsxl/game/GameType.java
+++ b/core/src/main/java/io/github/dre2n/dungeonsxl/game/GameType.java
@@ -48,90 +48,90 @@ public interface GameType {
     /**
      * @return the playerVersusPlayer
      */
-    public boolean isPlayerVersusPlayer();
+    public Boolean isPlayerVersusPlayer();
 
     /**
      * @param playerVersusPlayer
      * the playerVersusPlayer to set
      */
-    public void setPlayerVersusPlayer(boolean playerVersusPlayer);
+    public void setPlayerVersusPlayer(Boolean playerVersusPlayer);
 
     /**
      * @return the friendlyFire
      */
-    public boolean isFriendlyFire();
+    public Boolean isFriendlyFire();
 
     /**
      * @param friendlyFire
      * the friendlyFire to set
      */
-    public void setFriendlyFire(boolean friendlyFire);
+    public void setFriendlyFire(Boolean friendlyFire);
 
     /**
      * @return the mobWaves
      */
-    public boolean hasMobWaves();
+    public Boolean hasMobWaves();
 
     /**
      * @param mobWaves
      * enable / disable mob waves
      */
-    public void setMobWaves(boolean mobWaves);
+    public void setMobWaves(Boolean mobWaves);
 
     /**
      * @return if players get rewards after the dungeon
      */
-    public boolean hasRewards();
+    public Boolean hasRewards();
 
     /**
      * @param rewards
      * enable / disable rewards
      */
-    public void setRewards(boolean rewards);
+    public void setRewards(Boolean rewards);
 
     /**
      * @return if players shall see how long they play
      */
-    public boolean getShowTime();
+    public Boolean getShowTime();
 
     /**
      * @param showTime
      * set if players shall see how long they play
      */
-    public void setShowTime(boolean showTime);
+    public void setShowTime(Boolean showTime);
 
     /**
      * @return if all blocks may be destroyed
      */
-    public boolean canBreakBlocks();
+    public Boolean canBreakBlocks();
 
     /**
      * @param breakBlocks
      * if blocks may be destroyed
      */
-    public void setBreakBlocks(boolean breakBlocks);
+    public void setBreakBlocks(Boolean breakBlocks);
 
     /**
      * @return if blocks placed in game may be destroyed
      */
-    public boolean canBreakPlacedBlocks();
+    public Boolean canBreakPlacedBlocks();
 
     /**
      * @param breakPlacedBlocks
      * if placed blocks may be destroyed
      */
-    public void setBreakPlacedBlocks(boolean breakPlacedBlocks);
+    public void setBreakPlacedBlocks(Boolean breakPlacedBlocks);
 
     /**
      * @return if blocks may be placed
      */
-    public boolean canPlaceBlocks();
+    public Boolean canPlaceBlocks();
 
     /**
      * @param placeBlocks
      * if blocks may be placed
      */
-    public void setPlaceBlocks(boolean placeBlocks);
+    public void setPlaceBlocks(Boolean placeBlocks);
 
     /**
      * @return the gameMode
@@ -147,12 +147,12 @@ public interface GameType {
     /**
      * @return if players lose lives
      */
-    public boolean hasLives();
+    public Boolean hasLives();
 
     /**
      * @param lives
      * set if the gametype uses player lives
      */
-    public void setLives(boolean lives);
+    public void setLives(Boolean lives);
 
 }
diff --git a/core/src/main/java/io/github/dre2n/dungeonsxl/game/GameTypeDefault.java b/core/src/main/java/io/github/dre2n/dungeonsxl/game/GameTypeDefault.java
index 4fddf370..2777cddb 100644
--- a/core/src/main/java/io/github/dre2n/dungeonsxl/game/GameTypeDefault.java
+++ b/core/src/main/java/io/github/dre2n/dungeonsxl/game/GameTypeDefault.java
@@ -38,23 +38,24 @@ public enum GameTypeDefault implements GameType {
     QUEST_TIME_IS_RUNNING("Quest - Time is Running", "Quest TiR", false, false, false, true, true, false, false, false, GameMode.SURVIVAL, true),
     TEST("Test", "Test", false, false, false, false, true, true, true, true, GameMode.SURVIVAL, false),
     TUTORIAL("Tutorial", "Tutorial", false, false, false, true, false, false, false, false, GameMode.SURVIVAL, false),
-    DEFAULT("Default", "Default", false, false, false, true, false, false, false, false, GameMode.SURVIVAL, true);
+    DEFAULT("Default", "Default", false, false, false, true, false, false, false, false, GameMode.SURVIVAL, true),
+    CUSTOM("Custom", "Custom");
 
     private String displayName;
     private String signName;
-    private boolean playerVersusPlayer;
-    private boolean friendlyFire;
-    private boolean mobWaves;
-    private boolean rewards;
-    private boolean showTime;
-    private boolean breakBlocks;
-    private boolean breakPlacedBlocks;
-    private boolean placeBlocks;
+    private Boolean playerVersusPlayer;
+    private Boolean friendlyFire;
+    private Boolean mobWaves;
+    private Boolean rewards;
+    private Boolean showTime;
+    private Boolean breakBlocks;
+    private Boolean breakPlacedBlocks;
+    private Boolean placeBlocks;
     private GameMode gameMode;
-    private boolean lives;
+    private Boolean lives;
 
-    GameTypeDefault(String displayName, String signName, boolean playerVersusPlayer, boolean friendlyFire, boolean mobWaves, boolean rewards,
-            boolean showTime, boolean breakBlocks, boolean breakPlacedBlocks, boolean placeBlocks, GameMode gameMode, boolean lives) {
+    GameTypeDefault(String displayName, String signName, Boolean playerVersusPlayer, Boolean friendlyFire, Boolean mobWaves, Boolean rewards,
+            Boolean showTime, Boolean breakBlocks, Boolean breakPlacedBlocks, Boolean placeBlocks, GameMode gameMode, Boolean lives) {
         this.displayName = displayName;
         this.signName = signName;
         this.playerVersusPlayer = playerVersusPlayer;
@@ -69,6 +70,11 @@ public enum GameTypeDefault implements GameType {
         this.lives = lives;
     }
 
+    GameTypeDefault(String displayName, String signName) {
+        this.displayName = displayName;
+        this.signName = signName;
+    }
+
     @Override
     public String getDisplayName() {
         return displayName;
@@ -90,82 +96,82 @@ public void setSignName(String signName) {
     }
 
     @Override
-    public boolean isPlayerVersusPlayer() {
+    public Boolean isPlayerVersusPlayer() {
         return playerVersusPlayer;
     }
 
     @Override
-    public void setPlayerVersusPlayer(boolean playerVersusPlayer) {
+    public void setPlayerVersusPlayer(Boolean playerVersusPlayer) {
         this.playerVersusPlayer = playerVersusPlayer;
     }
 
     @Override
-    public boolean isFriendlyFire() {
+    public Boolean isFriendlyFire() {
         return friendlyFire;
     }
 
     @Override
-    public void setFriendlyFire(boolean friendlyFire) {
+    public void setFriendlyFire(Boolean friendlyFire) {
         this.friendlyFire = friendlyFire;
     }
 
     @Override
-    public boolean hasMobWaves() {
+    public Boolean hasMobWaves() {
         return mobWaves;
     }
 
     @Override
-    public void setMobWaves(boolean mobWaves) {
+    public void setMobWaves(Boolean mobWaves) {
         this.mobWaves = mobWaves;
     }
 
     @Override
-    public boolean hasRewards() {
+    public Boolean hasRewards() {
         return rewards;
     }
 
     @Override
-    public void setRewards(boolean rewards) {
+    public void setRewards(Boolean rewards) {
         this.rewards = rewards;
     }
 
     @Override
-    public boolean getShowTime() {
+    public Boolean getShowTime() {
         return showTime;
     }
 
     @Override
-    public void setShowTime(boolean showTime) {
+    public void setShowTime(Boolean showTime) {
         this.showTime = showTime;
     }
 
     @Override
-    public boolean canBreakBlocks() {
+    public Boolean canBreakBlocks() {
         return breakBlocks;
     }
 
     @Override
-    public void setBreakBlocks(boolean breakBlocks) {
+    public void setBreakBlocks(Boolean breakBlocks) {
         this.breakBlocks = breakBlocks;
     }
 
     @Override
-    public boolean canBreakPlacedBlocks() {
+    public Boolean canBreakPlacedBlocks() {
         return breakPlacedBlocks;
     }
 
     @Override
-    public void setBreakPlacedBlocks(boolean breakPlacedBlocks) {
+    public void setBreakPlacedBlocks(Boolean breakPlacedBlocks) {
         this.breakPlacedBlocks = breakPlacedBlocks;
     }
 
     @Override
-    public boolean canPlaceBlocks() {
+    public Boolean canPlaceBlocks() {
         return placeBlocks;
     }
 
     @Override
-    public void setPlaceBlocks(boolean placeBlocks) {
+    public void setPlaceBlocks(Boolean placeBlocks) {
         this.placeBlocks = placeBlocks;
     }
 
@@ -180,12 +186,12 @@ public void setGameMode(GameMode gameMode) {
     }
 
     @Override
-    public boolean hasLives() {
+    public Boolean hasLives() {
         return lives;
     }
 
     @Override
-    public void setLives(boolean lives) {
+    public void setLives(Boolean lives) {
         this.lives = lives;
     }
 
diff --git a/core/src/main/java/io/github/dre2n/dungeonsxl/global/GlobalProtection.java b/core/src/main/java/io/github/dre2n/dungeonsxl/global/GlobalProtection.java
index 42e30ba5..e081bcbd 100644
--- a/core/src/main/java/io/github/dre2n/dungeonsxl/global/GlobalProtection.java
+++ b/core/src/main/java/io/github/dre2n/dungeonsxl/global/GlobalProtection.java
@@ -5,7 +5,10 @@
  */
 package io.github.dre2n.dungeonsxl.global;
 
+import io.github.dre2n.commons.util.messageutil.MessageUtil;
 import io.github.dre2n.dungeonsxl.DungeonsXL;
+import io.github.dre2n.dungeonsxl.config.DMessages;
+import io.github.dre2n.dungeonsxl.player.DGlobalPlayer;
 import java.io.File;
 import java.util.Collection;
 import org.bukkit.World;
@@ -54,6 +57,7 @@ public int getId() {
         return id;
     }
 
+    /* Actions */
     /**
      * Delete this protection.
      */
@@ -61,7 +65,6 @@ public void delete() {
         protections.removeProtection(this);
     }
 
-    /* Abstracts */
     /**
      * Save the data to the default file
      */
@@ -77,6 +80,20 @@ public void save(File file) {
         save(YamlConfiguration.loadConfiguration(file));
     }
 
+    public boolean onBreak(DGlobalPlayer dPlayer) {
+        if (dPlayer.isInBreakMode()) {
+            delete();
+            MessageUtil.sendMessage(dPlayer.getPlayer(), plugin.getMessageConfig().getMessage(DMessages.PLAYER_PROTECTED_BLOCK_DELETED));
+            MessageUtil.sendMessage(dPlayer.getPlayer(), plugin.getMessageConfig().getMessage(DMessages.CMD_BREAK_PROTECTED_MODE));
+            dPlayer.setInBreakMode(false);
+            return false;
+
+        } else {
+            return true;
+        }
+    }
+
+    /* Abstracts */
     /**
      * @param config
      * the config to save the protection to
diff --git a/core/src/main/java/io/github/dre2n/dungeonsxl/listener/BlockListener.java b/core/src/main/java/io/github/dre2n/dungeonsxl/listener/BlockListener.java
index 763726f7..b55c7fca 100644
--- a/core/src/main/java/io/github/dre2n/dungeonsxl/listener/BlockListener.java
+++ b/core/src/main/java/io/github/dre2n/dungeonsxl/listener/BlockListener.java
@@ -20,9 +20,6 @@
 import io.github.dre2n.commons.util.messageutil.MessageUtil;
 import io.github.dre2n.dungeonsxl.DungeonsXL;
 import io.github.dre2n.dungeonsxl.config.DMessages;
-import io.github.dre2n.dungeonsxl.game.Game;
-import io.github.dre2n.dungeonsxl.game.GameType;
-import io.github.dre2n.dungeonsxl.game.GameTypeDefault;
 import io.github.dre2n.dungeonsxl.global.DPortal;
 import io.github.dre2n.dungeonsxl.global.GameSign;
 import io.github.dre2n.dungeonsxl.global.GlobalProtection;
@@ -35,8 +32,6 @@
 import io.github.dre2n.dungeonsxl.task.RedstoneEventTask;
 import io.github.dre2n.dungeonsxl.world.DEditWorld;
 import io.github.dre2n.dungeonsxl.world.DGameWorld;
-import io.github.dre2n.dungeonsxl.world.GamePlaceableBlock;
-import org.bukkit.Location;
 import org.bukkit.Material;
 import org.bukkit.block.Block;
 import org.bukkit.block.Sign;
@@ -80,16 +75,9 @@ public void onBreak(BlockBreakEvent event) {
 
         GlobalProtection protection = plugin.getGlobalProtections().getByBlock(event.getBlock());
         if (protection != null) {
-            if (dGlobalPlayer.isInBreakMode()) {
-                protection.delete();
-                MessageUtil.sendMessage(player, plugin.getMessageConfig().getMessage(DMessages.PLAYER_PROTECTED_BLOCK_DELETED));
-                MessageUtil.sendMessage(player, plugin.getMessageConfig().getMessage(DMessages.CMD_BREAK_PROTECTED_MODE));
-                dGlobalPlayer.setInBreakMode(false);
-
-            } else {
+            if (protection.onBreak(dGlobalPlayer)) {
                 event.setCancelled(true);
             }
-
             return;
         }
 
@@ -103,24 +91,7 @@ public void onBreak(BlockBreakEvent event) {
         // Deny DGameWorld block breaking
         DGameWorld gameWorld = DGameWorld.getByWorld(block.getWorld());
         if (gameWorld != null) {
-            for (DSign dSign : gameWorld.getDSigns()) {
-                if (dSign.getSign().equals(block)) {
-                    event.setCancelled(true);
-                    return;
-                }
-            }
-
-            Game game = gameWorld.getGame();
-            if (game != null) {
-                GameType gameType = game.getType();
-                if (gameType == GameTypeDefault.DEFAULT) {
-                    event.setCancelled(!game.getRules().canBuild());
-
-                } else if (!gameType.canBuild()) {
-                    event.setCancelled(true);
-                }
-
-            } else {
+            if (gameWorld.onBreak(player, block)) {
                 event.setCancelled(true);
             }
         }
@@ -136,27 +107,9 @@ public void onPlace(BlockPlaceEvent event) {
             return;
         }
 
-        Game game = gameWorld.getGame();
-        if (game != null) {
-            if (game.getRules().canBuild() || GamePlaceableBlock.canBuildHere(block, block.getFace(event.getBlockAgainst()), event.getItemInHand().getType(), gameWorld)) {
-                return;
-            }
-        }
-
-        // Workaround for a bug that would allow 3-Block-high jumping
-        Location loc = event.getPlayer().getLocation();
-        if (loc.getY() > block.getY() + 1.0 && loc.getY() <= block.getY() + 1.5) {
-            if (loc.getX() >= block.getX() - 0.3 && loc.getX() <= block.getX() + 1.3) {
-                if (loc.getZ() >= block.getZ() - 0.3 && loc.getZ() <= block.getZ() + 1.3) {
-                    loc.setX(block.getX() + 0.5);
-                    loc.setY(block.getY());
-                    loc.setZ(block.getZ() + 0.5);
-                    event.getPlayer().teleport(loc);
-                }
-            }
+        if (gameWorld.onPlace(event.getPlayer(), block, event.getBlockAgainst(), event.getItemInHand())) {
+            event.setCancelled(true);
         }
-
-        event.setCancelled(true);
     }
 
     @EventHandler(priority = EventPriority.NORMAL)
diff --git a/core/src/main/java/io/github/dre2n/dungeonsxl/player/DGroup.java b/core/src/main/java/io/github/dre2n/dungeonsxl/player/DGroup.java
index 1b9c8c29..c707d6b0 100644
--- a/core/src/main/java/io/github/dre2n/dungeonsxl/player/DGroup.java
+++ b/core/src/main/java/io/github/dre2n/dungeonsxl/player/DGroup.java
@@ -658,18 +658,9 @@ public void startGame(Game game) {
                 requirement.demand(player);
             }
 
-            GameType gameType = game.getType();
-            if (gameType == GameTypeDefault.DEFAULT) {
-                player.setGameMode(rules.getGameMode());
-                if (rules.isTimeIsRunning()) {
-                    timeIsRunningTask = new TimeIsRunningTask(this, rules.getTimeToFinish()).runTaskTimer(plugin, 20, 20);
-                }
-
-            } else {
-                player.setGameMode(gameType.getGameMode());
-                if (gameType.getShowTime()) {
-                    timeIsRunningTask = new TimeIsRunningTask(this, rules.getTimeToFinish()).runTaskTimer(plugin, 20, 20);
-                }
+            player.setGameMode(rules.getGameMode());
+            if (rules.isTimeIsRunning()) {
+                timeIsRunningTask = new TimeIsRunningTask(this, rules.getTimeToFinish()).runTaskTimer(plugin, 20, 20);
             }
 
             // Permission bridge
diff --git a/core/src/main/java/io/github/dre2n/dungeonsxl/sign/ReadySign.java b/core/src/main/java/io/github/dre2n/dungeonsxl/sign/ReadySign.java
index d3719748..65de59da 100644
--- a/core/src/main/java/io/github/dre2n/dungeonsxl/sign/ReadySign.java
+++ b/core/src/main/java/io/github/dre2n/dungeonsxl/sign/ReadySign.java
@@ -88,7 +88,7 @@ public void onInit() {
             gameType = plugin.getGameTypes().getBySign(this);
 
         } else {
-            gameType = GameTypeDefault.DEFAULT;
+            gameType = GameTypeDefault.CUSTOM;
         }
 
         if (!lines[2].isEmpty()) {
diff --git a/core/src/main/java/io/github/dre2n/dungeonsxl/world/DGameWorld.java b/core/src/main/java/io/github/dre2n/dungeonsxl/world/DGameWorld.java
index c5f98275..3052f17f 100644
--- a/core/src/main/java/io/github/dre2n/dungeonsxl/world/DGameWorld.java
+++ b/core/src/main/java/io/github/dre2n/dungeonsxl/world/DGameWorld.java
@@ -21,6 +21,7 @@
 import io.github.dre2n.dungeonsxl.event.gameworld.GameWorldStartGameEvent;
 import io.github.dre2n.dungeonsxl.event.gameworld.GameWorldUnloadEvent;
 import io.github.dre2n.dungeonsxl.game.Game;
+import io.github.dre2n.dungeonsxl.game.GameRules;
 import io.github.dre2n.dungeonsxl.mob.DMob;
 import io.github.dre2n.dungeonsxl.player.DGroup;
 import io.github.dre2n.dungeonsxl.reward.RewardChest;
@@ -37,15 +38,18 @@
 import io.github.dre2n.dungeonsxl.trigger.TriggerTypeDefault;
 import java.io.File;
 import java.util.ArrayList;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.concurrent.CopyOnWriteArrayList;
 import org.bukkit.Chunk;
 import org.bukkit.Location;
 import org.bukkit.World;
+import org.bukkit.block.Block;
 import org.bukkit.block.Sign;
 import org.bukkit.entity.Entity;
 import org.bukkit.entity.EntityType;
 import org.bukkit.entity.LivingEntity;
+import org.bukkit.entity.Player;
 import org.bukkit.entity.Spider;
 import org.bukkit.inventory.ItemStack;
 import org.bukkit.scheduler.BukkitRunnable;
@@ -60,6 +64,7 @@ public class DGameWorld extends DInstanceWorld {
     private boolean isPlaying = false;
 
     // TO DO: Which lists actually need to be CopyOnWriteArrayLists?
+    private List<Block> placedBlocks = new LinkedList<>();
     private CopyOnWriteArrayList<GamePlaceableBlock> placeableBlocks = new CopyOnWriteArrayList<>();
     private List<ItemStack> secureObjects = new CopyOnWriteArrayList<>();
     private CopyOnWriteArrayList<Chunk> loadedChunks = new CopyOnWriteArrayList<>();
@@ -451,6 +456,59 @@ public void update() {
         }
     }
 
+    public boolean onBreak(Player player, Block block) {
+        for (DSign dSign : dSigns) {
+            if (dSign.getSign().getBlock().equals(block)) {
+                return true;
+            }
+        }
+
+        for (RewardChest rChest : rewardChests) {
+            if (rChest.getChest().getBlock().equals(block)) {
+                return true;
+            }
+        }
+
+        Game game = getGame();
+        if (game != null) {
+            GameRules rules = game.getRules();
+            if (rules.canBreakBlocks()) {
+                return (false);
+            } else if (rules.canBreakPlacedBlocks()) {
+                return (!placedBlocks.contains(block));
+            }
+        }
+
+        return true;
+    }
+
+    public boolean onPlace(Player player, Block block, Block against, ItemStack hand) {
+        // Workaround for a bug that would allow 3-Block-high jumping
+        Location loc = player.getLocation();
+        if (loc.getY() > block.getY() + 1.0 && loc.getY() <= block.getY() + 1.5) {
+            if (loc.getX() >= block.getX() - 0.3 && loc.getX() <= block.getX() + 1.3) {
+                if (loc.getZ() >= block.getZ() - 0.3 && loc.getZ() <= block.getZ() + 1.3) {
+                    loc.setX(block.getX() + 0.5);
+                    loc.setY(block.getY());
+                    loc.setZ(block.getZ() + 0.5);
+                    player.teleport(loc);
+                }
+            }
+        }
+
+        Game game = getGame();
+        if (game == null) {
+            return true;
+        }
+
+        if (game.getRules().canPlaceBlocks() || GamePlaceableBlock.canBuildHere(block, block.getFace(against), hand.getType(), this)) {
+            placedBlocks.add(block);
+            return false;
+        }
+
+        return true;
+    }
+
     /* Statics */
     /**
      * @param world
diff --git a/core/src/test/java/io/github/dre2n/dungeonsxl/game/CustomGameType.java b/core/src/test/java/io/github/dre2n/dungeonsxl/game/CustomGameType.java
index 0c79e280..16a14714 100644
--- a/core/src/test/java/io/github/dre2n/dungeonsxl/game/CustomGameType.java
+++ b/core/src/test/java/io/github/dre2n/dungeonsxl/game/CustomGameType.java
@@ -27,19 +27,19 @@ public enum CustomGameType implements GameType {
 
     private String displayName;
     private String signName;
-    private boolean playerVersusPlayer;
-    private boolean friendlyFire;
-    private boolean mobWaves;
-    private boolean rewards;
-    private boolean showTime;
-    private boolean breakBlocks;
-    private boolean breakPlacedBlocks;
-    private boolean placeBlocks;
+    private Boolean playerVersusPlayer;
+    private Boolean friendlyFire;
+    private Boolean mobWaves;
+    private Boolean rewards;
+    private Boolean showTime;
+    private Boolean breakBlocks;
+    private Boolean breakPlacedBlocks;
+    private Boolean placeBlocks;
     private GameMode gameMode;
-    private boolean lives;
+    private Boolean lives;
 
-    CustomGameType(String displayName, String signName, boolean playerVersusPlayer, boolean friendlyFire, boolean mobWaves, boolean rewards,
-            boolean showTime, boolean breakBlocks, boolean breakPlacedBlocks, boolean placeBlocks, GameMode gameMode, boolean lives) {
+    CustomGameType(String displayName, String signName, Boolean playerVersusPlayer, Boolean friendlyFire, Boolean mobWaves, Boolean rewards,
+            Boolean showTime, Boolean breakBlocks, Boolean breakPlacedBlocks, Boolean placeBlocks, GameMode gameMode, Boolean lives) {
         this.displayName = displayName;
         this.signName = signName;
         this.playerVersusPlayer = playerVersusPlayer;
@@ -75,82 +75,82 @@ public void setSignName(String signName) {
     }
 
     @Override
-    public boolean isPlayerVersusPlayer() {
+    public Boolean isPlayerVersusPlayer() {
         return playerVersusPlayer;
     }
 
     @Override
-    public void setPlayerVersusPlayer(boolean playerVersusPlayer) {
+    public void setPlayerVersusPlayer(Boolean playerVersusPlayer) {
         this.playerVersusPlayer = playerVersusPlayer;
     }
 
     @Override
-    public boolean isFriendlyFire() {
+    public Boolean isFriendlyFire() {
         return friendlyFire;
     }
 
     @Override
-    public void setFriendlyFire(boolean friendlyFire) {
+    public void setFriendlyFire(Boolean friendlyFire) {
         this.friendlyFire = friendlyFire;
     }
 
     @Override
-    public boolean hasMobWaves() {
+    public Boolean hasMobWaves() {
         return mobWaves;
     }
 
     @Override
-    public void setMobWaves(boolean mobWaves) {
+    public void setMobWaves(Boolean mobWaves) {
         this.mobWaves = mobWaves;
     }
 
     @Override
-    public boolean hasRewards() {
+    public Boolean hasRewards() {
         return rewards;
     }
 
     @Override
-    public void setRewards(boolean rewards) {
+    public void setRewards(Boolean rewards) {
         this.rewards = rewards;
     }
 
     @Override
-    public boolean getShowTime() {
+    public Boolean getShowTime() {
         return showTime;
     }
 
     @Override
-    public void setShowTime(boolean showTime) {
+    public void setShowTime(Boolean showTime) {
         this.showTime = showTime;
     }
 
     @Override
-    public boolean canBreakBlocks() {
+    public Boolean canBreakBlocks() {
         return breakBlocks;
     }
 
     @Override
-    public void setBreakBlocks(boolean breakBlocks) {
+    public void setBreakBlocks(Boolean breakBlocks) {
         this.breakBlocks = breakBlocks;
     }
 
     @Override
-    public boolean canBreakPlacedBlocks() {
+    public Boolean canBreakPlacedBlocks() {
         return breakPlacedBlocks;
     }
 
     @Override
-    public void setBreakPlacedBlocks(boolean breakPlacedBlocks) {
+    public void setBreakPlacedBlocks(Boolean breakPlacedBlocks) {
         this.breakPlacedBlocks = breakPlacedBlocks;
     }
 
     @Override
-    public boolean canPlaceBlocks() {
+    public Boolean canPlaceBlocks() {
         return placeBlocks;
     }
 
     @Override
-    public void setPlaceBlocks(boolean placeBlocks) {
+    public void setPlaceBlocks(Boolean placeBlocks) {
         this.placeBlocks = placeBlocks;
     }
 
@@ -165,12 +165,12 @@ public void setGameMode(GameMode gameMode) {
     }
 
     @Override
-    public boolean hasLives() {
+    public Boolean hasLives() {
         return lives;
     }
 
     @Override
-    public void setLives(boolean lives) {
+    public void setLives(Boolean lives) {
         this.lives = lives;
     }
 

From 914c31a92ffa9508b7bdb36c776f4d7ffff0c926 Mon Sep 17 00:00:00 2001
From: Daniel Saukel <sa.da@unitybox.de>
Date: Thu, 28 Jul 2016 17:02:10 +0200
Subject: [PATCH 5/5] Implement break / place whitelists

---
 .../dre2n/dungeonsxl/world/DGameWorld.java    | 41 ++++++++++++++++---
 1 file changed, 35 insertions(+), 6 deletions(-)

diff --git a/core/src/main/java/io/github/dre2n/dungeonsxl/world/DGameWorld.java b/core/src/main/java/io/github/dre2n/dungeonsxl/world/DGameWorld.java
index 3052f17f..cc441f7b 100644
--- a/core/src/main/java/io/github/dre2n/dungeonsxl/world/DGameWorld.java
+++ b/core/src/main/java/io/github/dre2n/dungeonsxl/world/DGameWorld.java
@@ -38,11 +38,15 @@
 import io.github.dre2n.dungeonsxl.trigger.TriggerTypeDefault;
 import java.io.File;
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.CopyOnWriteArrayList;
 import org.bukkit.Chunk;
 import org.bukkit.Location;
+import org.bukkit.Material;
 import org.bukkit.World;
 import org.bukkit.block.Block;
 import org.bukkit.block.Sign;
@@ -470,12 +474,31 @@ public boolean onBreak(Player player, Block block) {
         }
 
         Game game = getGame();
-        if (game != null) {
-            GameRules rules = game.getRules();
-            if (rules.canBreakBlocks()) {
-                return (false);
-            } else if (rules.canBreakPlacedBlocks()) {
+        if (game == null) {
+            return true;
+        }
+
+        GameRules rules = game.getRules();
+        if (!rules.canBreakBlocks() && !rules.canBreakPlacedBlocks()) {
+            return true;
+        }
+
+        Map<Material, HashSet<Material>> whitelist = rules.getBreakWhitelist();
+        Material material = block.getType();
+        Material breakTool = player.getItemInHand().getType();
+
+        if (whitelist == null) {
+            if (rules.canBreakPlacedBlocks()) {
+                return (!placedBlocks.contains(block));
+            } else if (rules.canBreakBlocks()) {
+                return false;
+            }
+
+        } else if (whitelist.containsKey(material) && whitelist.get(material) == null | whitelist.get(material).isEmpty() | whitelist.get(material).contains(breakTool)) {
+            if (rules.canBreakPlacedBlocks()) {
                 return (!placedBlocks.contains(block));
+            } else if (rules.canBreakBlocks()) {
+                return false;
             }
         }
 
@@ -501,7 +524,13 @@ public boolean onPlace(Player player, Block block, Block against, ItemStack hand
             return true;
         }
 
-        if (game.getRules().canPlaceBlocks() || GamePlaceableBlock.canBuildHere(block, block.getFace(against), hand.getType(), this)) {
+        GameRules rules = game.getRules();
+        if (!rules.canPlaceBlocks() && !GamePlaceableBlock.canBuildHere(block, block.getFace(against), hand.getType(), this)) {
+            return true;
+        }
+
+        Set<Material> whitelist = rules.getPlaceWhitelist();
+        if (whitelist == null || whitelist.contains(block.getType())) {
             placedBlocks.add(block);
             return false;
         }