Trouble cancelling VehicleEntityCollisionEvent

Discussion in 'Plugin Development' started by Bobcat00, Jun 29, 2014.

Thread Status:
Not open for further replies.
  1. Offline

    Bobcat00

    I'm tired of my minecarts running into cows and other obstructions, so I wrote a plugin that's called upon a VehicleEntityCollisionEvent. After performing some checks, it teleports the cow or whatever out of the way and cancels the event. The teleport works fine, but the event isn't always canceled, and the minecart stops. I think I'm doing everything right, but maybe you guys will spot something. Calling setCollisionCancelled or just setCancelled produce the same results.

    I'm having a similar problem with a minecart running into an empty minecart. But this is even worse, because many times the event isn't fired at all. Any ideas?

    Code:
    package com.bobcat00.minecartcollisions;
    import org.bukkit.ChatColor;
    import org.bukkit.Location;
    import org.bukkit.entity.Animals;
    import org.bukkit.entity.Entity;
    import org.bukkit.entity.Minecart;
    import org.bukkit.entity.Monster;
    import org.bukkit.entity.NPC;
    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.VehicleEntityCollisionEvent;
    public final class Listeners implements Listener
    {
     private MinecartCollisions plugin;
     
     public Listeners(MinecartCollisions plugin)
     {
      this.plugin = plugin;
     }
     
        @EventHandler(priority = EventPriority.HIGH)
        public void onVehicleEntityCollision(VehicleEntityCollisionEvent event)
        {
         if (event.isCancelled()) return;
         
         if (!(event.getVehicle() instanceof Minecart)) return;
         
         Minecart minecart = (Minecart) event.getVehicle();
         
         if (!minecart.isEmpty() && (minecart.getPassenger() instanceof Player))
         {
          Player player = (Player) minecart.getPassenger();
          
                if (player.hasPermission("minecartcollisions.handle"))
                {
                    // player.sendMessage(ChatColor.LIGHT_PURPLE + "[MinecartCollisions] " + ChatColor.WHITE + "Collision");
                    
                    // Player is riding in a minecart and player has the required permission
                    // Now inspect the entity and decide what, if anything, to do
                    
                    Entity entity = event.getEntity();
                    
                    if (((entity instanceof Monster) || (entity instanceof Animals) || (entity instanceof NPC)) &&
                        !entity.isInsideVehicle())
                    {
                     // Entity is a monster, animal, or NPC and is not riding in a minecart.
                     // Move the entity further away from the minecart.
                     // This may kill the entity (sucks to be him!).
                     
                     Location minecartLocation = minecart.getLocation();
                     double minecartX = minecartLocation.getX();
                     double minecartZ = minecartLocation.getZ();
                     
                     Location entityLocation = entity.getLocation();
                     double entityX = entityLocation.getX();
                     double entityZ = entityLocation.getZ();
                     double newX = entityX;
                     double newZ = entityZ;
                     
                     if (entityX < minecartX)
                      newX = entityX - 0.8;
                     else
                      newX = entityX + 0.8;
                     
                     if (entityZ < minecartZ)
                      newZ = entityZ - 0.8;
                     else
                      newZ = entityZ + 0.8;
                     
                     entityLocation.setX(newX);
                     entityLocation.setZ(newZ);
                     entity.teleport(entityLocation);
                     
                     // Cancel the event
                     event.setCollisionCancelled(true);
                     
                        if (player.hasPermission("minecartcollisions.debug"))
                        {
                            player.sendMessage(ChatColor.LIGHT_PURPLE + "[MinecartCollisions] " + ChatColor.WHITE + 
                              "Teleported " + entity.getType() + " to " +
                              newX + ", " + newZ);
                        }
                     
                    } else if ((entity instanceof Minecart) && entity.isEmpty())
                    {
                     //plugin.getLogger().info("entity " + entity.getType() + "isEmpty=" + entity.isEmpty());
                     
                     // Remove abandoned minecart blocking the tracks
                     entity.remove();
                     
                     // Cancel the event
                     event.setCollisionCancelled(true);
                     
                        if (player.hasPermission("minecartcollisions.debug"))
                        {
                            player.sendMessage(ChatColor.LIGHT_PURPLE + "[MinecartCollisions] " + ChatColor.WHITE + 
                              "Removed " + entity.getType());
                        }
                   
                   }
                    
                } // end if player.hasPermission
         } // end if minecart is carrying a player
         
        } // end event handler
     
    }
    
     
  2. Offline

    NoLiver92

    Bobcat00 With the minecradt stopping, why not apply a velocity to it to push it again? wouldnt this stop the minecraft stopping after a collision?

    I have no idea about the empty minecart, sorry
     
  3. Offline

    Bobcat00

    Well, it looks like this bug has been in Bukkit for at least 2 1/2 years: :(
    So I added a debug statement to print out the minecart's velocity during the event. The velocity has already been changed to zero or even reversed direction. Yes, I could change it, but I don't know what it was before the event occurred (or even which direction the minecart was moving).

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: Jun 9, 2016
  4. Offline

    unrealdesign

    If you 100% need this for your plugin, a resource heavy approach that could fix this is:

    Every tick, save the Vehicle in a list/set. Then on the event trigger, set it to the previous velocity. If it doesn't work every tick, then just increase it till it's at the lowest possible tick rate.

    I'm not sure this will even work, but it is a possibility :p
     
  5. Offline

    MCForger

    Bobcat00 unrealdesign
    You could just listen for the VehicleMoveEvent and put the velocity in a HashMap as a value and have the minecarts uuid as the key. Just make sure you are only adding Minecarts to the HashMap so you do not fill it with other vehicle types.
     
  6. Offline

    Bobcat00

    unrealdesign MCForger - There's a similar plugin from 2012 that listens for VehicleUpdateEvent, calls getNearbyEntities, and moves anything within 0.8 blocks of the minecart. So I guess this works around the problem by moving the entities before the collision actually occurs.

    It just seems to wasteful to check for this for every vehicle update every time instead of using VehicleEntityCollisionEvent, which hapens rarely.
     
  7. Offline

    Bobcat00

    I changed my plugin to use VehicleMoveEvent, and I can teleport things out of the way before a collision actually occurs. This solves the velocity problem. I checked the plugin timing, and my plugin is actually using less time than WorldGuard uses for the exact same event.

    Question: I'm looking for entities within 1 block of the minecart and moving them 1 block further away in the x and z dimensions. Does that sound optimal? Or should I use a number like 0.8 blocks instead? Not sure what makes the most sense.
     
Thread Status:
Not open for further replies.

Share This Page