diff --git a/src/de/craftinc/gates/GateChangeListener.java b/src/de/craftinc/gates/GateChangeListener.java
deleted file mode 100644
index 7e0eae9..0000000
--- a/src/de/craftinc/gates/GateChangeListener.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*  Craft Inc. Gates
-    Copyright (C) 2011-2014 Craft Inc. Gates Team (see AUTHORS.txt)
-
-    This program is free software: you can redistribute it and/or modify
-    it under the terms of the GNU Lesser General Public License as published
-    by the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU Lesser General Public License for more details.
-
-    You should have received a copy of the GNU Lesser General Public License
-    along with this program (LGPLv3).  If not, see .
-*/
-package de.craftinc.gates;
-
-
-import java.util.Map;
-
-public interface GateChangeListener {
-    String newGate = "GateChangeListener-newGate"; // value will be null
-    String removedGate = "GateChangeListener-removedGate"; // value will be null
-    String changedID = "GateChangeListener-changedID"; // value will be the old ID
-    String changedLocation = "GateChangeListener-changedLocation"; // value will the old location
-    String changedExit = "GateChangeListener-changedExit"; // value will be the old exit
-
-    void gateChangedHandler(final Gate g, final Map changeSet);
-}
diff --git a/src/de/craftinc/gates/Plugin.java b/src/de/craftinc/gates/Plugin.java
index d14e7d5..263aa2e 100644
--- a/src/de/craftinc/gates/Plugin.java
+++ b/src/de/craftinc/gates/Plugin.java
@@ -21,8 +21,10 @@ import java.util.*;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
-
+import de.craftinc.gates.controllers.GatesManager;
 import de.craftinc.gates.listeners.*;
+import de.craftinc.gates.models.Gate;
+import de.craftinc.gates.controllers.PermissionController;
 import de.craftinc.gates.util.ConfigurationUtil;
 import net.milkbowl.vault.permission.Permission;
 
@@ -37,50 +39,46 @@ import org.bukkit.plugin.java.JavaPlugin;
 import de.craftinc.gates.commands.*;
 import org.mcstats.Metrics;
 
