EvilDoorTrap: A Minecraft Plugin Tutorial (Spigot/Paper)

Build better worlds with Ai powered Minecraft plugins and mods. Instantly generate new items, commands and unique game mechanics


 This plugin creates a malicious trap: whenever a player opens a door, a block of floating, player-seeking TNT is spawned. If the TNT tags the player, they are instantly eliminated.

1. Project Files Structure

FileLocationDescription
plugin.ymlsrc/main/resources/Plugin metadata for the server.
EvilDoorTrap.javacom.yourname.evildoortrapMain plugin class (initialization).
DoorListener.javacom.yourname.evildoortrapHandles the player interaction (door open event).
TNTTracker.javacom.yourname.evildoortrapThe core logic for spawning and tracking the TNT.

2. Plugin Metadata: plugin.yml

This file is mandatory and tells the server where to find your plugin's entry point.

YAML

 # The name of the plugin  
 name: EvilDoorTrap  
 # The current version of the plugin  
 version: 1.0  
 # The main class that extends JavaPlugin.   
 # ***CRITICAL: Replace com.yourname.evildoortrap with your actual package structure.***  
 main: com.yourname.evildoortrap.EvilDoorTrap  
 # The API version for compatibility (e.g., 1.18 or 1.20)  
 api-version: 1.20   
 # Optional command (useful for a simple on/off switch later)  
 commands:  
  toggletrap:  
   description: Toggles the Evil Door Trap plugin.  
   usage: /toggletrap  

3. Main Plugin Class: EvilDoorTrap.java

This is the entry point for your plugin. It handles startup and shutdown tasks.

Java

 package com.yourname.evildoortrap;  
 import org.bukkit.plugin.java.JavaPlugin;  
 public class EvilDoorTrap extends JavaPlugin {  
   // A variable to hold the ID of a repeating task if we use one globally.  
   // Not strictly needed for this design, but good practice for other tasks.  
   // Initialized to -1 to signify 'no task running'.  
   private int tntFollowTaskID = -1;   
   /**  
    * Called when the plugin is enabled (the server starts or the plugin is loaded).  
    */  
   @Override  
   public void onEnable() {  
     // Registers the DoorListener class so it can receive events from the server.  
     getServer().getPluginManager().registerEvents(new DoorListener(this), this);  
     getLogger().info("EvilDoorTrap enabled and watching for door opens!");  
   }  
   /**  
    * Called when the plugin is disabled (the server stops or the plugin is unloaded).  
    */  
   @Override  
   public void onDisable() {  
     // Best practice: if any global repeating tasks were running, stop them here.  
     if (tntFollowTaskID != -1) {  
       getServer().getScheduler().cancelTask(tntFollowTaskID);  
     }  
     getLogger().info("EvilDoorTrap disabled.");  
   }  
   // Getters and Setters (omitted for brevity in blog post, but good practice).  
 }  

4. Event Listener: DoorListener.java

This class listens for players interacting with blocks, specifically doors.

Java

 package com.yourname.evildoortrap;  
 import org.bukkit.block.Block;  
 import org.bukkit.entity.Player;  
 import org.bukkit.event.EventHandler;  
 import org.bukkit.event.Listener;  
 import org.bukkit.event.block.Action;  
 import org.bukkit.event.player.PlayerInteractEvent;  
 public class DoorListener implements Listener {  
   private final EvilDoorTrap plugin;  
   public DoorListener(EvilDoorTrap plugin) {  
     this.plugin = plugin;  
   }  
   /**  
    * This method is called every time a player interacts with the world (e.g., clicks a block).  
    * @param event The PlayerInteractEvent triggered by the player.  
    */  
   @EventHandler  
   public void onPlayerInteract(PlayerInteractEvent event) {  
     // 1. Check if the player is right-clicking a block (necessary for opening doors).  
     if (event.getAction() != Action.RIGHT_CLICK_BLOCK) {  
       return;  
     }  
     Block clickedBlock = event.getClickedBlock();  
     if (clickedBlock == null) {  
       return;  
     }  
     // 2. Check if the block is a door (covers wood, iron, fence gates, etc., if they have "_DOOR" in their name)  
     // This uses a string check for flexibility across different door types/versions.  
     if (clickedBlock.getType().name().contains("_DOOR")) {  
       Player player = event.getPlayer();  
       // 3. Trigger the TNT spawning and tracking logic in a separate helper class.  
       // We spawn the TNT slightly above and centered on the door block.  
       TNTTracker.startTracking(  
         plugin,   
         player,   
         clickedBlock.getLocation().add(0.5, 1.0, 0.5)  
       );  
     }  
   }  
 }  

