From 7118251fdbefc74744ca118050f1ea1892faf1c0 Mon Sep 17 00:00:00 2001
From: SergeyDertan <superator2018@gmail.com>
Date: Fri, 19 Apr 2019 17:33:21 +0300
Subject: [PATCH] Nether portal flag Prevents nether portal from being created
 in region Already created portal will work

---
 README.md                                     |  3 +-
 pom.xml                                       |  2 +-
 .../Event/RegionEventsHandler.java            | 97 ++++++++++++++++++-
 .../SRegionProtector/GUI/Page/FlagsPage.java  |  2 +
 .../Provider/DataObject/Converter.java        | 23 ++---
 .../Region/Flags/RegionFlags.java             | 13 ++-
 .../Region/RegionManager.java                 |  6 +-
 .../SRegionProtector/Utils/Pair.java          | 17 ++++
 src/main/resources/config.yml                 |  1 +
 src/main/resources/lang/eng.yml               |  3 +-
 src/main/resources/lang/rus.yml               |  3 +-
 src/main/resources/plugin.yml                 |  4 +-
 src/main/resources/region-settings.yml        |  2 +
 13 files changed, 148 insertions(+), 28 deletions(-)

diff --git a/README.md b/README.md
index 3bcdb6f..5c3f326 100644
--- a/README.md
+++ b/README.md
@@ -2,9 +2,10 @@
 SRegionProtector is a nukkit plugin that allows players to protect their regions.
 ## Download
 * [NukkitX.com](https://nukkitx.com/resources/sregionprotector.164/)
-* [GoogleDrive](https://drive.google.com/file/d/1eCtg4VsgBcNEMaApQJRn5yW5YP_fAUd9/view?usp=sharing)
+* [GoogleDrive](https://drive.google.com/file/d/1aQJ1U2J_toxUKHs266ikN0MJE8jTwcOq/view?usp=sharing)
 * [All versions](https://drive.google.com/drive/folders/1Z98RAPGY-7NK49ktsBYkZgafUB50r7NC?usp=sharing)
 ## Features
+* GUI
 * API for another plugins
 * Lots of flags
 * Flexible settings
diff --git a/pom.xml b/pom.xml
index e1b03b4..4b7e4df 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
 
     <groupId>sergeydertan.sregionprotector</groupId>
     <artifactId>SRegionProtector</artifactId>
-    <version>19.2</version>
+    <version>19.3</version>
     <name>SRegionProtector</name>
     <description>Flexible region protection plugin for nukkit</description>
     <url>https://github.com/SergeyDertan/SRegionProtector</url>
diff --git a/src/main/java/Sergey_Dertan/SRegionProtector/Event/RegionEventsHandler.java b/src/main/java/Sergey_Dertan/SRegionProtector/Event/RegionEventsHandler.java
index 96203b2..8d5d1b4 100644
--- a/src/main/java/Sergey_Dertan/SRegionProtector/Event/RegionEventsHandler.java
+++ b/src/main/java/Sergey_Dertan/SRegionProtector/Event/RegionEventsHandler.java
@@ -5,6 +5,7 @@
 import Sergey_Dertan.SRegionProtector.Region.Chunk.ChunkManager;
 import Sergey_Dertan.SRegionProtector.Region.Flags.RegionFlags;
 import Sergey_Dertan.SRegionProtector.Region.Region;
+import Sergey_Dertan.SRegionProtector.Utils.Pair;
 import Sergey_Dertan.SRegionProtector.Utils.Tags;
 import cn.nukkit.Player;
 import cn.nukkit.block.*;
@@ -27,15 +28,16 @@
 import cn.nukkit.event.redstone.RedstoneUpdateEvent;
 import cn.nukkit.event.weather.LightningStrikeEvent;
 import cn.nukkit.item.ItemID;
+import cn.nukkit.level.EnumLevel;
 import cn.nukkit.level.Position;
 import cn.nukkit.level.Sound;
+import cn.nukkit.level.format.FullChunk;
 import cn.nukkit.math.BlockFace;
 import cn.nukkit.math.Vector3;
 import it.unimi.dsi.fastutil.objects.Object2BooleanArrayMap;
 import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
 
-import java.util.Collection;
-import java.util.Iterator;
+import java.util.*;
 
 @SuppressWarnings({"WeakerAccess", "unused"})
 public final class RegionEventsHandler implements Listener {
@@ -50,6 +52,9 @@ public final class RegionEventsHandler implements Listener {
     private final Object2BooleanMap<Class> isMonster;
     private final Class monster; //mobplugin
 
+    private final Pair<Vector3, Integer>[] portalBlocks;
+
+    @SuppressWarnings("unchecked")
     public RegionEventsHandler(ChunkManager chunkManager, boolean[] flagsStatus, boolean[] needMessage, boolean prioritySystem) {
         this.chunkManager = chunkManager;
         this.flagsStatus = flagsStatus;
@@ -64,6 +69,60 @@ public RegionEventsHandler(ChunkManager chunkManager, boolean[] flagsStatus, boo
         } catch (ClassNotFoundException ignore) {
         }
         this.monster = monster;
+
+        this.portalBlocks = this.netherPortalBlocks();
+    }
+
+    /**
+     * @see BlockNetherPortal#spawnPortal(Position)
+     */
+    private Pair[] netherPortalBlocks() {
+        Map<Vector3, Integer> blocks = new HashMap<>();
+
+        blocks.put(new Vector3(1), BlockID.OBSIDIAN);
+        blocks.put(new Vector3(2), BlockID.OBSIDIAN);
+
+        //z=1
+        blocks.put(new Vector3(0, 0, 1), BlockID.OBSIDIAN);
+        blocks.put(new Vector3(1, 0, 1), BlockID.OBSIDIAN);
+        blocks.put(new Vector3(2, 0, 1), BlockID.OBSIDIAN);
+        blocks.put(new Vector3(3, 0, 1), BlockID.OBSIDIAN);
+        //z=2
+        blocks.put(new Vector3(1, 0, 2), BlockID.OBSIDIAN);
+        blocks.put(new Vector3(2, 0, 2), BlockID.OBSIDIAN);
+        //z=1
+        //y=1
+        blocks.put(new Vector3(0, 1, 1), BlockID.OBSIDIAN);
+        blocks.put(new Vector3(1, 1, 1), BlockID.NETHER_PORTAL);
+        blocks.put(new Vector3(2, 1, 1), BlockID.NETHER_PORTAL);
+        blocks.put(new Vector3(3, 1, 1), BlockID.OBSIDIAN);
+        //y=2
+        //z=1
+        blocks.put(new Vector3(0, 2, 1), BlockID.OBSIDIAN);
+        blocks.put(new Vector3(1, 2, 1), BlockID.NETHER_PORTAL);
+        blocks.put(new Vector3(2, 2, 1), BlockID.NETHER_PORTAL);
+        blocks.put(new Vector3(3, 2, 1), BlockID.OBSIDIAN);
+        //y=3
+        blocks.put(new Vector3(0, 3, 1), BlockID.OBSIDIAN);
+        blocks.put(new Vector3(1, 3, 1), BlockID.NETHER_PORTAL);
+        blocks.put(new Vector3(2, 3, 1), BlockID.NETHER_PORTAL);
+        blocks.put(new Vector3(3, 3, 1), BlockID.OBSIDIAN);
+        //y=4
+        blocks.put(new Vector3(0, 4, 1), BlockID.OBSIDIAN);
+        blocks.put(new Vector3(1, 4, 1), BlockID.OBSIDIAN);
+        blocks.put(new Vector3(2, 4, 1), BlockID.OBSIDIAN);
+        blocks.put(new Vector3(3, 4, 1), BlockID.OBSIDIAN);
+
+        for (int x = -1; x < 4; x++) {
+            for (int y = 1; y < 4; y++) {
+                for (int z = -1; z < 3; z++) {
+                    blocks.putIfAbsent(new Vector3(x, y, z), BlockID.AIR);
+                }
+            }
+        }
+        List<Pair<Vector3, Integer>> blockss = new ArrayList<>();
+        blocks.forEach((k, v) -> blockss.add(new Pair<>(k, v)));
+        return blockss.toArray(new Pair[0]);
     }
 
     //break & minefarm flags
@@ -355,6 +414,40 @@ public void levelLoad(LevelLoadEvent e) {
         }
     }
 
+    //prevent nether portal from spawning in region
+    @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
+    public void entityPortalEnter(EntityPortalEnterEvent e) {
+        if (!this.flagsStatus[RegionFlags.FLAG_NETHER_PORTAL]) return;
+        if (e.getPortalType() != EntityPortalEnterEvent.PortalType.NETHER) return;
+        Position portal = EnumLevel.moveToNether(e.getEntity()).floor();
+        if (portal == null) return;
+
+        for (int x = -1; x < 2; x++) {
+            for (int z = -1; z < 2; z++) {
+                int chunkX = (portal.getFloorX() >> 4) + x;
+                int chunkZ = (portal.getFloorZ() >> 4) + z;
+                FullChunk chunk = portal.level.getChunk(chunkX, chunkZ, true);
+                if (chunk == null || !(chunk.isGenerated() || chunk.isPopulated())) {
+                    portal.level.generateChunk(chunkX, chunkZ, true);
+                }
+            }
+        }
+
+        for (Pair<Vector3, Integer> block : this.portalBlocks) {
+            Vector3 pos = portal.add(block.key).floor();
+            if (portal.level.getBlockIdAt((int) pos.x, (int) pos.y, (int) pos.z) != block.value) {
+                Region region = this.chunkManager.getRegion(pos, portal.level.getName());
+                if (region != null && region.getFlagState(RegionFlags.FLAG_NETHER_PORTAL)) {
+                    e.setCancelled();
+                    if (e.getEntity() instanceof Player) {
+                        Messenger.getInstance().sendMessage(((Player) e.getEntity()), "region.protected." + RegionFlags.getFlagName(RegionFlags.FLAG_NETHER_PORTAL));
+                    }
+                    break;
+                }
+            }
+        }
+    }
+
     private boolean canInteractWith(int flag, Position pos, Player player) {
         if (!this.flagsStatus[flag]) return false;
         Chunk chunk = this.chunkManager.getChunk((long) pos.x, (long) pos.z, pos.level.getName(), true, false);
diff --git a/src/main/java/Sergey_Dertan/SRegionProtector/GUI/Page/FlagsPage.java b/src/main/java/Sergey_Dertan/SRegionProtector/GUI/Page/FlagsPage.java
index 34b83ab..63743ea 100644
--- a/src/main/java/Sergey_Dertan/SRegionProtector/GUI/Page/FlagsPage.java
+++ b/src/main/java/Sergey_Dertan/SRegionProtector/GUI/Page/FlagsPage.java
@@ -49,6 +49,8 @@ public final class FlagsPage implements Page {
         this.flagToBlock[RegionFlags.FLAG_MINEFARM] = BlockID.DIAMOND_ORE;
         this.flagToBlock[RegionFlags.FLAG_POTION_LAUNCH] = ItemID.SPLASH_POTION;
         this.flagToBlock[RegionFlags.FLAG_HEAL] = ItemID.GOLDEN_APPLE;
+        this.flagToBlock[RegionFlags.FLAG_NETHER_PORTAL] = BlockID.NETHER_PORTAL;
+        this.flagToBlock[RegionFlags.FLAG_SELL] = ItemID.EMERALD;
     }
 
     @Override
diff --git a/src/main/java/Sergey_Dertan/SRegionProtector/Provider/DataObject/Converter.java b/src/main/java/Sergey_Dertan/SRegionProtector/Provider/DataObject/Converter.java
index d0402c6..0df507f 100644
--- a/src/main/java/Sergey_Dertan/SRegionProtector/Provider/DataObject/Converter.java
+++ b/src/main/java/Sergey_Dertan/SRegionProtector/Provider/DataObject/Converter.java
@@ -3,15 +3,12 @@
 import Sergey_Dertan.SRegionProtector.Region.Flags.Flag.RegionFlag;
 import Sergey_Dertan.SRegionProtector.Region.Flags.Flag.RegionSellFlag;
 import Sergey_Dertan.SRegionProtector.Region.Flags.Flag.RegionTeleportFlag;
-import Sergey_Dertan.SRegionProtector.Region.Flags.RegionFlags;
 import Sergey_Dertan.SRegionProtector.Region.Region;
 import Sergey_Dertan.SRegionProtector.Utils.Utils;
 import cn.nukkit.math.Vector3;
 import com.alibaba.fastjson.JSON;
 
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.*;
 
 import static Sergey_Dertan.SRegionProtector.Region.Flags.RegionFlags.*;
 import static Sergey_Dertan.SRegionProtector.Utils.Tags.*;
@@ -95,11 +92,11 @@ public static Region fromDataObject(RegionDataObject dataObject, RegionFlag[] fl
     }
 
     public static Region fromDataObject(RegionDataObject dataObject, FlagListDataObject flagsDataObject) {
-        return fromDataObject(dataObject, fromDataObject(flagsDataObject));
+        return fromDataObject(dataObject, fromDataObject(flagsDataObject).toArray(new RegionFlag[0]));
     }
 
-    public static RegionFlag[] fromDataObject(FlagListDataObject dataObject) {
-        RegionFlag[] flags = new RegionFlag[RegionFlags.FLAG_AMOUNT];
+    public static List<RegionFlag> fromDataObject(FlagListDataObject dataObject) {
+        List<RegionFlag> flags = new ArrayList<>();
         Boolean[] state = JSON.parseArray(dataObject.state, Boolean.class).toArray(new Boolean[0]);
         @SuppressWarnings("unchecked")
         Map<String, Object> teleportData = (Map<String, Object>) JSON.parse(dataObject.teleportData);
@@ -109,24 +106,24 @@ public static RegionFlag[] fromDataObject(FlagListDataObject dataObject) {
                 double y = ((Number) teleportData.get(Y_TAG)).doubleValue();
                 double z = ((Number) teleportData.get(Z_TAG)).doubleValue();
                 String level = (String) teleportData.get(LEVEL_TAG);
-                flags[i] = new RegionTeleportFlag(state[i], new Vector3(x, y, z), level);
+                flags.add(new RegionTeleportFlag(state[i], new Vector3(x, y, z), level));
                 continue;
             }
             if (i == FLAG_SELL) {
-                flags[i] = new RegionSellFlag(state[i], dataObject.sellData);
+                flags.add(new RegionSellFlag(state[i], dataObject.sellData));
                 continue;
             }
-            flags[i] = new RegionFlag(state[i]);
+            flags.add(new RegionFlag(state[i]));
         }
         return flags;
     }
 
     public static FlagListDataObject toDataObject(Map<String, Map<String, Object>> data) { //for the yaml data provider
         FlagListDataObject dataObject = new FlagListDataObject();
-        boolean[] state = new boolean[RegionFlags.FLAG_AMOUNT];
+        List<Boolean> state = new ArrayList<>();
         for (Map.Entry<String, Map<String, Object>> flag : data.entrySet()) {
             if (getFlagId(flag.getKey()) == FLAG_INVALID) continue;
-            state[getFlagId(flag.getKey())] = (Boolean) flag.getValue().get(STATE_TAG);
+            state.add(getFlagId(flag.getKey()), (Boolean) flag.getValue().get(STATE_TAG));
             if (getFlagId(flag.getKey()) == FLAG_SELL) {
                 dataObject.sellData = ((Number) flag.getValue().getOrDefault(PRICE_TAG, -1L)).longValue();
             }
@@ -141,7 +138,7 @@ public static FlagListDataObject toDataObject(Map<String, Map<String, Object>> d
                 dataObject.teleportData = JSON.toJSONString(teleport);
             }
         }
-        dataObject.state = JSON.toJSONString(state);
+        dataObject.state = JSON.toJSONString(state.toArray(new Boolean[0]));
         return dataObject;
     }
 }
diff --git a/src/main/java/Sergey_Dertan/SRegionProtector/Region/Flags/RegionFlags.java b/src/main/java/Sergey_Dertan/SRegionProtector/Region/Flags/RegionFlags.java
index 90bd06a..683de96 100644
--- a/src/main/java/Sergey_Dertan/SRegionProtector/Region/Flags/RegionFlags.java
+++ b/src/main/java/Sergey_Dertan/SRegionProtector/Region/Flags/RegionFlags.java
@@ -14,6 +14,7 @@
 
 import java.util.Arrays;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 @SuppressWarnings({"WeakerAccess", "unused"})
@@ -57,8 +58,9 @@ public abstract class RegionFlags {
     public static final int FLAG_SMART_DOORS = 31;
     public static final int FLAG_MINEFARM = 32;
     public static final int FLAG_FALL_DAMAGE = 33;
+    public static final int FLAG_NETHER_PORTAL = 34;
 
-    public static final int FLAG_AMOUNT = 34;
+    public static final int FLAG_AMOUNT = 35;
 
     public static final RegionFlag[] defaults = new RegionFlag[FLAG_AMOUNT];
     public static final Permission[] permissions = new Permission[FLAG_AMOUNT];
@@ -103,6 +105,7 @@ public abstract class RegionFlags {
         flagList.put(FLAG_SMART_DOORS, "smart-doors");
         flagList.put(FLAG_MINEFARM, "minefarm");
         flagList.put(FLAG_FALL_DAMAGE, "fall-damage");
+        flagList.put(FLAG_NETHER_PORTAL, "nether-portal");
         flags = ImmutableBiMap.copyOf(flagList);
 
         Map<String, Integer> aAliases = new HashMap<>(FLAG_AMOUNT);
@@ -150,6 +153,7 @@ public abstract class RegionFlags {
         fState.put(FLAG_SMART_DOORS, false);
         fState.put(FLAG_MINEFARM, false);
         fState.put(FLAG_FALL_DAMAGE, false);
+        fState.put(FLAG_NETHER_PORTAL, true);
 
         state = ImmutableMap.copyOf(fState);
     }
@@ -190,10 +194,9 @@ public static boolean getStateFromString(String state, int flag) {
         throw new RuntimeException("Wrong state");
     }
 
-    public static void fixMissingFlags(RegionFlag[] flags) {
-        for (int i = 0; i < FLAG_AMOUNT; ++i) {
-            if (flags[i] != null) continue;
-            flags[i] = defaults[i].clone();
+    public static void fixMissingFlags(List<RegionFlag> flags) {
+        for (int i = flags.size(); i < FLAG_AMOUNT; ++i) {
+            flags.add(defaults[i].clone());
         }
     }
 
diff --git a/src/main/java/Sergey_Dertan/SRegionProtector/Region/RegionManager.java b/src/main/java/Sergey_Dertan/SRegionProtector/Region/RegionManager.java
index 1665a9f..3777f44 100644
--- a/src/main/java/Sergey_Dertan/SRegionProtector/Region/RegionManager.java
+++ b/src/main/java/Sergey_Dertan/SRegionProtector/Region/RegionManager.java
@@ -93,15 +93,15 @@ public void init() {
 
             FlagListDataObject flags = this.provider.loadFlags(name);
 
-            RegionFlag[] flagList = Converter.fromDataObject(flags);
+            List<RegionFlag> flagList = Converter.fromDataObject(flags);
 
             boolean needUpdate = false;
-            if (flagList.length < FLAG_AMOUNT) {
+            if (flagList.size() < FLAG_AMOUNT) {
                 needUpdate = true;
                 fixMissingFlags(flagList);
             }
 
-            Region region = new Region(name, creator, level, priority, minX, minY, minZ, maxX, maxY, maxZ, owners, members, flagList);
+            Region region = new Region(name, creator, level, priority, minX, minY, minZ, maxX, maxY, maxZ, owners, members, flagList.toArray(new RegionFlag[0]));
 
             region.needUpdate = needUpdate;
 
diff --git a/src/main/java/Sergey_Dertan/SRegionProtector/Utils/Pair.java b/src/main/java/Sergey_Dertan/SRegionProtector/Utils/Pair.java
index a782592..618b26e 100644
--- a/src/main/java/Sergey_Dertan/SRegionProtector/Utils/Pair.java
+++ b/src/main/java/Sergey_Dertan/SRegionProtector/Utils/Pair.java
@@ -1,5 +1,7 @@
 package Sergey_Dertan.SRegionProtector.Utils;
 
+import java.util.Objects;
+
 public final class Pair<F, S> {
 
     public F key;
@@ -9,4 +11,19 @@ public Pair(F key, S value) {
         this.key = key;
         this.value = value;
     }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) return true;
+        if (obj instanceof Pair) {
+            Pair pair = (Pair) obj;
+            return Objects.equals(this.key, pair.key) && Objects.equals(this.value, pair.value);
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return (this.key != null ? this.key.hashCode() : 0) * 13 + (this.value != null ? this.value.hashCode() : 0);
+    }
 }
diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml
index a388458..8fbdbc6 100644
--- a/src/main/resources/config.yml
+++ b/src/main/resources/config.yml
@@ -57,6 +57,7 @@ active-flags:
   smart-doors: true
   minefarm: true
   fall-damage: true
+  nether-portal: true
 display:
   place: true
   break: true
diff --git a/src/main/resources/lang/eng.yml b/src/main/resources/lang/eng.yml
index 7184cd6..d58a9c8 100644
--- a/src/main/resources/lang/eng.yml
+++ b/src/main/resources/lang/eng.yml
@@ -53,7 +53,8 @@ region.protected.ender-pearl: "You can`t use enderpearl in this region"
 region.protected.chest-access: "You can`t use chests in this region"
 region.protected.sleep: "You can`t use bed in this region"
 region.protected.fire: "You can`t ignite blocks in this region"
-region.protected.fall_damage: "You can`t take fall damage in this region"
+region.protected.fall-damage: "You can`t take fall damage in this region"
+region.protected.nether-portal: "Nether portal can`t be created in region"
 
 region.selection.pos1: "First pos set"
 region.selection.pos2: "Second pos set"
diff --git a/src/main/resources/lang/rus.yml b/src/main/resources/lang/rus.yml
index a1b2b37..803f378 100644
--- a/src/main/resources/lang/rus.yml
+++ b/src/main/resources/lang/rus.yml
@@ -53,7 +53,8 @@ region.protected.ender-pearl: "Вы не можете использовать 
 region.protected.chest-access: "Вы не можете использовать сундуки в этом регионе"
 region.protected.sleep: "Вы не можете использовать кровать в этом регионе"
 region.protected.fire: "Вы не можете поджигать в этом регионе"
-region.protected.fall_damage: "Вы не можете получать урон от падения в этом регионе"
+region.protected.fall-damage: "Вы не можете получать урон от падения в этом регионе"
+region.protected.nether-portal: "Портал не может быть создан в этом регионе"
 
 region.selection.pos1: "Первая точка установлена"
 region.selection.pos2: "Вторая точка установлена"
diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml
index 5ed09c7..26d03fa 100644
--- a/src/main/resources/plugin.yml
+++ b/src/main/resources/plugin.yml
@@ -161,8 +161,10 @@ permissions:
               sregionprotector.region.flag.chunk_loader:
                 default: op
               sregionprotector.region.flag.smart_doors:
-                default: op
+                default: true
               sregionprotector.region.flag.minefarm:
                 default: op
               sregionprotector.region.flag.fall_damage:
                 default: op
+              sregionprotector.region.flag.nether_portal:
+                default: op
diff --git a/src/main/resources/region-settings.yml b/src/main/resources/region-settings.yml
index 2f40eeb..803d7ae 100644
--- a/src/main/resources/region-settings.yml
+++ b/src/main/resources/region-settings.yml
@@ -32,6 +32,7 @@ default-flags:
   smart-doors: true
   minefarm: false
   fall-damage: false
+  nether-portal: true
 need-message:
   move: false
   place: true
@@ -55,6 +56,7 @@ need-message:
   sleep: true
   minefarm: false
   fall-damage: false
+  nether-portal: true
 max-region-name-length: 10
 min-region-name-length: 3
 # 1 second = 20 ticks