-
 public class Plugin extends JavaPlugin {
-    public static final String permissionInfo = "craftincgates.info";
-    public static final String permissionManage = "craftincgates.manage";
-    public static final String permissionUse = "craftincgates.use";
-
     private static Plugin instance;
-    private static Permission permission;
 
     private String baseCommand;
     protected List commands = new ArrayList<>();
+
     private GatesManager gatesManager = new GatesManager();
 
-    private PlayerMoveListener moveListener = new PlayerMoveListener();
+    private PermissionController permissionController = new PermissionController();
+
+    private PlayerMoveListener moveListener;
+    private VehicleMoveListener vehicleListener;
     private PlayerTeleportListener teleportListener = new PlayerTeleportListener();
     private PlayerRespawnListener respawnListener = new PlayerRespawnListener();
     private PlayerChangedWorldListener worldChangeListener = new PlayerChangedWorldListener();
     private PlayerJoinListener joinListener = new PlayerJoinListener();
     private BlockBreakListener blockBreakListener = new BlockBreakListener();
 
-
     public Plugin() {
         instance = this;
+        moveListener = new PlayerMoveListener(this);
+        vehicleListener = new VehicleMoveListener(this);
     }
 
-
     public static Plugin getPlugin() {
         return instance;
     }
 
-
     public GatesManager getGatesManager() {
         return gatesManager;
     }
 
-
     @Override
     public void onLoad() {
         ConfigurationSerialization.registerClass(Gate.class);
     }
 
-
     private void setupPermissions() {
         if (getServer().getPluginManager().getPlugin("Vault") == null) {
+            log("Not using setup permission provider provided by Vault.");
             return;
         }
 
@@ -88,13 +86,12 @@ public class Plugin extends JavaPlugin {
 
         if (rsp != null) {
             log("Using permission provider provided by Vault.");
-            permission = rsp.getProvider();
+            permissionController.setPermission(rsp.getProvider());
         } else {
             log("Not using setup permission provider provided by Vault.");
         }
     }
 
-
     @Override
     public void onDisable() {
         // Save gates
@@ -103,7 +100,6 @@ public class Plugin extends JavaPlugin {
         log("Disabled");
     }
 
-
     @Override
     public void onEnable() {
         // Setup Metrics
@@ -153,6 +149,9 @@ public class Plugin extends JavaPlugin {
         }
     }
 
+    public PermissionController getPermissionController() {
+        return permissionController;
+    }
 
     private void registerEventListeners() {
         PluginManager pm = this.getServer().getPluginManager();
@@ -162,6 +161,7 @@ public class Plugin extends JavaPlugin {
         pm.registerEvents(this.respawnListener, this);
         pm.registerEvents(this.worldChangeListener, this);
         pm.registerEvents(this.joinListener, this);
+        pm.registerEvents(this.vehicleListener, this);
 
         if (getConfig().getBoolean(ConfigurationUtil.confCheckForBrokenGateFramesKey)) {
             pm.registerEvents(this.blockBreakListener, this);
@@ -224,9 +224,4 @@ public class Plugin extends JavaPlugin {
     public static void log(Level level, String msg) {
         Logger.getLogger("Minecraft").log(level, "[" + instance.getDescription().getFullName() + "] " + msg);
     }
-
-
-    public static Permission getPermission() {
-        return permission;
-    }
 }
diff --git a/src/de/craftinc/gates/commands/BaseCommand.java b/src/de/craftinc/gates/commands/BaseCommand.java
index e3af472..a13a986 100644
--- a/src/de/craftinc/gates/commands/BaseCommand.java
+++ b/src/de/craftinc/gates/commands/BaseCommand.java
@@ -19,42 +19,48 @@ package de.craftinc.gates.commands;
 import java.util.ArrayList;
 import java.util.List;
 
+import de.craftinc.gates.controllers.PermissionController;
 import de.craftinc.gates.util.ConfigurationUtil;
 import org.bukkit.ChatColor;
 import org.bukkit.command.CommandSender;
 import org.bukkit.configuration.file.FileConfiguration;
 import org.bukkit.entity.Player;
 
-import de.craftinc.gates.Gate;
-import de.craftinc.gates.GatesManager;
+import de.craftinc.gates.models.Gate;
+import de.craftinc.gates.controllers.GatesManager;
 import de.craftinc.gates.Plugin;
 import de.craftinc.gates.util.TextUtil;
 
 public abstract class BaseCommand {
+    PermissionController permissionController;
 
-    protected List aliases = new ArrayList<>();
-    protected List requiredParameters = new ArrayList<>();
+    List aliases = new ArrayList<>();
+    List requiredParameters = new ArrayList<>();
     List optionalParameters = new ArrayList<>();
 
-    protected String helpDescription = "no description";
+    String helpDescription = "no description";
 
     List parameters;
     CommandSender sender;
-    protected Player player;
-    protected Gate gate;
+    Player player;
+    Gate gate;
 
-    protected boolean senderMustBePlayer = true;
+    boolean senderMustBePlayer = true;
     boolean hasGateParam = true;
 
-    protected String requiredPermission;
-    protected boolean needsPermissionAtCurrentLocation;
+    String requiredPermission;
+    boolean needsPermissionAtCurrentLocation;
 
-    protected boolean shouldPersistToDisk;
+    boolean shouldPersistToDisk;
 
     public List getAliases() {
         return aliases;
     }
 
+    public BaseCommand() {
+        permissionController = Plugin.getPlugin().getPermissionController();
+    }
+
     public void execute(CommandSender sender, List parameters) {
         this.sender = sender;
         this.parameters = parameters;
@@ -74,23 +80,72 @@ public abstract class BaseCommand {
         }
     }
 
-    private boolean getSaveOnChanges() {
-        FileConfiguration config = Plugin.getPlugin().getConfig();
-        return config.getBoolean(ConfigurationUtil.confSaveOnChangesKey);
-    }
-
     abstract protected void perform();
 
-    protected void sendMessage(String message) {
+    void sendMessage(String message) {
         sender.sendMessage(message);
     }
 
-    protected void sendMessage(List messages) {
+    void sendMessage(List messages) {
         for (String message : messages) {
             this.sendMessage(message);
         }
     }
 
+    boolean setGateUsingParameter(String param) {
+        GatesManager gateManager = Plugin.getPlugin().getGatesManager();
+
+        if (!gateManager.gateExists(param)) {
+            return false;
+        } else {
+            gate = gateManager.getGateWithId(param);
+            return true;
+        }
+    }
+
+    /**
+     * This will return false if a gate is required for this command but this.gate == null.
+     */
+    boolean hasPermission() {
+        if (needsPermissionAtCurrentLocation) {
+            return permissionController.hasPermission(sender, requiredPermission);
+        } else {
+            return permissionController.hasPermission(sender, gate, requiredPermission);
+        }
+    }
+
+    /*
+        Help and usage description
+    */
+
+    String getUsageTemplate(boolean withDescription) {
+        String ret = "";
+
+        ret += ChatColor.AQUA;
+        ret += "/" + Plugin.getPlugin().getBaseCommand() + " " + TextUtil.implode(getAliases(), ",") + " ";
+
+        List parts = new ArrayList<>();
+
+        for (String requiredParameter : requiredParameters) {
+            parts.add("[" + requiredParameter + "]");
+        }
+
+        for (String optionalParameter : optionalParameters) {
+            parts.add("*[" + optionalParameter + "]");
+        }
+
+
+        ret += ChatColor.DARK_AQUA;
+        ret += TextUtil.implode(parts, " ");
+
+        if (withDescription) {
+            ret += " ";
+            ret += ChatColor.YELLOW;
+            ret += helpDescription;
+        }
+        return ret;
+    }
+
     private boolean validateCall() {
         boolean allParametersThere = parameters.size() >= requiredParameters.size();
         boolean senderIsPlayer = this.sender instanceof Player;
@@ -110,7 +165,7 @@ public abstract class BaseCommand {
             if (!allParametersThere) {
                 sendMessage(ChatColor.RED + "Some parameters are missing! " +
                         ChatColor.AQUA + "Usage: " +
-                        this.getUsageTemplate()
+                        getUsageTemplate(false)
                 );
                 valid = false;
             } else if ((!senderHasPermission && this.hasGateParam) ||
@@ -130,110 +185,8 @@ public abstract class BaseCommand {
         return valid;
     }
 
-    boolean setGateUsingParameter(String param) {
-        GatesManager gateManager = Plugin.getPlugin().getGatesManager();
-
-        if (!gateManager.gateExists(param)) {
-            return false;
-        } else {
-            gate = gateManager.getGateWithId(param);
-            return true;
-        }
-    }
-
-    /**
-     * This will return false if a gate is required for this command but this.gate == null.
-     */
-    boolean hasPermission() {
-        if (Plugin.getPermission() == null) { // fallback - use the standard bukkit permission system
-            return this.sender.hasPermission(this.requiredPermission);
-        }
-
-        if (!(this.sender instanceof Player)) {
-            // sender is no player - there is no information about the senders locations
-            return Plugin.getPermission().has(this.sender, this.requiredPermission);
-        }
-
-
-        Player p = (Player) this.sender;
-        boolean hasPermission = false;
-
-        switch (this.requiredPermission) {
-            case Plugin.permissionInfo:
-
-                if (this.hasGateParam) {
-                    hasPermission = this.hasPermissionAtGateLocationAndExit(p);
-                } else {
-                    hasPermission = hasPermissionAtPlayerLocation(p);
-                }
-                break;
-            case Plugin.permissionUse:
-                hasPermission = this.hasPermissionAtGateLocationAndExit(p);
-                break;
-            case Plugin.permissionManage:
-
-                if (this.needsPermissionAtCurrentLocation && this.hasGateParam) {
-                    boolean hasPermissionAtCurrentLocation = hasPermissionAtPlayerLocation(p);
-                    hasPermission = hasPermissionAtCurrentLocation && this.hasPermissionAtGateLocationAndExit(p);
-                } else if (this.needsPermissionAtCurrentLocation) {
-                    hasPermission = hasPermissionAtPlayerLocation(p);
-                } else {
-                    hasPermission = this.hasPermissionAtGateLocationAndExit(p);
-                }
-                break;
-        }
-
-        return hasPermission;
-    }
-
-    private boolean hasPermissionAtGateLocationAndExit(Player p) {
-        if (this.gate == null || p == null) {
-            return false;
-        }
-
-        boolean permAtLocation = this.gate.getLocation() == null || Plugin.getPermission().has(this.gate.getLocation().getWorld(), p.getName(), this.requiredPermission);
-        boolean permAtExit = this.gate.getExit() == null || Plugin.getPermission().has(this.gate.getExit().getWorld(), p.getName(), this.requiredPermission);
-
-        return permAtLocation & permAtExit;
-    }
-
-    private boolean hasPermissionAtPlayerLocation(Player p) {
-        return Plugin.getPermission().has(p.getWorld(), p.getName(), this.requiredPermission);
-    }
-
-    /*
-        Help and usage description
-    */
-
-    String getUsageTemplate(boolean withDescription) {
-        String ret = "";
-
-        ret += ChatColor.AQUA;
-        ret += "/" + Plugin.getPlugin().getBaseCommand() + " " + TextUtil.implode(this.getAliases(), ",") + " ";
-
-        List parts = new ArrayList<>();
-
-        for (String requiredParameter : this.requiredParameters) {
-            parts.add("[" + requiredParameter + "]");
-        }
-
-        for (String optionalParameter : this.optionalParameters) {
-            parts.add("*[" + optionalParameter + "]");
-        }
-
-
-        ret += ChatColor.DARK_AQUA;
-        ret += TextUtil.implode(parts, " ");
-
-        if (withDescription) {
-            ret += " ";
-            ret += ChatColor.YELLOW;
-            ret += this.helpDescription;
-        }
-        return ret;
-    }
-
-    private String getUsageTemplate() {
-        return getUsageTemplate(false);
+    private boolean getSaveOnChanges() {
+        FileConfiguration config = Plugin.getPlugin().getConfig();
+        return config.getBoolean(ConfigurationUtil.confSaveOnChangesKey);
     }
 }
diff --git a/src/de/craftinc/gates/commands/CommandAllowRiding.java b/src/de/craftinc/gates/commands/CommandAllowRiding.java
index 070a257..9b42533 100644
--- a/src/de/craftinc/gates/commands/CommandAllowRiding.java
+++ b/src/de/craftinc/gates/commands/CommandAllowRiding.java
@@ -16,8 +16,7 @@
 */
 package de.craftinc.gates.commands;
 
-
-import de.craftinc.gates.Plugin;
+import de.craftinc.gates.controllers.PermissionController;
 import org.bukkit.ChatColor;
 
 public class CommandAllowRiding extends BaseCommand {
@@ -29,7 +28,7 @@ public class CommandAllowRiding extends BaseCommand {
         requiredParameters.add("id");
 
         helpDescription = "Allow players to travel while riding.";
-        requiredPermission = Plugin.permissionManage;
+        requiredPermission = PermissionController.permissionManage;
         needsPermissionAtCurrentLocation = false;
         shouldPersistToDisk = true;
 
diff --git a/src/de/craftinc/gates/commands/CommandClose.java b/src/de/craftinc/gates/commands/CommandClose.java
index 43858b7..3222fee 100644
--- a/src/de/craftinc/gates/commands/CommandClose.java
+++ b/src/de/craftinc/gates/commands/CommandClose.java
@@ -18,12 +18,12 @@ package de.craftinc.gates.commands;
 
 import java.util.logging.Level;
 
+import de.craftinc.gates.controllers.PermissionController;
 import de.craftinc.gates.util.GateBlockChangeSender;
 import org.bukkit.ChatColor;
 
 import de.craftinc.gates.Plugin;
 
-
 public class CommandClose extends BaseCommand {
 
     public CommandClose() {
@@ -32,7 +32,7 @@ public class CommandClose extends BaseCommand {
 
         requiredParameters.add("id");
         helpDescription = "Closes a gate to prevent players from using it.";
-        requiredPermission = Plugin.permissionManage;
+        requiredPermission = PermissionController.permissionManage;
         needsPermissionAtCurrentLocation = false;
         shouldPersistToDisk = true;
         senderMustBePlayer = false;
diff --git a/src/de/craftinc/gates/commands/CommandDenyRiding.java b/src/de/craftinc/gates/commands/CommandDenyRiding.java
index 76f5dba..143b383 100644
--- a/src/de/craftinc/gates/commands/CommandDenyRiding.java
+++ b/src/de/craftinc/gates/commands/CommandDenyRiding.java
@@ -16,7 +16,7 @@
 */
 package de.craftinc.gates.commands;
 
-import de.craftinc.gates.Plugin;
+import de.craftinc.gates.controllers.PermissionController;
 import org.bukkit.ChatColor;
 
 public class CommandDenyRiding extends BaseCommand {
@@ -27,7 +27,7 @@ public class CommandDenyRiding extends BaseCommand {
 
         requiredParameters.add("id");
         helpDescription = "Deny players to travel while riding.";
-        requiredPermission = Plugin.permissionManage;
+        requiredPermission = PermissionController.permissionManage;
         needsPermissionAtCurrentLocation = false;
         shouldPersistToDisk = true;
         senderMustBePlayer = false;
diff --git a/src/de/craftinc/gates/commands/CommandExit.java b/src/de/craftinc/gates/commands/CommandExit.java
index 4250291..ec6939e 100644
--- a/src/de/craftinc/gates/commands/CommandExit.java
+++ b/src/de/craftinc/gates/commands/CommandExit.java
@@ -18,6 +18,7 @@ package de.craftinc.gates.commands;
 
 import java.util.logging.Level;
 
+import de.craftinc.gates.controllers.PermissionController;
 import de.craftinc.gates.util.GateBlockChangeSender;
 import org.bukkit.ChatColor;
 
@@ -34,7 +35,7 @@ public class CommandExit extends BaseCommand {
 
         helpDescription = "Change exit of location.";
 
-        requiredPermission = Plugin.permissionManage;
+        requiredPermission = PermissionController.permissionManage;
 
         needsPermissionAtCurrentLocation = true;
         shouldPersistToDisk = true;
diff --git a/src/de/craftinc/gates/commands/CommandExitOpen.java b/src/de/craftinc/gates/commands/CommandExitOpen.java
index 5bc2963..8c4ccd5 100644
--- a/src/de/craftinc/gates/commands/CommandExitOpen.java
+++ b/src/de/craftinc/gates/commands/CommandExitOpen.java
@@ -17,6 +17,7 @@
 package de.craftinc.gates.commands;
 
 
+import de.craftinc.gates.controllers.PermissionController;
 import de.craftinc.gates.Plugin;
 import de.craftinc.gates.util.GateBlockChangeSender;
 import org.bukkit.ChatColor;
@@ -32,7 +33,7 @@ public class CommandExitOpen extends BaseCommand {
 
         requiredParameters.add("id");
         helpDescription = "Change exit of location and open that gate afterwards.";
-        requiredPermission = Plugin.permissionManage;
+        requiredPermission = PermissionController.permissionManage;
         needsPermissionAtCurrentLocation = true;
         shouldPersistToDisk = true;
         senderMustBePlayer = true;
diff --git a/src/de/craftinc/gates/commands/CommandHelp.java b/src/de/craftinc/gates/commands/CommandHelp.java
index f774d63..f637c8d 100644
--- a/src/de/craftinc/gates/commands/CommandHelp.java
+++ b/src/de/craftinc/gates/commands/CommandHelp.java
@@ -16,7 +16,7 @@
 */
 package de.craftinc.gates.commands;
 
-import de.craftinc.gates.Plugin;
+import de.craftinc.gates.controllers.PermissionController;
 import de.craftinc.gates.util.TextUtil;
 
 import java.util.ArrayList;
@@ -69,7 +69,7 @@ public class CommandHelp extends BaseCommand {
         optionalParameters.add("page");
         helpDescription = "prints this help page";
 
-        requiredPermission = Plugin.permissionInfo;
+        requiredPermission = PermissionController.permissionInfo;
 
         hasGateParam = false;
         needsPermissionAtCurrentLocation = false;
diff --git a/src/de/craftinc/gates/commands/CommandHide.java b/src/de/craftinc/gates/commands/CommandHide.java
index 877244b..af60973 100644
--- a/src/de/craftinc/gates/commands/CommandHide.java
+++ b/src/de/craftinc/gates/commands/CommandHide.java
@@ -18,6 +18,7 @@ package de.craftinc.gates.commands;
 
 import java.util.logging.Level;
 
+import de.craftinc.gates.controllers.PermissionController;
 import de.craftinc.gates.util.GateBlockChangeSender;
 import org.bukkit.ChatColor;
 
@@ -34,7 +35,7 @@ public class CommandHide extends BaseCommand {
 
         helpDescription = "Makes a gate NOT consist of gate blocks while open.";
 
-        requiredPermission = Plugin.permissionManage;
+        requiredPermission = PermissionController.permissionManage;
 
         needsPermissionAtCurrentLocation = false;
         shouldPersistToDisk = true;
diff --git a/src/de/craftinc/gates/commands/CommandInfo.java b/src/de/craftinc/gates/commands/CommandInfo.java
index 0dd1833..81ede3c 100644
--- a/src/de/craftinc/gates/commands/CommandInfo.java
+++ b/src/de/craftinc/gates/commands/CommandInfo.java
@@ -16,6 +16,8 @@
 */
 package de.craftinc.gates.commands;
 
+import de.craftinc.gates.models.Gate;
+import de.craftinc.gates.controllers.PermissionController;
 import de.craftinc.gates.util.GateBlockChangeSender;
 import org.bukkit.ChatColor;
 
@@ -23,6 +25,8 @@ import de.craftinc.gates.Plugin;
 import de.craftinc.gates.util.TextUtil;
 import org.bukkit.entity.Player;
 
+import java.util.HashSet;
+
 public class CommandInfo extends BaseCommand {
 
     public CommandInfo() {
@@ -33,7 +37,7 @@ public class CommandInfo extends BaseCommand {
 
         helpDescription = "Print detailed information about a certain or the closest gate.";
 
-        requiredPermission = Plugin.permissionInfo;
+        requiredPermission = PermissionController.permissionInfo;
 
         needsPermissionAtCurrentLocation = false;
         shouldPersistToDisk = false;
@@ -106,7 +110,10 @@ public class CommandInfo extends BaseCommand {
 
 
         if (this.sender instanceof Player) {
-            GateBlockChangeSender.temporaryHighlightGateFrame((Player) this.sender, this.gate);
+            HashSet set = new HashSet<>();
+            set.add(this.gate);
+
+            GateBlockChangeSender.temporaryHighlightGatesFrames((Player)this.sender, set);
         }
     }
 }
diff --git a/src/de/craftinc/gates/commands/CommandList.java b/src/de/craftinc/gates/commands/CommandList.java
index c870156..00fb769 100644
--- a/src/de/craftinc/gates/commands/CommandList.java
+++ b/src/de/craftinc/gates/commands/CommandList.java
@@ -21,10 +21,10 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 
+import de.craftinc.gates.controllers.PermissionController;
 import org.bukkit.ChatColor;
-import org.bukkit.entity.Player;
 
-import de.craftinc.gates.Gate;
+import de.craftinc.gates.models.Gate;
 import de.craftinc.gates.Plugin;
 import de.craftinc.gates.util.TextUtil;
 
@@ -48,7 +48,7 @@ public class CommandList extends BaseCommand {
 
         helpDescription = "lists all availible gates.";
 
-        requiredPermission = Plugin.permissionInfo;
+        requiredPermission = PermissionController.permissionInfo;
         shouldPersistToDisk = false;
         senderMustBePlayer = false;
     }
@@ -149,31 +149,14 @@ public class CommandList extends BaseCommand {
      */
     private Collection getAllGates() {
         Collection gates = Plugin.getPlugin().getGatesManager().allGates();
+        // create a copy since we cannot iterate over a collection while modifying it!
+        Collection gatesCopy = new ArrayList<>(gates);
 
-        if (this.sender instanceof Player && Plugin.getPermission() != null) {
-            Player p = (Player) this.sender;
-
-            // create a copy since we cannot iterate over a collection while modifying it!
-            Collection gatesCopy = new ArrayList<>(gates);
-
-            for (Gate gate : gatesCopy) {
-                if (gate.getLocation() != null) {
-                    boolean permissionAtGateLocation = Plugin.getPermission().has(gate.getLocation().getWorld(), p.getName(), this.requiredPermission);
-                    if (!permissionAtGateLocation) {
-                        gates.remove(gate);
-                        continue;
-                    }
-                }
-
-                if (gate.getExit() != null) {
-                    boolean permissionAtGateExit = Plugin.getPermission().has(gate.getExit().getWorld(), p.getName(), this.requiredPermission);
-                    if (!permissionAtGateExit) {
-                        gates.remove(gate);
-                    }
-                }
+        for (Gate gate : gatesCopy) {
+            if (!this.permissionController.hasPermission(this.sender, gate, this.requiredPermission)) {
+                gates.remove(gate);
             }
         }
-
         return gates;
     }
 
diff --git a/src/de/craftinc/gates/commands/CommandLocation.java b/src/de/craftinc/gates/commands/CommandLocation.java
index f0cd22a..09314e6 100644
--- a/src/de/craftinc/gates/commands/CommandLocation.java
+++ b/src/de/craftinc/gates/commands/CommandLocation.java
@@ -18,6 +18,7 @@ package de.craftinc.gates.commands;
 
 import java.util.Set;
 
+import de.craftinc.gates.controllers.PermissionController;
 import de.craftinc.gates.util.GateBlockChangeSender;
 import org.bukkit.ChatColor;
 import org.bukkit.Location;
@@ -33,7 +34,7 @@ public class CommandLocation extends BaseLocationCommand {
 
         requiredParameters.add("id");
         helpDescription = "Set the entrance of the gate to your current location.";
-        requiredPermission = Plugin.permissionManage;
+        requiredPermission = PermissionController.permissionManage;
 
         needsPermissionAtCurrentLocation = true;
         shouldPersistToDisk = true;
diff --git a/src/de/craftinc/gates/commands/CommandNearby.java b/src/de/craftinc/gates/commands/CommandNearby.java
index 349a804..7449ff4 100644
--- a/src/de/craftinc/gates/commands/CommandNearby.java
+++ b/src/de/craftinc/gates/commands/CommandNearby.java
@@ -1,7 +1,8 @@
 package de.craftinc.gates.commands;
 
-import de.craftinc.gates.Gate;
-import de.craftinc.gates.GatesManager;
+import de.craftinc.gates.models.Gate;
+import de.craftinc.gates.controllers.GatesManager;
+import de.craftinc.gates.controllers.PermissionController;
 import de.craftinc.gates.Plugin;
 import de.craftinc.gates.util.GateBlockChangeSender;
 import de.craftinc.gates.util.TextUtil;
@@ -16,7 +17,7 @@ public class CommandNearby extends BaseLocationCommand {
         aliases.add("nb");
 
         helpDescription = "Highlight nearby gates";
-        requiredPermission = Plugin.permissionInfo;
+        requiredPermission = PermissionController.permissionInfo;
         needsPermissionAtCurrentLocation = true;
         shouldPersistToDisk = false;
         senderMustBePlayer = true;
diff --git a/src/de/craftinc/gates/commands/CommandNew.java b/src/de/craftinc/gates/commands/CommandNew.java
index f304318..6194c7a 100644
--- a/src/de/craftinc/gates/commands/CommandNew.java
+++ b/src/de/craftinc/gates/commands/CommandNew.java
@@ -16,11 +16,12 @@
 */
 package de.craftinc.gates.commands;
 
+import de.craftinc.gates.controllers.PermissionController;
 import org.bukkit.ChatColor;
 import org.bukkit.Location;
 
-import de.craftinc.gates.Gate;
-import de.craftinc.gates.GatesManager;
+import de.craftinc.gates.models.Gate;
+import de.craftinc.gates.controllers.GatesManager;
 import de.craftinc.gates.Plugin;
 
 public class CommandNew extends BaseLocationCommand {
@@ -34,7 +35,7 @@ public class CommandNew extends BaseLocationCommand {
         senderMustBePlayer = true;
         hasGateParam = false;
         helpDescription = "Create a gate at your current location.";
-        requiredPermission = Plugin.permissionManage;
+        requiredPermission = PermissionController.permissionManage;
         needsPermissionAtCurrentLocation = true;
         shouldPersistToDisk = true;
         senderMustBePlayer = true;
diff --git a/src/de/craftinc/gates/commands/CommandOpen.java b/src/de/craftinc/gates/commands/CommandOpen.java
index b188e75..bd7e04f 100644
--- a/src/de/craftinc/gates/commands/CommandOpen.java
+++ b/src/de/craftinc/gates/commands/CommandOpen.java
@@ -16,6 +16,7 @@
 */
 package de.craftinc.gates.commands;
 
+import de.craftinc.gates.controllers.PermissionController;
 import de.craftinc.gates.util.GateBlockChangeSender;
 import org.bukkit.ChatColor;
 
@@ -29,7 +30,7 @@ public class CommandOpen extends BaseCommand {
 
         requiredParameters.add("id");
         helpDescription = "Open a gate so players can use it.";
-        requiredPermission = Plugin.permissionManage;
+        requiredPermission = PermissionController.permissionManage;
 
         needsPermissionAtCurrentLocation = false;
         shouldPersistToDisk = true;
diff --git a/src/de/craftinc/gates/commands/CommandRemove.java b/src/de/craftinc/gates/commands/CommandRemove.java
index 802d893..8ab4bec 100644
--- a/src/de/craftinc/gates/commands/CommandRemove.java
+++ b/src/de/craftinc/gates/commands/CommandRemove.java
@@ -16,6 +16,7 @@
 */
 package de.craftinc.gates.commands;
 
+import de.craftinc.gates.controllers.PermissionController;
 import de.craftinc.gates.util.GateBlockChangeSender;
 import org.bukkit.ChatColor;
 
@@ -33,7 +34,7 @@ public class CommandRemove extends BaseCommand {
         senderMustBePlayer = false;
         helpDescription = "Removes the gate from the game.";
 
-        requiredPermission = Plugin.permissionManage;
+        requiredPermission = PermissionController.permissionManage;
 
         needsPermissionAtCurrentLocation = false;
         shouldPersistToDisk = true;
diff --git a/src/de/craftinc/gates/commands/CommandRename.java b/src/de/craftinc/gates/commands/CommandRename.java
index 0a424c0..4731e4c 100644
--- a/src/de/craftinc/gates/commands/CommandRename.java
+++ b/src/de/craftinc/gates/commands/CommandRename.java
@@ -16,9 +16,10 @@
 */
 package de.craftinc.gates.commands;
 
+import de.craftinc.gates.controllers.PermissionController;
 import org.bukkit.ChatColor;
 
-import de.craftinc.gates.GatesManager;
+import de.craftinc.gates.controllers.GatesManager;
 import de.craftinc.gates.Plugin;
 
 public class CommandRename extends BaseCommand {
@@ -35,7 +36,7 @@ public class CommandRename extends BaseCommand {
 
         helpDescription = "Changes the id of a gate.";
 
-        requiredPermission = Plugin.permissionManage;
+        requiredPermission = PermissionController.permissionManage;
 
         needsPermissionAtCurrentLocation = false;
         shouldPersistToDisk = true;
diff --git a/src/de/craftinc/gates/commands/CommandUnhide.java b/src/de/craftinc/gates/commands/CommandUnhide.java
index f747b47..89dcc89 100644
--- a/src/de/craftinc/gates/commands/CommandUnhide.java
+++ b/src/de/craftinc/gates/commands/CommandUnhide.java
@@ -16,11 +16,10 @@
 */
 package de.craftinc.gates.commands;
 
+import de.craftinc.gates.controllers.PermissionController;
 import de.craftinc.gates.util.GateBlockChangeSender;
 import org.bukkit.ChatColor;
 
-import de.craftinc.gates.Plugin;
-
 public class CommandUnhide extends BaseCommand {
 
     public CommandUnhide() {
@@ -29,7 +28,7 @@ public class CommandUnhide extends BaseCommand {
 
         requiredParameters.add("id");
         helpDescription = "Make that gate visible";
-        requiredPermission = Plugin.permissionManage;
+        requiredPermission = PermissionController.permissionManage;
         needsPermissionAtCurrentLocation = false;
         shouldPersistToDisk = true;
         senderMustBePlayer = false;
diff --git a/src/de/craftinc/gates/GatesManager.java b/src/de/craftinc/gates/controllers/GatesManager.java
similarity index 96%
rename from src/de/craftinc/gates/GatesManager.java
rename to src/de/craftinc/gates/controllers/GatesManager.java
index 365a2f4..99a96b6 100644
--- a/src/de/craftinc/gates/GatesManager.java
+++ b/src/de/craftinc/gates/controllers/GatesManager.java
@@ -14,7 +14,7 @@
     You should have received a copy of the GNU Lesser General Public License
     along with this program (LGPLv3).  If not, see .
 */
-package de.craftinc.gates;
+package de.craftinc.gates.controllers;
 
 import java.io.File;
 import java.io.FileWriter;
@@ -23,6 +23,8 @@ import java.io.IOException;
 import java.util.*;
 import java.util.logging.Level;
 
+import de.craftinc.gates.Plugin;
+import de.craftinc.gates.models.Gate;
 import de.craftinc.gates.persistence.MigrationUtil;
 import de.craftinc.gates.util.ConfigurationUtil;
 import org.bukkit.Chunk;
@@ -31,9 +33,8 @@ import org.bukkit.block.Block;
 import org.bukkit.configuration.file.FileConfiguration;
 import org.bukkit.configuration.file.YamlConfiguration;
 
-import de.craftinc.gates.util.SimpleChunk;
-import de.craftinc.gates.util.SimpleLocation;
-
+import de.craftinc.gates.models.SimpleChunk;
+import de.craftinc.gates.models.SimpleLocation;
 
 public class GatesManager {
     protected List gates;
@@ -88,19 +89,16 @@ public class GatesManager {
         return nearestGate;
     }
 
-
-    public Gate getGateAtLocation(final Location location) {
+    Gate getGateAtLocation(final Location location) {
         SimpleLocation simpleLocation = new SimpleLocation(location);
         return gatesByLocation.get(simpleLocation);
     }
 
-
     public Gate getGateAtFrameLocation(final Location location) {
         SimpleLocation simpleLocation = new SimpleLocation(location);
         return gatesByFrameLocation.get(simpleLocation);
     }
 
-
     public void saveGatesToDisk() {
         if (storageFileIsInvalid) {
             Plugin.log(Level.SEVERE, "ERROR: Not saving gates to disk. Storage file is invalid or corrupted!");
@@ -121,7 +119,9 @@ public class GatesManager {
 
 
     @SuppressWarnings("unchecked")
-    boolean loadGatesFromDisk() {
+    public boolean loadGatesFromDisk() {
+        // TODO: refactor: move loading/saving logic into persistence package
+
         this.gatesConfigFile = new File(Plugin.getPlugin().getDataFolder(), "gates.yml");
 
         if (!this.gatesConfigFile.exists()) {
@@ -289,7 +289,6 @@ public class GatesManager {
         if (gateBlocks != null) {
 
             for (Location l : gateBlocks) {
-
                 SimpleLocation sl = new SimpleLocation(l);
                 gatesByLocation.remove(sl);
 
@@ -311,7 +310,6 @@ public class GatesManager {
 
     private void addGateByLocations(final Gate g) {
         for (Location l : g.getGateBlockLocations()) {
-
             SimpleLocation sl = new SimpleLocation(l);
             gatesByLocation.put(sl, g);
 
@@ -368,7 +366,7 @@ public class GatesManager {
                     Set gatesForC = gatesByChunk.get(sc);
 
                     if (gatesForC == null) {
-                        gatesForC = new HashSet(); // NOTE: not optimizing size here
+                        gatesForC = new HashSet<>(); // NOTE: not optimizing size here
                         gatesByChunk.put(sc, gatesForC);
                     }
 
@@ -378,7 +376,7 @@ public class GatesManager {
         }
     }
 
-    void storeInvalidGate(Map map) {
+    public void storeInvalidGate(Map map) {
         File invalidGatesFile = new File(Plugin.getPlugin().getDataFolder(), "invalid_gates.yml");
         Boolean invalidGatesFileExists = invalidGatesFile.exists();
 
@@ -428,13 +426,11 @@ public class GatesManager {
         }
     }
 
-
     public void handleGateIdChange(final Gate g, final String oldId) {
         this.removeGateById(oldId);
         this.addGateWithId(g);
     }
 
-
     public void handleGateLocationChange(final Gate g,
                                          final Location oldLocation,
                                          final Set oldGateBlockLocations,
@@ -449,12 +445,10 @@ public class GatesManager {
         this.addGateByFrameLocations(g);
     }
 
-
     public void handleGateExitChange(final Gate g, final Location oldExit) {
         // nothing to do
     }
 
-
     public void handleNewGate(final Gate g) {
         this.gates.add(g);
 
@@ -464,7 +458,6 @@ public class GatesManager {
         this.addGateByFrameLocations(g);
     }
 
-
     public void handleDeletion(final Gate g) {
         this.gates.remove(g);
 
@@ -474,12 +467,10 @@ public class GatesManager {
         this.removeGateByFrameLocation(g.getGateFrameBlocks());
     }
 
-
     public boolean gateExists(final String id) {
         return gatesById.containsKey(id.toLowerCase());
     }
 
-
     public List allGates() {
         return gates;
     }
diff --git a/src/de/craftinc/gates/controllers/PermissionController.java b/src/de/craftinc/gates/controllers/PermissionController.java
new file mode 100644
index 0000000..cbc0c02
--- /dev/null
+++ b/src/de/craftinc/gates/controllers/PermissionController.java
@@ -0,0 +1,58 @@
+package de.craftinc.gates.controllers;
+
+import de.craftinc.gates.models.Gate;
+import net.milkbowl.vault.permission.Permission;
+import org.bukkit.Location;
+import org.bukkit.OfflinePlayer;
+import org.bukkit.World;
+import org.bukkit.command.CommandSender;
+
+public class PermissionController {
+    public static final String permissionInfo = "craftincgates.info";
+    public static final String permissionManage = "craftincgates.manage";
+    public static final String permissionUse = "craftincgates.use";
+
+    private Permission permission;
+
+    public void setPermission(Permission permission) {
+        this.permission = permission;
+    }
+
+    public boolean hasPermission(CommandSender sender, Gate gate, String permission) {
+        assert(sender != null);
+        assert(permission != null);
+
+        if (gate == null) {
+            return hasPermission(sender, permission);
+        }
+
+        final Location location = gate.getLocation();
+        final Location exit = gate.getExit();
+
+        boolean permAtLocation = location == null || hasPermission(sender, location.getWorld(), permission);
+        boolean permAtExit = exit == null || hasPermission(sender, exit.getWorld(), permission);
+
+        return permAtLocation && permAtExit;
+    }
+
+    public boolean hasPermission(CommandSender sender, String permission) {
+        return hasPermission(sender, (World)null, permission);
+    }
+
+    private boolean hasPermission(CommandSender sender, World world, String permission) {
+        assert(sender != null);
+        assert(permission != null);
+
+        if (this.permission == null) {
+            // fallback - use the standard bukkit permission system
+            return sender.hasPermission(permission);
+        }
+
+        if (!(sender instanceof OfflinePlayer)) {
+            return this.permission.has(sender, permission);
+        }
+
+        String worldName = world != null ? world.getName() : null;
+        return this.permission.playerHas(worldName, (OfflinePlayer)sender, permission);
+    }
+}
diff --git a/src/de/craftinc/gates/controllers/TeleportController.java b/src/de/craftinc/gates/controllers/TeleportController.java
new file mode 100644
index 0000000..7b015a7
--- /dev/null
+++ b/src/de/craftinc/gates/controllers/TeleportController.java
@@ -0,0 +1,126 @@
+package de.craftinc.gates.controllers;
+
+import de.craftinc.gates.Plugin;
+import de.craftinc.gates.models.Gate;
+import de.craftinc.gates.util.ConfigurationUtil;
+import org.bukkit.ChatColor;
+import org.bukkit.Location;
+import org.bukkit.configuration.file.FileConfiguration;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.Player;
+import org.bukkit.event.player.PlayerTeleportEvent;
+
+import java.util.Calendar;
+import java.util.HashMap;
+
+public class TeleportController {
+    private TeleportMessageUtil messageUtil;
+    private GatesManager gatesManager;
+    private PermissionController permissionController;
+
+    public TeleportController(Plugin plugin) {
+        this.gatesManager = plugin.getGatesManager();
+        this.messageUtil = new TeleportMessageUtil(plugin.getConfig());
+        this.permissionController = plugin.getPermissionController();
+    }
+
+    /**
+     * Try teleporting a player at given location.
+     */
+    public void teleport(final Player player, Location toLocation) {
+        final Gate gateAtLocation = gatesManager.getGateAtLocation(toLocation);
+
+        if ((gateAtLocation == null) || !gateAtLocation.isOpen()) {
+            return;
+        }
+
+        if (!permissionController.hasPermission(player, gateAtLocation, PermissionController.permissionUse)) {
+            messageUtil.sendNoPermissionMessage(player);
+            return;
+        }
+
+        this.teleportPlayer(player, gateAtLocation);
+    }
+
+    private void teleportPlayer(final Player player, final Gate gate) {
+        final Entity vehicle = player.getVehicle();
+
+        if (vehicle != null && !gate.getAllowsVehicles()) {
+            messageUtil.sendVehicleForbiddenMessage(player);
+            Plugin.log("no vehicle transport");
+            return;
+        }
+
+        final Location destination = calculateDestination(player, gate);
+        player.teleport(destination);
+
+        if (vehicle != null) {
+            vehicle.teleport(destination, PlayerTeleportEvent.TeleportCause.PLUGIN);
+            vehicle.setPassenger(player);
+        }
+
+        messageUtil.sendTeleportMessage(player);
+    }
+
+    private Location calculateDestination(Player player, Gate gate) {
+        final Location exit = gate.getExit();
+        final Location pLocation = player.getLocation();
+        final Float newYaw = exit.getYaw() - gate.getLocation().getYaw() + pLocation.getYaw();
+
+        return new Location(exit.getWorld(),
+                exit.getX(),
+                exit.getY(),
+                exit.getZ(),
+                newYaw,
+                pLocation.getPitch()
+        );
+    }
+}
+
+class TeleportMessageUtil {
+    private HashMap lastNoPermissionMessages = new HashMap<>();
+    private FileConfiguration config;
+
+    TeleportMessageUtil(FileConfiguration config) {
+        this.config = config;
+    }
+
+    void sendVehicleForbiddenMessage(Player player) {
+        if (!config.getBoolean(ConfigurationUtil.confShowTeleportNoPermissionMessageKey)) {
+            return;
+        }
+
+        final String notAllowedMessage = config.getString(ConfigurationUtil.confGateTeleportVehicleNotAllowedMessageKey);
+        player.sendMessage(ChatColor.DARK_AQUA + notAllowedMessage);
+    }
+
+    void sendNoPermissionMessage(Player player) {
+        if (!config.getBoolean(ConfigurationUtil.confShowTeleportNoPermissionMessageKey)) {
+            return;
+        }
+        final String playerName = player.getPlayer().getName();
+
+        if (playerName == null) {
+            return;
+        }
+
+        final Long now = Calendar.getInstance().getTimeInMillis();
+
+        // do not display messages more often than once per second
+        if (!this.lastNoPermissionMessages.containsKey(playerName)
+                || this.lastNoPermissionMessages.get(playerName) < now - 10000L) {
+
+            final String noPermissionString = config.getString(ConfigurationUtil.confGateTeleportNoPermissionMessageKey);
+            player.sendMessage(ChatColor.RED + noPermissionString);
+            this.lastNoPermissionMessages.put(playerName, now);
+        }
+    }
+
+    void sendTeleportMessage(Player player) {
+        if (!config.getBoolean(ConfigurationUtil.confShowTeleportMessageKey)) {
+            return;
+        }
+        final String teleportMessage = config.getString(ConfigurationUtil.confGateTeleportMessageKey);
+        player.sendMessage(ChatColor.DARK_AQUA + teleportMessage);
+    }
+}
\ No newline at end of file
diff --git a/src/de/craftinc/gates/listeners/BlockBreakListener.java b/src/de/craftinc/gates/listeners/BlockBreakListener.java
index a96b27c..abe2e0e 100644
--- a/src/de/craftinc/gates/listeners/BlockBreakListener.java
+++ b/src/de/craftinc/gates/listeners/BlockBreakListener.java
@@ -16,8 +16,7 @@
 */
 package de.craftinc.gates.listeners;
 
-
-import de.craftinc.gates.Gate;
+import de.craftinc.gates.models.Gate;
 import de.craftinc.gates.Plugin;
 import de.craftinc.gates.util.GateBlockChangeSender;
 import org.bukkit.event.EventHandler;
@@ -26,6 +25,7 @@ import org.bukkit.event.Listener;
 import org.bukkit.event.block.BlockBreakEvent;
 
 public class BlockBreakListener implements Listener {
+
     @EventHandler(priority = EventPriority.MONITOR)
     public void onBlockBreak(BlockBreakEvent event) {
         if (event.isCancelled()) {
@@ -37,8 +37,7 @@ public class BlockBreakListener implements Listener {
         if (gate != null && !gate.isHidden()) {
             try {
                 gate.setOpen(false);
-            } catch (Exception ignored) {
-            }
+            } catch (Exception ignored) {}
 
             GateBlockChangeSender.updateGateBlocks(gate);
         }
diff --git a/src/de/craftinc/gates/listeners/PlayerChangedWorldListener.java b/src/de/craftinc/gates/listeners/PlayerChangedWorldListener.java
index dace944..a95f763 100644
--- a/src/de/craftinc/gates/listeners/PlayerChangedWorldListener.java
+++ b/src/de/craftinc/gates/listeners/PlayerChangedWorldListener.java
@@ -16,17 +16,18 @@
 */
 package de.craftinc.gates.listeners;
 
-
 import de.craftinc.gates.util.GateBlockChangeSender;
+import org.bukkit.entity.Player;
 import org.bukkit.event.EventHandler;
 import org.bukkit.event.EventPriority;
 import org.bukkit.event.Listener;
 import org.bukkit.event.player.PlayerChangedWorldEvent;
 
-
 public class PlayerChangedWorldListener implements Listener {
+
     @EventHandler(priority = EventPriority.NORMAL)
     public void onPlayerChangeWorld(PlayerChangedWorldEvent event) {
-        GateBlockChangeSender.updateGateBlocks(event.getPlayer());
+        Player p = event.getPlayer();
+        GateBlockChangeSender.updateGateBlocks(p, p.getLocation());
     }
 }
diff --git a/src/de/craftinc/gates/listeners/PlayerJoinListener.java b/src/de/craftinc/gates/listeners/PlayerJoinListener.java
index da33562..eba4a6e 100644
--- a/src/de/craftinc/gates/listeners/PlayerJoinListener.java
+++ b/src/de/craftinc/gates/listeners/PlayerJoinListener.java
@@ -16,16 +16,18 @@
 */
 package de.craftinc.gates.listeners;
 
-
 import de.craftinc.gates.util.GateBlockChangeSender;
+import org.bukkit.entity.Player;
 import org.bukkit.event.EventHandler;
 import org.bukkit.event.EventPriority;
 import org.bukkit.event.Listener;
 import org.bukkit.event.player.PlayerJoinEvent;
 
 public class PlayerJoinListener implements Listener {
+
     @EventHandler(priority = EventPriority.NORMAL)
     public void onPlayerJoin(PlayerJoinEvent event) {
-        GateBlockChangeSender.updateGateBlocks(event.getPlayer());
+        Player p = event.getPlayer();
+        GateBlockChangeSender.updateGateBlocks(p, p.getLocation());
     }
 }
diff --git a/src/de/craftinc/gates/listeners/PlayerMoveListener.java b/src/de/craftinc/gates/listeners/PlayerMoveListener.java
index 0bf3fd2..376fd5b 100644
--- a/src/de/craftinc/gates/listeners/PlayerMoveListener.java
+++ b/src/de/craftinc/gates/listeners/PlayerMoveListener.java
@@ -16,30 +16,20 @@
 */
 package de.craftinc.gates.listeners;
 
-import java.util.Calendar;
-import java.util.HashMap;
-
-import de.craftinc.gates.util.ConfigurationUtil;
+import de.craftinc.gates.Plugin;
+import de.craftinc.gates.controllers.TeleportController;
 import de.craftinc.gates.util.GateBlockChangeSender;
-import de.craftinc.gates.util.VehicleCloner;
-import org.bukkit.ChatColor;
-import org.bukkit.Location;
-import org.bukkit.entity.Entity;
-import org.bukkit.entity.Player;
-import org.bukkit.entity.Vehicle;
 import org.bukkit.event.EventHandler;
 import org.bukkit.event.EventPriority;
 import org.bukkit.event.Listener;
 import org.bukkit.event.player.PlayerMoveEvent;
 
-import de.craftinc.gates.Gate;
-import de.craftinc.gates.GatesManager;
-import de.craftinc.gates.Plugin;
-import org.bukkit.scheduler.BukkitScheduler;
-
-
 public class PlayerMoveListener implements Listener {
-    private HashMap lastNoPermissionMessages = new HashMap<>();
+    private TeleportController teleportController;
+
+    public PlayerMoveListener(Plugin plugin) {
+        this.teleportController = new TeleportController(plugin);
+    }
 
     @EventHandler(priority = EventPriority.NORMAL)
     public void onPlayerMove(PlayerMoveEvent event) {
@@ -51,113 +41,6 @@ public class PlayerMoveListener implements Listener {
             GateBlockChangeSender.updateGateBlocks(event.getPlayer(), event.getTo());
         }
 
-        final GatesManager gateManager = Plugin.getPlugin().getGatesManager();
-        final Gate gateAtLocation = gateManager.getGateAtLocation(event.getTo());
-
-        if ((gateAtLocation == null) || !gateAtLocation.isOpen()) {
-            return;
-        }
-
-        // Check for permission
-        if (!hasPermission(event.getPlayer(), gateAtLocation)
-                && Plugin.getPlugin().getConfig().getBoolean(ConfigurationUtil.confShowTeleportNoPermissionMessageKey)) {
-
-            final String playerName = event.getPlayer().getName();
-
-            if (playerName == null) {
-                return;
-            }
-
-            // get the current time
-            final Long now = Calendar.getInstance().getTimeInMillis();
-
-            // do not display messages more often than once per second
-            if (!this.lastNoPermissionMessages.containsKey(playerName) || this.lastNoPermissionMessages.get(playerName) < now - 10000L) {
-
-                final String noPermissionString = Plugin.getPlugin().getConfig().getString(ConfigurationUtil.confGateTeleportNoPermissionMessageKey);
-                event.getPlayer().sendMessage(ChatColor.RED + noPermissionString);
-                this.lastNoPermissionMessages.put(playerName, now);
-            }
-        } else {
-            this.teleportPlayer(event.getPlayer(), gateAtLocation);
-        }
-    }
-
-
-    /**
-     * Teleports a player.
-     *
-     * @param player The player to teleport.
-     * @param gate   The gate to which exit the player will be teleported.
-     */
-    private void teleportPlayer(final Player player, final Gate gate) {
-        // Destination
-        final Float newYaw = gate.getExit().getYaw() - gate.getLocation().getYaw() + player.getLocation().getYaw();
-        final Location destLocation = new Location(gate.getExit().getWorld(),
-                gate.getExit().getX(),
-                gate.getExit().getY(),
-                gate.getExit().getZ(),
-                newYaw,
-                player.getLocation().getPitch()
-        );
-
-        // Riding
-        final Entity vehicle = player.getVehicle();
-        final boolean vehicleIsSuitable = (vehicle != null) && (vehicle instanceof Vehicle);
-
-        if (vehicle != null && (!vehicleIsSuitable) || !gate.getAllowsVehicles()) {
-
-            if (!gate.getAllowsVehicles() && Plugin.getPlugin().getConfig().getBoolean(ConfigurationUtil.confShowTeleportNoPermissionMessageKey)) {
-                final String notAllowedMessage = Plugin.getPlugin().getConfig().getString(ConfigurationUtil.confGateTeleportVehicleNotAllowedMessageKey);
-                player.sendMessage(ChatColor.DARK_AQUA + notAllowedMessage);
-            }
-
-            return;
-        }
-
-        //  (eject player)
-        if (vehicleIsSuitable) {
-            vehicle.eject();
-            vehicle.remove();
-        }
-
-        // Teleport
-        player.teleport(destLocation);
-
-        // Riding (mount player)
-        if (vehicleIsSuitable) {
-            final Plugin plugin = Plugin.getPlugin();
-            final BukkitScheduler scheduler = plugin.getServer().getScheduler();
-
-            destLocation.getChunk().load(); // load the destination chunk, no new entity will be created otherwise
-
-            scheduler.scheduleSyncDelayedTask(plugin, new Runnable() {
-                public void run() {
-                    // FIXME: the code below should be executed after the chunk got loaded and not after a fixed time!
-
-                    // create a new entity at the destination location
-                    final Vehicle newVehicle = VehicleCloner.clone((Vehicle) vehicle, destLocation);
-                    newVehicle.setPassenger(player);
-                }
-            }, 2);
-        }
-
-        // Message
-        if (Plugin.getPlugin().getConfig().getBoolean(ConfigurationUtil.confShowTeleportMessageKey)) {
-            final String teleportMessage = Plugin.getPlugin().getConfig().getString(ConfigurationUtil.confGateTeleportMessageKey);
-            player.sendMessage(ChatColor.DARK_AQUA + teleportMessage);
-        }
-    }
-
-
-    private boolean hasPermission(final Player player, final Gate gate) {
-        if (Plugin.getPermission() == null) { // fallback: use the standard bukkit permission system
-            return player.hasPermission(Plugin.permissionUse);
-        } else {
-            final boolean permAtLocation = Plugin.getPermission().has(gate.getLocation().getWorld(), player.getName(), Plugin.permissionUse);
-            final boolean permAtExit = Plugin.getPermission().has(gate.getExit().getWorld(), player.getName(), Plugin.permissionUse);
-
-            return permAtLocation && permAtExit;
-        }
+        teleportController.teleport(event.getPlayer(), event.getTo());
     }
 }
diff --git a/src/de/craftinc/gates/listeners/PlayerRespawnListener.java b/src/de/craftinc/gates/listeners/PlayerRespawnListener.java
index 0649e21..d8064e6 100644
--- a/src/de/craftinc/gates/listeners/PlayerRespawnListener.java
+++ b/src/de/craftinc/gates/listeners/PlayerRespawnListener.java
@@ -16,14 +16,12 @@
 */
 package de.craftinc.gates.listeners;
 
-
 import de.craftinc.gates.util.GateBlockChangeSender;
 import org.bukkit.event.EventHandler;
 import org.bukkit.event.EventPriority;
 import org.bukkit.event.Listener;
 import org.bukkit.event.player.PlayerRespawnEvent;
 
-
 public class PlayerRespawnListener implements Listener {
     @EventHandler(priority = EventPriority.NORMAL)
     public void onPlayerRespawn(PlayerRespawnEvent event) {
diff --git a/src/de/craftinc/gates/listeners/PlayerTeleportListener.java b/src/de/craftinc/gates/listeners/PlayerTeleportListener.java
index 3b5d079..ddaf82c 100644
--- a/src/de/craftinc/gates/listeners/PlayerTeleportListener.java
+++ b/src/de/craftinc/gates/listeners/PlayerTeleportListener.java
@@ -16,7 +16,6 @@
 */
 package de.craftinc.gates.listeners;
 
-
 import de.craftinc.gates.util.GateBlockChangeSender;
 import org.bukkit.event.EventHandler;
 import org.bukkit.event.EventPriority;
@@ -24,6 +23,7 @@ import org.bukkit.event.Listener;
 import org.bukkit.event.player.PlayerTeleportEvent;
 
 public class PlayerTeleportListener implements Listener {
+
     @EventHandler(priority = EventPriority.MONITOR)
     public void onPlayerTeleport(PlayerTeleportEvent event) {
         if (event.isCancelled()) {
diff --git a/src/de/craftinc/gates/listeners/VehicleMoveListener.java b/src/de/craftinc/gates/listeners/VehicleMoveListener.java
new file mode 100644
index 0000000..3c23cee
--- /dev/null
+++ b/src/de/craftinc/gates/listeners/VehicleMoveListener.java
@@ -0,0 +1,27 @@
+package de.craftinc.gates.listeners;
+
+import de.craftinc.gates.Plugin;
+import de.craftinc.gates.controllers.TeleportController;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.Listener;
+import org.bukkit.event.vehicle.VehicleMoveEvent;
+
+public class VehicleMoveListener implements Listener {
+    private TeleportController teleportController;
+
+    public VehicleMoveListener(Plugin plugin) {
+        this.teleportController = new TeleportController(plugin);
+    }
+
+    @EventHandler(priority = EventPriority.NORMAL)
+    public void onVehicleMove(VehicleMoveEvent event) {
+        Entity passenger = event.getVehicle().getPassenger();
+
+        if (passenger instanceof Player) {
+            teleportController.teleport((Player)passenger, event.getTo());
+        }
+    }
+}
diff --git a/src/de/craftinc/gates/Gate.java b/src/de/craftinc/gates/models/Gate.java
similarity index 92%
rename from src/de/craftinc/gates/Gate.java
rename to src/de/craftinc/gates/models/Gate.java
index 3c8f1fd..14f83d1 100644
--- a/src/de/craftinc/gates/Gate.java
+++ b/src/de/craftinc/gates/models/Gate.java
@@ -14,37 +14,30 @@
     You should have received a copy of the GNU Lesser General Public License
     along with this program (LGPLv3).  If not, see .
 */
-package de.craftinc.gates;
+package de.craftinc.gates.models;
 
+import de.craftinc.gates.Plugin;
 import de.craftinc.gates.util.ConfigurationUtil;
 import de.craftinc.gates.util.FloodUtil;
 import de.craftinc.gates.persistence.LocationUtil;
 import org.bukkit.Location;
 import org.bukkit.Material;
 import org.bukkit.block.Block;
+import org.bukkit.block.BlockFace;
 import org.bukkit.configuration.serialization.ConfigurationSerializable;
 
 import java.util.*;
 
 
 public class Gate implements ConfigurationSerializable {
-    protected Location location; /* saving both location and gateBlockLocations is redundant but makes it easy to allow players to reshape gates */
+    private Location location; /* saving both location and gateBlockLocations is redundant but makes it easy to allow players to reshape gates */
     private Set gateBlockLocations = new HashSet<>(); /* Locations of the blocks inside the gate */
-
     private Set gateFrameBlocks = new HashSet<>();
-
-    protected Location exit;
-
+    private Location exit;
     private boolean isHidden = false;
     private boolean isOpen = false;
-
     private boolean allowsVehicles = true;
-
-    protected String id;
-
-    public static String getGateBlocksKey() {
-        return gateBlocksKey;
-    }
+    private String id;
 
     /**
      * You should never create two gates with the same 'id'. Also see 'setId(String id)'.
@@ -55,11 +48,21 @@ public class Gate implements ConfigurationSerializable {
         setId(id);
     }
 
-
     public String toString() {
         return super.toString() + " " + this.getId();
     }
 
+    public GateDirection getDirection() {
+        if (gateBlockLocations.isEmpty()) {
+            return null;
+        } else {
+            Block some = ((Location)gateBlockLocations.toArray()[0]).getBlock();
+            boolean east = gateBlockLocations.contains(some.getRelative(BlockFace.EAST).getLocation());
+            boolean west = gateBlockLocations.contains(some.getRelative(BlockFace.WEST).getLocation());
+
+            return east || west ? GateDirection.EastWest : GateDirection.NorthSouth;
+        }
+    }
 
     /**
      * @return This method might return a 'null' data.
@@ -68,7 +71,6 @@ public class Gate implements ConfigurationSerializable {
         return location;
     }
 
-
     /**
      * @param location Supplying 'null' is permitted.
      * @throws Exception Will throw an exception if the gate is open and an invalid (no gate frame) location is
@@ -186,7 +188,7 @@ public class Gate implements ConfigurationSerializable {
     /**
      * Checks if values attributes do add up; will close gate on wrong values.
      */
-    void validate() throws Exception {
+    public void validate() throws Exception {
         if (!isOpen) {
             return;
         }
diff --git a/src/de/craftinc/gates/models/GateDirection.java b/src/de/craftinc/gates/models/GateDirection.java
new file mode 100644
index 0000000..8ec3657
--- /dev/null
+++ b/src/de/craftinc/gates/models/GateDirection.java
@@ -0,0 +1,6 @@
+package de.craftinc.gates.models;
+
+public enum GateDirection {
+    EastWest,
+    NorthSouth
+}
diff --git a/src/de/craftinc/gates/models/GateMaterial.java b/src/de/craftinc/gates/models/GateMaterial.java
new file mode 100644
index 0000000..ee7d5e9
--- /dev/null
+++ b/src/de/craftinc/gates/models/GateMaterial.java
@@ -0,0 +1,102 @@
+package de.craftinc.gates.models;
+
+import org.bukkit.Material;
+
+import java.security.InvalidParameterException;
+
+public class GateMaterial {
+
+    private Material material;
+
+    public GateMaterial(String materialString) throws InvalidParameterException {
+        Material material;
+
+        switch (materialString) {
+            case "sapling":
+                material = Material.SAPLING;
+                break;
+            case "water":
+                material = Material.STATIONARY_WATER;
+                break;
+            case "lava":
+                material = Material.STATIONARY_LAVA;
+                break;
+            case "cobweb":
+                material = Material.WEB;
+                break;
+            case "grass":
+                material = Material.LONG_GRASS;
+                break;
+            case "dead bush":
+                material = Material.DEAD_BUSH;
+                break;
+            case "dandelion":
+                material = Material.YELLOW_FLOWER;
+                break;
+            case "poppy":
+                material = Material.RED_ROSE;
+                break;
+            case "brown mushroom":
+                material = Material.BROWN_MUSHROOM;
+                break;
+            case "red mushroom":
+                material = Material.RED_MUSHROOM;
+                break;
+            case "torch":
+                material = Material.TORCH;
+                break;
+            case "redstone torch (off)":
+                material = Material.REDSTONE_TORCH_OFF;
+                break;
+            case "redstone torch (on)":
+                material = Material.REDSTONE_TORCH_ON;
+                break;
+            case "fence":
+                material = Material.FENCE;
+                break;
+            case "nether portal":
+                material = Material.PORTAL;
+                break;
+            case "iron bars":
+                material = Material.IRON_FENCE;
+                break;
+            case "glass pane":
+                material = Material.THIN_GLASS;
+                break;
+            case "fence gate":
+                material = Material.FENCE_GATE;
+                break;
+            case "nether brick fence":
+                material = Material.NETHER_FENCE;
+                break;
+            case "nether wart":
+                material = Material.NETHER_WARTS;
+                break;
+            case "end portal":
+                material = Material.ENDER_PORTAL;
+                break;
+            case "cobblestone wall":
+                material = Material.COBBLE_WALL;
+                break;
+            default:
+                throw new InvalidParameterException();
+        }
+
+        this.material = material;
+    }
+
+    public Material getMaterial() {
+        return material;
+    }
+
+    public byte getData(GateDirection direction) {
+        switch (material) {
+            case PORTAL:
+                return direction == GateDirection.EastWest ? (byte)0b0 : (byte)0b10;
+            case GRASS:
+                return 1;
+            default:
+                return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/de/craftinc/gates/util/SimpleChunk.java b/src/de/craftinc/gates/models/SimpleChunk.java
similarity index 98%
rename from src/de/craftinc/gates/util/SimpleChunk.java
rename to src/de/craftinc/gates/models/SimpleChunk.java
index 6dd07c3..8375827 100644
--- a/src/de/craftinc/gates/util/SimpleChunk.java
+++ b/src/de/craftinc/gates/models/SimpleChunk.java
@@ -14,7 +14,7 @@
     You should have received a copy of the GNU Lesser General Public License
     along with this program (LGPLv3).  If not, see .
 */
-package de.craftinc.gates.util;
+package de.craftinc.gates.models;
 
 import org.bukkit.Chunk;
 import org.bukkit.World;
diff --git a/src/de/craftinc/gates/util/SimpleLocation.java b/src/de/craftinc/gates/models/SimpleLocation.java
similarity index 98%
rename from src/de/craftinc/gates/util/SimpleLocation.java
rename to src/de/craftinc/gates/models/SimpleLocation.java
index 308b66c..caaee83 100644
--- a/src/de/craftinc/gates/util/SimpleLocation.java
+++ b/src/de/craftinc/gates/models/SimpleLocation.java
@@ -14,7 +14,7 @@
     You should have received a copy of the GNU Lesser General Public License
     along with this program (LGPLv3).  If not, see .
 */
-package de.craftinc.gates.util;
+package de.craftinc.gates.models;
 
 import org.bukkit.Location;
 
@@ -24,7 +24,6 @@ public class SimpleLocation {
     private int y;
     private int z;
 
-
     public SimpleLocation(Location l) {
         this.world = l.getWorld().getName();
 
@@ -35,7 +34,6 @@ public class SimpleLocation {
         this.z = l.getBlockZ();
     }
 
-
     public SimpleLocation(Location l, boolean isHeadPosition) {
         this.world = l.getWorld().getName();
 
@@ -50,13 +48,11 @@ public class SimpleLocation {
         }
     }
 
-
     @Override
     public String toString() {
         return super.toString() + " x: " + x + " y: " + y + " z: " + z + " world: " + world;
     }
 
-
     @Override
     public boolean equals(final Object o) {
         if (o instanceof SimpleLocation) {
@@ -74,7 +70,6 @@ public class SimpleLocation {
         return false;
     }
 
-
     @Override
     public int hashCode() {
         int hash = 13;
diff --git a/src/de/craftinc/gates/persistence/LocationUtil.java b/src/de/craftinc/gates/persistence/LocationUtil.java
index 05eb369..4886cc4 100644
--- a/src/de/craftinc/gates/persistence/LocationUtil.java
+++ b/src/de/craftinc/gates/persistence/LocationUtil.java
@@ -24,7 +24,6 @@ import org.bukkit.World;
 
 import de.craftinc.gates.Plugin;
 
-
 public class LocationUtil {
 
     private final static String worldKey = "world";
@@ -32,7 +31,7 @@ public class LocationUtil {
     private final static String yKey = "y";
     private final static String zKey = "z";
 
-    protected static World getWorld(final String name) throws Exception {
+    private static World getWorld(final String name) throws Exception {
         if (name == null) {
             throw new IllegalArgumentException("The name of the world must not be 'null");
         }
diff --git a/src/de/craftinc/gates/persistence/MigrationUtil.java b/src/de/craftinc/gates/persistence/MigrationUtil.java
index 83e2028..46efe73 100644
--- a/src/de/craftinc/gates/persistence/MigrationUtil.java
+++ b/src/de/craftinc/gates/persistence/MigrationUtil.java
@@ -16,7 +16,7 @@
 */
 package de.craftinc.gates.persistence;
 
-import de.craftinc.gates.Gate;
+import de.craftinc.gates.models.Gate;
 
 import de.craftinc.gates.Plugin;
 import org.bukkit.Location;
diff --git a/src/de/craftinc/gates/util/ConfigurationUtil.java b/src/de/craftinc/gates/util/ConfigurationUtil.java
index 74f5362..1c8c480 100644
--- a/src/de/craftinc/gates/util/ConfigurationUtil.java
+++ b/src/de/craftinc/gates/util/ConfigurationUtil.java
@@ -16,13 +16,12 @@
 */
 package de.craftinc.gates.util;
 
-
 import de.craftinc.gates.Plugin;
-import org.bukkit.Material;
+import de.craftinc.gates.models.GateMaterial;
 
+import java.security.InvalidParameterException;
 import java.util.logging.Level;
 
-
 public class ConfigurationUtil {
     public static final String confMaxGateBlocksKey = "maxGateBlocks";
     public static final String confPlayerGateBlockUpdateRadiusKey = "playerGateBlockUpdateRadius";
@@ -39,88 +38,12 @@ public class ConfigurationUtil {
 
     static GateMaterial getPortalMaterial() {
         String materialString = Plugin.getPlugin().getConfig().getString(confGateMaterialKey);
-        GateMaterial material = new GateMaterial();
 
-        switch (materialString) {
-            case "sapling":
-                material.material = Material.SAPLING;
-                break;
-            case "water":
-                material.material = Material.STATIONARY_WATER;
-                break;
-            case "lava":
-                material.material = Material.STATIONARY_LAVA;
-                break;
-            case "cobweb":
-                material.material = Material.WEB;
-                break;
-            case "grass":
-                material.material = Material.LONG_GRASS;
-                material.data = 1;
-                break;
-            case "dead bush":
-                material.material = Material.DEAD_BUSH;
-                break;
-            case "dandelion":
-                material.material = Material.YELLOW_FLOWER;
-                break;
-            case "poppy":
-                material.material = Material.RED_ROSE;
-                break;
-            case "brown mushroom":
-                material.material = Material.BROWN_MUSHROOM;
-                break;
-            case "red mushroom":
-                material.material = Material.RED_MUSHROOM;
-                break;
-            case "torch":
-                material.material = Material.TORCH;
-                break;
-            case "redstone torch (off)":
-                material.material = Material.REDSTONE_TORCH_OFF;
-                break;
-            case "redstone torch (on)":
-                material.material = Material.REDSTONE_TORCH_ON;
-                break;
-            case "fence":
-                material.material = Material.FENCE;
-                break;
-            case "nether portal":
-                material.material = Material.PORTAL;
-                break;
-            case "iron bars":
-                material.material = Material.IRON_FENCE;
-                break;
-            case "glass pane":
-                material.material = Material.THIN_GLASS;
-                break;
-            case "fence gate":
-                material.material = Material.FENCE_GATE;
-                break;
-            case "nether brick fence":
-                material.material = Material.NETHER_FENCE;
-                break;
-            case "nether wart":
-                material.material = Material.NETHER_WARTS;
-                break;
-            case "end portal":
-                material.material = Material.ENDER_PORTAL;
-                break;
-            case "cobblestone wall":
-                material.material = Material.COBBLE_WALL;
-                break;
-            default:  // fallback!
-                material.material = Material.PORTAL;
-                Plugin.log(Level.WARNING, "Gate material invalid! Please check and correct your configuration file!");
-                break;
+        try {
+            return new GateMaterial(materialString);
+        } catch (InvalidParameterException ignored) {
+            Plugin.log(Level.WARNING, "Gate material invalid! Please check and correct your configuration file!");
+            return new GateMaterial("nether portal");
         }
-
-        return material;
     }
 }
-
-
-class GateMaterial {
-    public Material material = Material.PORTAL;
-    public byte data = 0;
-}
diff --git a/src/de/craftinc/gates/util/FloodUtil.java b/src/de/craftinc/gates/util/FloodUtil.java
index 7c14265..3430d63 100644
--- a/src/de/craftinc/gates/util/FloodUtil.java
+++ b/src/de/craftinc/gates/util/FloodUtil.java
@@ -44,7 +44,6 @@ public class FloodUtil {
         exp2.add(BlockFace.SOUTH);
     }
 
-
     /**
      * Returns the all frame blocks of an gate.
      *
diff --git a/src/de/craftinc/gates/util/GateBlockChangeSender.java b/src/de/craftinc/gates/util/GateBlockChangeSender.java
index d85d7c5..54a3a8c 100644
--- a/src/de/craftinc/gates/util/GateBlockChangeSender.java
+++ b/src/de/craftinc/gates/util/GateBlockChangeSender.java
@@ -16,10 +16,10 @@
 */
 package de.craftinc.gates.util;
 
-
 import de.craftinc.gates.Plugin;
-import de.craftinc.gates.Gate;
+import de.craftinc.gates.models.Gate;
 
+import de.craftinc.gates.models.GateMaterial;
 import org.bukkit.Bukkit;
 import org.bukkit.Location;
 import org.bukkit.Material;
@@ -31,7 +31,6 @@ import java.util.Set;
 
 import static de.craftinc.gates.util.ConfigurationUtil.*;
 
-
 public class GateBlockChangeSender {
     /**
      * Replaces gate frame blocks with glowstone for a short period of time.
@@ -40,15 +39,12 @@ public class GateBlockChangeSender {
      *
      * @param player The player for whom the frame should be highlighted.
      *               Must not be null!
+     *
+     * @param gates The gates to highlighting
      */
     public static void temporaryHighlightGatesFrames(final Player player, final Set gates) {
-        if (player == null) {
-            throw new IllegalArgumentException("'player' must not be 'null'!");
-        }
-
-        if (gates == null) {
-            throw new IllegalArgumentException("'gate' must not be 'null!");
-        }
+        assert(player != null);
+        assert(gates != null);
 
         for (Gate g : gates) {
             Set frameBlocks = g.getGateFrameBlocks();
@@ -57,66 +53,9 @@ public class GateBlockChangeSender {
                 player.sendBlockChange(b.getLocation(), Material.GLOWSTONE, (byte) 0);
             }
         }
-
-        Plugin plugin = Plugin.getPlugin();
-        long highlightDuration = 20 * plugin.getConfig().getLong(confHighlightDurationKey);
-
-        Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() {
-            @Override
-            public void run() {
-                dehighlightGatesFrames(player, gates);
-            }
-        }, highlightDuration);
+        scheduleDelighting(player, gates);
     }
 
-
-    public static void temporaryHighlightGateFrame(final Player player, final Gate gate) {
-        if (gate == null) {
-            throw new IllegalArgumentException("'gate' must not be 'null!");
-        }
-
-        if (player == null) {
-            throw new IllegalArgumentException("'player' must not be 'null'!");
-        }
-
-        Set frameBlocks = gate.getGateFrameBlocks();
-
-        for (Block b : frameBlocks) {
-            player.sendBlockChange(b.getLocation(), Material.GLOWSTONE, (byte) 0);
-        }
-
-        Plugin plugin = Plugin.getPlugin();
-        long highlightDuration = 20 * plugin.getConfig().getLong(confHighlightDurationKey);
-
-        Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() {
-            @Override
-            public void run() {
-                dehighlightGateFrame(player, gate);
-            }
-        }, highlightDuration);
-    }
-
-
-    private static void dehighlightGatesFrames(final Player player, final Set gates) {
-        for (Gate g : gates) {
-            Set frameBlocks = g.getGateFrameBlocks();
-
-            for (Block b : frameBlocks) {
-                player.sendBlockChange(b.getLocation(), b.getType(), (byte) 0);
-            }
-        }
-    }
-
-
-    private static void dehighlightGateFrame(final Player player, final Gate gate) {
-        Set frameBlocks = gate.getGateFrameBlocks();
-
-        for (Block b : frameBlocks) {
-            player.sendBlockChange(b.getLocation(), b.getType(), (byte) 0);
-        }
-    }
-
-
     /**
      * Sends gate blocks to player at a given location. Will send the updates either immediately or
      * immediately and after a short delay.
@@ -127,33 +66,20 @@ public class GateBlockChangeSender {
      *                    second delay.
      */
     public static void updateGateBlocks(final Player player, final Location location, boolean sendDelayed) {
-        if (player == null) {
-            throw new IllegalArgumentException("'player' must not be 'null'!");
-        }
-
-        if (location == null) {
-            throw new IllegalArgumentException("'location' must not be 'null'!");
-        }
+        assert(player != null);
+        assert(location != null);
 
         Set gatesNearby = Plugin.getPlugin().getGatesManager().getNearbyGates(location.getChunk());
-        GateMaterial gateMaterial = getPortalMaterial();
 
         if (gatesNearby == null) {
             return; // no gates nearby
         }
 
         for (Gate g : gatesNearby) {
-
             if (!g.isOpen() || g.isHidden()) {
                 continue;
             }
-
-            for (Location l : g.getGateBlockLocations()) {
-
-                if (l.getBlock().getType() == Material.AIR) {
-                    player.sendBlockChange(l, gateMaterial.material, gateMaterial.data);
-                }
-            }
+            sendGateBlockChanges(g, true, player);
         }
 
         if (sendDelayed) {
@@ -166,25 +92,10 @@ public class GateBlockChangeSender {
         }
     }
 
-
-    /**
-     * This method calls: updateGateBlocks(player, location, false);
-     */
     public static void updateGateBlocks(final Player player, final Location location) {
         updateGateBlocks(player, location, false);
     }
 
-    /**
-     * This method calls: updateGateBlocks(player, player.getLocation(), false);
-     */
-    public static void updateGateBlocks(final Player player) {
-        if (player == null) {
-            throw new IllegalArgumentException("'player' must not be 'null'!");
-        }
-
-        updateGateBlocks(player, player.getLocation(), false);
-    }
-
 
     public static void updateGateBlocks(final Gate gate) {
         updateGateBlocks(gate, false);
@@ -197,45 +108,66 @@ public class GateBlockChangeSender {
      * @param remove Set to true if all visible gate blocks shall be removed.
      */
     public static void updateGateBlocks(final Gate gate, boolean remove) {
-        if (gate == null) {
-            throw new IllegalArgumentException("'gate must not be 'null'!");
-        }
+        assert(gate != null);
 
         Location gateLocation = gate.getLocation();
-
         if (gate.getGateBlockLocations().isEmpty()) {
             return;
         }
 
         ArrayList playersNearby = new ArrayList<>();
-
         int searchRadius = Plugin.getPlugin().getConfig().getInt(confPlayerGateBlockUpdateRadiusKey);
 
         for (Player p : Plugin.getPlugin().getServer().getOnlinePlayers()) {
-
             if (p.getWorld() == gateLocation.getWorld() && p.getLocation().distance(gateLocation) < searchRadius) {
                 playersNearby.add(p);
             }
         }
 
-        GateMaterial gateMaterial = getPortalMaterial();
-        Material material;
-        byte data = 0;
+        boolean isVisible = gate.isOpen() && !gate.isHidden() && !remove;
+        for (Player p : playersNearby) {
+            sendGateBlockChanges(gate, isVisible, p);
+        }
+    }
 
-        if (gate.isOpen() && !gate.isHidden() && !remove) {
-            material = gateMaterial.material;
-            data = gateMaterial.data;
+    private static void scheduleDelighting(final Player player, final Set gates) {
+        Plugin plugin = Plugin.getPlugin();
+        long highlightDuration = 20 * plugin.getConfig().getLong(confHighlightDurationKey);
+
+        Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() {
+            @Override
+            public void run() {
+                dehighlightGatesFrames(player, gates);
+            }
+        }, highlightDuration);
+    }
+
+    private static void dehighlightGatesFrames(final Player player, final Set gates) {
+        for (Gate g : gates) {
+            Set frameBlocks = g.getGateFrameBlocks();
+
+            for (Block b : frameBlocks) {
+                player.sendBlockChange(b.getLocation(), b.getType(), (byte) 0);
+            }
+        }
+    }
+
+    private static void sendGateBlockChanges(final Gate gate, boolean isVisible, final Player p) {
+        byte data;
+        Material material;
+
+        if (isVisible) {
+            GateMaterial gm = getPortalMaterial();
+            data = gm.getData(gate.getDirection());
+            material = gm.getMaterial();
         } else {
+            data = 0b0;
             material = Material.AIR;
         }
 
-        for (Player p : playersNearby) {
-
-            for (Location l : gate.getGateBlockLocations()) {
-
-                if (l.getBlock().getType() == Material.AIR) { // on server-side a gate is always made out of AIR
-                    p.sendBlockChange(l, material, data);
-                }
+        for (Location l : gate.getGateBlockLocations()) {
+            if (l.getBlock().getType() == Material.AIR) {
+                p.sendBlockChange(l, material, data);
             }
         }
     }
diff --git a/src/de/craftinc/gates/util/TextUtil.java b/src/de/craftinc/gates/util/TextUtil.java
index a4df65d..92a0b55 100644
--- a/src/de/craftinc/gates/util/TextUtil.java
+++ b/src/de/craftinc/gates/util/TextUtil.java
@@ -46,7 +46,6 @@ public class TextUtil {
         return s + repeat(s, times - 1);
     }
 
-
     /**
      * Joins all elements of list into a single string, separating the original strings with glue.
      */
@@ -64,5 +63,3 @@ public class TextUtil {
         return ret;
     }
 }
-
-
diff --git a/src/de/craftinc/gates/util/VehicleCloner.java b/src/de/craftinc/gates/util/VehicleCloner.java
deleted file mode 100644
index 069555e..0000000
--- a/src/de/craftinc/gates/util/VehicleCloner.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*  Craft Inc. Gates
-    Copyright (C) 2011-2013 Craft Inc. Gates Team (see AUTHORS.txt)
-
-    This program is free software: you can redistribute it and/or modify
-    it under the terms of the GNU Lesser General Public License as published
-    by the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU Lesser General Public License for more details.
-
-    You should have received a copy of the GNU Lesser General Public License
-    along with this program (LGPLv3).  If not, see .
-*/
-package de.craftinc.gates.util;
-
-import org.bukkit.Location;
-import org.bukkit.entity.*;
-
-
-public class VehicleCloner {
-
-    public static Vehicle clone(Vehicle parent, Location cloneLocation) {
-        Vehicle clone = cloneLocation.getWorld().spawn(cloneLocation, parent.getClass());
-
-        clone.setFallDistance(parent.getFallDistance());
-        clone.setFireTicks(parent.getFireTicks());
-        clone.setVelocity(parent.getVelocity());
-        clone.setTicksLived(parent.getTicksLived());
-        clone.setLastDamageCause(parent.getLastDamageCause());
-
-        if (clone instanceof Boat) {
-            Boat boat = (Boat) clone;
-            Boat parentBoat = (Boat) parent;
-
-            boat.setMaxSpeed(parentBoat.getMaxSpeed());
-            boat.setOccupiedDeceleration(parentBoat.getOccupiedDeceleration());
-            boat.setUnoccupiedDeceleration(parentBoat.getUnoccupiedDeceleration());
-            boat.setWorkOnLand(parentBoat.getWorkOnLand());
-            boat.setVelocity(parentBoat.getVelocity());
-        } else if (clone instanceof Animals) {
-            Animals animal = (Animals) clone;
-            Animals parentAnimal = (Animals) parent;
-
-            animal.setMaxHealth(parentAnimal.getMaxHealth());
-            animal.setHealth(parentAnimal.getMaxHealth());
-            animal.setRemainingAir(parentAnimal.getRemainingAir());
-            animal.setMaximumAir(parentAnimal.getMaximumAir());
-            animal.setMaximumNoDamageTicks(parentAnimal.getMaximumNoDamageTicks());
-            animal.setLastDamage(parentAnimal.getLastDamage());
-            animal.setNoDamageTicks(parentAnimal.getNoDamageTicks());
-            animal.addPotionEffects(parentAnimal.getActivePotionEffects());
-            animal.setRemoveWhenFarAway(parentAnimal.getRemoveWhenFarAway());
-            animal.setCanPickupItems(parentAnimal.getCanPickupItems());
-            animal.setCustomName(parentAnimal.getCustomName());
-            animal.setCustomNameVisible(parentAnimal.isCustomNameVisible());
-            animal.setTarget(parentAnimal.getTarget());
-            animal.setAge(parentAnimal.getAge());
-            animal.setAgeLock(parentAnimal.getAgeLock());
-
-            if (clone instanceof Horse) {
-                Horse horse = (Horse) clone;
-                Horse parentHorse = (Horse) parent;
-
-                horse.getInventory().setArmor(parentHorse.getInventory().getArmor());
-                horse.getInventory().setSaddle(parentHorse.getInventory().getSaddle());
-                horse.setCarryingChest(parentHorse.isCarryingChest());
-                horse.getInventory().setContents(parentHorse.getInventory().getContents());
-                horse.setTamed(parentHorse.isTamed());
-                horse.setOwner(parentHorse.getOwner());
-                horse.setJumpStrength(parentHorse.getJumpStrength());
-                horse.setMaxDomestication(parentHorse.getMaxDomestication());
-                horse.setDomestication(parentHorse.getDomestication());
-                horse.setStyle(parentHorse.getStyle());
-                horse.setColor(parentHorse.getColor());
-                horse.setVariant(parentHorse.getVariant());
-
-                if (parentHorse.isAdult()) {
-                    horse.setAdult();
-                } else {
-                    horse.setBaby();
-                }
-
-                horse.setBreed(parentHorse.canBreed());
-            } else if (clone instanceof Pig) {
-                Pig pig = (Pig) clone;
-                Pig parentPig = (Pig) parent;
-
-                pig.setSaddle(parentPig.hasSaddle());
-            }
-        } else if (clone instanceof Minecart) {
-            Minecart minecart = (Minecart) clone;
-            Minecart parentMinecart = (Minecart) parent;
-
-            minecart.setDerailedVelocityMod(parentMinecart.getDerailedVelocityMod());
-            minecart.setFlyingVelocityMod(parentMinecart.getFlyingVelocityMod());
-            minecart.setSlowWhenEmpty(parentMinecart.isSlowWhenEmpty());
-            minecart.setMaxSpeed(parentMinecart.getMaxSpeed());
-            minecart.setDamage(parentMinecart.getDamage());
-            minecart.setVelocity(parentMinecart.getVelocity());
-        }
-
-        return clone;
-    }
-
-}