diff --git a/paper-api/src/main/java/org/bukkit/event/CustomEventListener.java b/paper-api/src/main/java/org/bukkit/event/CustomEventListener.java
new file mode 100644
index 0000000000..fcc0af83da
--- /dev/null
+++ b/paper-api/src/main/java/org/bukkit/event/CustomEventListener.java
@@ -0,0 +1,5 @@
+package org.bukkit.event;
+
+public interface CustomEventListener {
+    public void onCustomEvent(Event event);
+}
diff --git a/paper-api/src/main/java/org/bukkit/event/Event.java b/paper-api/src/main/java/org/bukkit/event/Event.java
index b97ebd94bf..c73148b5a4 100644
--- a/paper-api/src/main/java/org/bukkit/event/Event.java
+++ b/paper-api/src/main/java/org/bukkit/event/Event.java
@@ -6,18 +6,41 @@ package org.bukkit.event;
  */
 public abstract class Event {
     private final Type type;
+    private final String name;
 
     protected Event(final Type type) {
+        exAssert(type != null, "type is null");
+        exAssert(type != Type.CUSTOM_EVENT, "use Event(String) to make custom events");
         this.type = type;
+        this.name = null;
     }
-
+    
+    protected Event(final String name) {
+        exAssert(name != null, "name is null");
+        this.type = Type.CUSTOM_EVENT;
+        this.name = name;
+    }
+    
     /**
      * Gets the Type of this event
-     * @return Server which this event was triggered on
+     * @return Event type that this object represents
      */
-    public Type getType() {
+    public final Type getType() {
         return type;
     }
+    
+    private void exAssert(boolean b, String s) {
+        if(!b) throw new IllegalArgumentException(s);
+    }
+    
+    /**
+     * Gets the event's name. Should only be used if getType returns null.
+     * @return 
+     */
+    public final String getEventName() {
+        if(type!=Type.CUSTOM_EVENT) return type.toString();
+        else return name;
+    }
 
     /**
      * Represents an events priority
@@ -83,7 +106,7 @@ public abstract class Event {
         BLOCK_PHYSICS (Category.BLOCK),
         BLOCK_PLACED (Category.BLOCK),
         BLOCK_RIGHTCLICKED (Category.BLOCK),
-        REDSTONE_CHANGE (Category.BLOCK);
+        REDSTONE_CHANGE (Category.BLOCK),
 
 
         /** 
@@ -124,11 +147,13 @@ public abstract class Event {
          * Sign Events (Item events??)
 
         SIGN_SHOW (Category.SIGN),
-        SIGN_CHANGE (Category.SIGN);
+        SIGN_CHANGE (Category.SIGN)
          */
+        
+        CUSTOM_EVENT (Category.CUSTOM);
 
-        private Category category;
-
+        private final Category category;
+        
         private Type(Category category) {
             this.category = category;
         }
diff --git a/paper-api/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java b/paper-api/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
index 021a396d9d..16fc8056c2 100644
--- a/paper-api/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
+++ b/paper-api/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
@@ -12,6 +12,7 @@ import java.util.jar.JarEntry;
 import java.util.jar.JarFile;
 import java.util.regex.Pattern;
 import org.bukkit.Server;
+import org.bukkit.event.CustomEventListener;
 import org.bukkit.event.Event;
 import org.bukkit.event.Listener;
 import org.bukkit.event.block.*;
@@ -118,6 +119,9 @@ public final class JavaPluginLoader implements PluginLoader {
                 trueListener.onBlockFlow((BlockFromToEvent)event);
                 break;
             }
+        } else if(listener instanceof CustomEventListener) {
+            if(event.getType()==Event.Type.CUSTOM_EVENT) 
+                ((CustomEventListener)listener).onCustomEvent(event);
         }
     }
 }