5. TNT Tracking Logic: TNTTracker.java

This is the core mechanic, using a repeating task to make the TNT follow the player.

Java

 package com.yourname.evildoortrap;  
 import org.bukkit.Location;  
 import org.bukkit.entity.EntityType;  
 import org.bukkit.entity.Player;  
 import org.bukkit.entity.TNTPrimed;  
 import org.bukkit.scheduler.BukkitRunnable;  
 import org.bukkit.util.Vector;  
 public class TNTTracker {  
   // TNT must be within 1.0 block distance (squared) to trigger the kill.  
   private static final double ACTIVATION_DISTANCE_SQUARED = 1.0;   
   // Controls how fast the TNT moves toward the player. Adjust this value!  
   private static final double SPEED_MULTIPLIER = 0.2;   
   public static void startTracking(EvilDoorTrap plugin, Player target, Location spawnLocation) {  
     // --- TNT Setup ---  
     // Spawn the TNTPrimed entity at the door location  
     TNTPrimed tnt = (TNTPrimed) spawnLocation.getWorld().spawnEntity(spawnLocation, EntityType.TNT);  
     // Set the fuse to be extremely long so it doesn't explode naturally  
     tnt.setFuseTicks(Integer.MAX_VALUE);   
     // Disable gravity to make it float/fly towards the player  
     tnt.setGravity(false);   
     // --- Tracking Logic (The Repeating Task) ---  
     // BukkitRunnable creates a task that can be scheduled to run repeatedly.  
     new BukkitRunnable() {  
       @Override  
       public void run() {  
         // Check if the player is still online or if the TNT was removed  
         if (!target.isOnline() || tnt.isDead()) {  
           tnt.remove();  
           this.cancel(); // Stop the repeating task  
           return;  
         }  
         // 1. Check for player collision (detonation)  
         Location tntLoc = tnt.getLocation();  
         Location playerLoc = target.getLocation();  
         // Check distance squared (more efficient than distance check)  
         if (tntLoc.distanceSquared(playerLoc) < ACTIVATION_DISTANCE_SQUARED) {  
           // Kill Logic: TNT is close enough - BOOM!  
           tnt.remove();     // Remove the tracking entity  
           target.setHealth(0.0); // Instantly kill the player  
           // Optional: Send a message to the victim  
           target.sendMessage("§cYou were killed by the Evil Door Trap!");  
           this.cancel(); // Stop tracking, task finished  
           return;  
         }  
         // 2. Movement Logic (Follow the Player)  
         // Calculate the direction vector from the TNT to the Player  
         Vector direction = playerLoc.toVector().subtract(tntLoc.toVector()).normalize();  
         // Apply velocity, scaled by the speed multiplier  
         tnt.setVelocity(direction.multiply(SPEED_MULTIPLIER));  
       }  
     // runTaskTimer(plugin, delay, period)  
     // delay: 0L (start immediately)  
     // period: 2L (run every 2 ticks, or 10 times per second - this makes movement smooth)  
     }.runTaskTimer(plugin, 0L, 2L);   
   }  
 }  
I’m Node — documenting my journey in Tech, AI, and Gaming. Not teaching, just sharing what I learn along the way

Post a Comment

Don't spam here
NextGen Digital Welcome to WhatsApp chat
Howdy! How can we help you today?
Type here...