@Override
 public RailLogic getLogic(MinecartMember<?> member, Block railsBlock) {
   // Get the direction of the rails to find out the logic to use
   BlockFace dir = Util.getPlateDirection(railsBlock);
   if (dir == BlockFace.SELF) {
     // set track direction based on direction of this cart
     dir = FaceUtil.toRailsDirection(member.getDirectionTo());
   }
   return RailLogicHorizontal.get(dir);
 }
 @Override
 public boolean parseSet(String key, String arg) {
   if (key.equals("exitoffset")) {
     Vector vec = Util.parseVector(arg, null);
     if (vec != null) {
       for (CartProperties prop : this) {
         prop.exitOffset = vec;
       }
     }
   } else if (key.equals("exityaw")) {
     float yaw = ParseUtil.parseFloat(arg, 0.0f);
     for (CartProperties prop : this) {
       prop.exitYaw = yaw;
     }
   } else if (key.equals("exitpitch")) {
     float pitch = ParseUtil.parseFloat(arg, 0.0f);
     for (CartProperties prop : this) {
       prop.exitPitch = pitch;
     }
   } else if (LogicUtil.contains(key, "exitrot", "exitrotation")) {
     String[] angletext = Util.splitBySeparator(arg);
     float yaw = 0.0f;
     float pitch = 0.0f;
     if (angletext.length == 2) {
       yaw = ParseUtil.parseFloat(angletext[0], 0.0f);
       pitch = ParseUtil.parseFloat(angletext[1], 0.0f);
     } else if (angletext.length == 1) {
       yaw = ParseUtil.parseFloat(angletext[0], 0.0f);
     }
     for (CartProperties prop : this) {
       prop.exitYaw = yaw;
       prop.exitPitch = pitch;
     }
   } else if (key.equals("sound") || key.equals("minecartsound")) {
     this.soundEnabled = ParseUtil.parseBool(arg);
   } else if (key.equals("mobcollision")) {
     this.mobCollision = CollisionMode.parse(arg);
   } else if (key.equals("playercollision")) {
     this.playerCollision = CollisionMode.parse(arg);
   } else if (key.equals("misccollision")) {
     this.miscCollision = CollisionMode.parse(arg);
   } else if (key.equals("traincollision")) {
     this.trainCollision = CollisionMode.parse(arg);
   } else if (LogicUtil.contains(key, "collision", "collide")) {
     this.setColliding(ParseUtil.parseBool(arg));
   } else if (LogicUtil.contains(key, "linking", "link")) {
     this.trainCollision = CollisionMode.fromLinking(ParseUtil.parseBool(arg));
   } else if (LogicUtil.contains(key, "slow", "slowdown")) {
     this.setSlowingDown(ParseUtil.parseBool(arg));
   } else if (LogicUtil.contains(key, "setdefault", "default")) {
     this.setDefault(arg);
   } else if (key.equals("pushmobs")) {
     this.mobCollision = CollisionMode.fromPushing(ParseUtil.parseBool(arg));
   } else if (key.equals("pushplayers")) {
     this.playerCollision = CollisionMode.fromPushing(ParseUtil.parseBool(arg));
   } else if (key.equals("pushmisc")) {
     this.miscCollision = CollisionMode.fromPushing(ParseUtil.parseBool(arg));
   } else if (LogicUtil.contains(key, "push", "pushing")) {
     CollisionMode mode = CollisionMode.fromPushing(ParseUtil.parseBool(arg));
     this.playerCollision = this.mobCollision = this.miscCollision = mode;
   } else if (LogicUtil.contains(key, "speedlimit", "maxspeed")) {
     this.setSpeedLimit(ParseUtil.parseDouble(arg, 0.4));
   } else if (LogicUtil.contains(key, "allowmanual", "manualmove", "manual")) {
     this.allowManualMovement = ParseUtil.parseBool(arg);
   } else if (LogicUtil.contains(key, "keepcloaded", "loadchunks", "keeploaded")) {
     this.keepChunksLoaded = ParseUtil.parseBool(arg);
   } else if (key.equals("addtag")) {
     this.addTags(arg);
   } else if (key.equals("settag")) {
     this.setTags(arg);
   } else if (key.equals("destination")) {
     this.setDestination(arg);
   } else if (key.equals("remtag")) {
     this.removeTags(arg);
   } else if (LogicUtil.contains(key, "name", "rename", "setname")) {
     this.setName(generateTrainName(arg));
   } else if (LogicUtil.contains(key, "dname", "displayname", "setdisplayname", "setdname")) {
     this.setDisplayName(arg);
   } else if (LogicUtil.contains(key, "mobenter", "mobsenter")) {
     this.mobCollision = CollisionMode.fromEntering(ParseUtil.parseBool(arg));
   } else if (key.equals("playerenter")) {
     this.setPlayersEnter(ParseUtil.parseBool(arg));
   } else if (key.equals("playerexit")) {
     this.setPlayersExit(ParseUtil.parseBool(arg));
   } else if (LogicUtil.contains(key, "invincible", "godmode")) {
     this.setInvincible(ParseUtil.parseBool(arg));
   } else if (key.equals("setownerperm")) {
     for (CartProperties prop : this) {
       prop.clearOwnerPermissions();
       prop.getOwnerPermissions().add(arg);
     }
   } else if (key.equals("addownerperm")) {
     for (CartProperties prop : this) {
       prop.getOwnerPermissions().add(arg);
     }
   } else if (key.equals("remownerperm")) {
     for (CartProperties prop : this) {
       prop.getOwnerPermissions().remove(arg);
     }
   } else if (key.equals("setowner")) {
     arg = arg.toLowerCase();
     for (CartProperties cprop : this) {
       cprop.clearOwners();
       cprop.getOwners().add(arg);
     }
   } else if (key.equals("addowner")) {
     arg = arg.toLowerCase();
     for (CartProperties cprop : this) {
       cprop.getOwners().add(arg);
     }
   } else if (key.equals("remowner")) {
     arg = arg.toLowerCase();
     for (CartProperties cprop : this) {
       cprop.getOwners().remove(arg);
     }
   } else if (LogicUtil.contains(key, "spawnitemdrops", "spawndrops", "killdrops")) {
     this.setSpawnItemDrops(ParseUtil.parseBool(arg));
   } else {
     return false;
   }
   return true;
 }
 public boolean matchName(String[] expressionElements, boolean firstAny, boolean lastAny) {
   return Util.matchText(this.getTrainName(), expressionElements, firstAny, lastAny);
 }
 public boolean matchName(String expression) {
   return Util.matchText(this.getTrainName(), expression);
 }
  @SuppressWarnings("rawtypes")
  public static MinecartMember getAt(World world, ChunkCoordinates coord, boolean checkmoving) {
    net.minecraft.server.Chunk chunk = WorldUtil.getChunk(world, coord.x >> 4, coord.z >> 4);
    if (chunk != null) {
      MinecartMember mm;
      MinecartMember result = null;
      for (List list : chunk.entitySlices) {
        for (Object e : list) {
          if (e instanceof MinecartMember) {
            mm = (MinecartMember) e;
            if (mm.getBlockX() != coord.x) continue;
            if (mm.getBlockY() != coord.y) continue;
            if (mm.getBlockZ() != coord.z) continue;
            result = mm;
            if (result.isHeadingTo(coord)) return result;
          }
        }
      }
      if (result == null && checkmoving) {
        Block b = world.getWorld().getBlockAt(coord.x, coord.y, coord.z);
        int id = b.getTypeId();

        // get the two connected rails to check
        if (BlockUtil.isRails(id)) {
          BlockFace[] possible = FaceUtil.getFaces(BlockUtil.getRails(b).getDirection());
          MinecartMember mm1 = getAt(Util.getRailsBlock(b.getRelative(possible[0])), false);
          MinecartMember mm2 = getAt(Util.getRailsBlock(b.getRelative(possible[1])), false);
          if (mm1 != null && mm2 != null && mm1.group == mm2.group) {
            Location loc = b.getLocation();
            return mm1.distance(loc) < mm2.distance(loc) ? mm1 : mm2;
          } else if (isHeadingTo(mm1, coord)) {
            return mm1;
          } else if (isHeadingTo(mm2, coord)) {
            return mm2;
          } else {
            return null;
          }
        } else if (Util.isPressurePlate(id)) {
          // check all directions
          MinecartMember mm1 = getAt(Util.getRailsBlock(b.getRelative(BlockFace.NORTH)), false);
          MinecartMember mm2 = getAt(Util.getRailsBlock(b.getRelative(BlockFace.SOUTH)), false);
          MinecartMember mm3 = getAt(Util.getRailsBlock(b.getRelative(BlockFace.EAST)), false);
          MinecartMember mm4 = getAt(Util.getRailsBlock(b.getRelative(BlockFace.WEST)), false);
          if (mm1 != null && mm2 != null && mm1.group == mm2.group) {
            Location loc = b.getLocation();
            return mm1.distance(loc) < mm2.distance(loc) ? mm1 : mm2;
          } else if (mm3 != null && mm4 != null && mm3.group == mm4.group) {
            Location loc = b.getLocation();
            return mm3.distance(loc) < mm4.distance(loc) ? mm3 : mm4;
          } else if (isHeadingTo(mm1, coord)) {
            return mm1;
          } else if (isHeadingTo(mm2, coord)) {
            return mm2;
          } else if (isHeadingTo(mm3, coord)) {
            return mm3;
          } else if (isHeadingTo(mm4, coord)) {
            return mm4;
          } else {
            return null;
          }
        }
      }
      return result;
    }
    return null;
  }
 @Override
 public BlockFace getDirection(Block railsBlock) {
   return Util.getPlateDirection(railsBlock);
 }