Skip to main content

Overview

Custom events allow you to create new trigger points in Skript that users can listen to. This enables deep integration between your addon and Skript scripts.

Creating a SkriptEvent

All Skript events extend the SkriptEvent abstract class and define when and how they should be triggered.

Basic Structure

import ch.njol.skript.lang.SkriptEvent;
import ch.njol.skript.lang.Literal;
import ch.njol.skript.lang.SkriptParser.ParseResult;
import org.bukkit.event.Event;

public class MyCustomEvent extends SkriptEvent {
    
    @Override
    public boolean init(Literal<?>[] args, int matchedPattern, 
            ParseResult parseResult) {
        // Initialize event-specific data
        return true;
    }
    
    @Override
    public boolean check(Event event) {
        // Determine if this event applies to the Bukkit event
        return true;
    }
    
    @Override
    public String toString(@Nullable Event event, boolean debug) {
        return "my custom event";
    }
}

Real Example: Test Case Event

Here’s a real example from Skript’s testing framework:
@NoDoc
public class EvtTestCase extends SkriptEvent {
    
    static {
        if (TestMode.ENABLED && !TestMode.GEN_DOCS) {
            Skript.registerEvent("Test Case", EvtTestCase.class, 
                SkriptTestEvent.class, "test %string% [when <.+>]")
                .description("Contents represent one test case.")
                .examples("")
                .since("2.5");
            
            EventValues.registerEventValue(SkriptTestEvent.class, 
                Block.class, ignored -> SkriptJUnitTest.getBlock());
            EventValues.registerEventValue(SkriptTestEvent.class, 
                Location.class, ignored -> SkriptJUnitTest.getTestLocation());
            EventValues.registerEventValue(SkriptTestEvent.class, 
                World.class, ignored -> SkriptJUnitTest.getTestWorld());
        }
    }
    
    private Literal<String> name;
    @Nullable
    private Condition condition;
    
    @Override
    @SuppressWarnings("unchecked")
    public boolean init(Literal<?>[] args, int matchedPattern, 
            ParseResult parseResult) {
        name = (Literal<String>) args[0];
        if (!parseResult.regexes.isEmpty()) {
            String cond = parseResult.regexes.get(0).group();
            condition = Condition.parse(cond, 
                "Can't understand this condition: " + cond);
        }
        return true;
    }
    
    @Override
    public boolean check(Event event) {
        String n = name.getSingle();
        if (n == null)
            return false;
        Skript.info("Running test case " + n);
        TestTracker.testStarted(n);
        return true;
    }
    
    @Override
    public boolean shouldLoadEvent() {
        return condition != null ? condition.check(new SkriptTestEvent()) : true;
    }
    
    @Override
    public String toString(@Nullable Event event, boolean debug) {
        return "test " + name.getSingle();
    }
}
Source: /src/main/java/ch/njol/skript/test/runner/EvtTestCase.java:14-69

Registering Event Values

Event values allow expressions to retrieve data from your custom event.

Registration

import ch.njol.skript.registrations.EventValues;
import org.skriptlang.skript.lang.converter.Converter;

// Register what values are available in this event
EventValues.registerEventValue(SkriptTestEvent.class, 
    Block.class, 
    ignored -> SkriptJUnitTest.getBlock());

EventValues.registerEventValue(SkriptTestEvent.class, 
    Location.class, 
    ignored -> SkriptJUnitTest.getTestLocation());

EventValues.registerEventValue(SkriptTestEvent.class, 
    World.class, 
    ignored -> SkriptJUnitTest.getTestWorld());
Source: /src/main/java/ch/njol/skript/test/runner/EvtTestCase.java:23-26

Usage in Scripts

Once registered, users can access these values:
on test "my test":
    set {_block} to event-block  # Returns the Block
    set {_loc} to event-location # Returns the Location
    set {_world} to event-world  # Returns the World

Creating Custom Bukkit Events

Often you’ll want to create your own Bukkit event that Skript can listen to.

Define the Bukkit Event

import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.bukkit.entity.Player;

public class CustomPlayerEvent extends Event {
    
    private static final HandlerList handlers = new HandlerList();
    private final Player player;
    private final String message;
    
    public CustomPlayerEvent(Player player, String message) {
        this.player = player;
        this.message = message;
    }
    
    public Player getPlayer() {
        return player;
    }
    
    public String getMessage() {
        return message;
    }
    
    @Override
    public HandlerList getHandlers() {
        return handlers;
    }
    
    public static HandlerList getHandlerList() {
        return handlers;
    }
}

Create the SkriptEvent

import ch.njol.skript.lang.SkriptEvent;
import ch.njol.skript.lang.Literal;
import ch.njol.skript.lang.SkriptParser.ParseResult;
import ch.njol.skript.registrations.EventValues;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;

public class EvtCustomPlayer extends SkriptEvent {
    
    static {
        Skript.registerEvent("Custom Player Event", 
            EvtCustomPlayer.class, 
            CustomPlayerEvent.class,
            "[on] custom player event"
        );
        
        // Register event values
        EventValues.registerEventValue(CustomPlayerEvent.class, 
            Player.class, 
            CustomPlayerEvent::getPlayer
        );
        
        EventValues.registerEventValue(CustomPlayerEvent.class, 
            String.class, 
            CustomPlayerEvent::getMessage
        );
    }
    
    @Override
    public boolean init(Literal<?>[] args, int matchedPattern, 
            ParseResult parseResult) {
        return true;
    }
    
    @Override
    public boolean check(Event event) {
        // All CustomPlayerEvents should trigger this
        return event instanceof CustomPlayerEvent;
    }
    
    @Override
    public String toString(@Nullable Event event, boolean debug) {
        return "custom player event";
    }
}

Fire the Event

import org.bukkit.Bukkit;

public void triggerCustomEvent(Player player, String message) {
    CustomPlayerEvent event = new CustomPlayerEvent(player, message);
    Bukkit.getPluginManager().callEvent(event);
}

Conditional Event Loading

You can control when events are loaded using shouldLoadEvent():
@Override
public boolean shouldLoadEvent() {
    // Only load this event if a condition is met
    return condition != null ? 
        condition.check(new SkriptTestEvent()) : true;
}
Source: /src/main/java/ch/njol/skript/test/runner/EvtTestCase.java:56-58 This allows you to:
  • Enable events only in certain game versions
  • Require specific plugins to be installed
  • Check configuration settings

Advanced Pattern Matching

Events can have complex patterns with captures:
static {
    Skript.registerEvent("Test Case", EvtTestCase.class, 
        SkriptTestEvent.class, 
        "test %string% [when <.+>]"  // Captures string and optional regex
    );
}

@Override
public boolean init(Literal<?>[] args, int matchedPattern, 
        ParseResult parseResult) {
    name = (Literal<String>) args[0];  // The %string% capture
    
    // Access regex captures
    if (!parseResult.regexes.isEmpty()) {
        String cond = parseResult.regexes.get(0).group();
        condition = Condition.parse(cond, 
            "Can't understand this condition: " + cond);
    }
    return true;
}
Source: /src/main/java/ch/njol/skript/test/runner/EvtTestCase.java:29-42

Event Documentation

Use Skript’s documentation annotations:
import ch.njol.skript.doc.*;

@Name("My Custom Event")
@Description("Triggered when something custom happens.")
@Examples({
    "on custom event:",
    "    broadcast \"Custom event triggered!\""
})
@Since("1.0")
public class EvtCustom extends SkriptEvent {
    // Implementation
}

Complete Example

package com.example.addon.events;

import ch.njol.skript.Skript;
import ch.njol.skript.lang.SkriptEvent;
import ch.njol.skript.lang.Literal;
import ch.njol.skript.lang.SkriptParser.ParseResult;
import ch.njol.skript.registrations.EventValues;
import ch.njol.skript.doc.*;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.jetbrains.annotations.Nullable;

@Name("Player Level Up")
@Description("Triggered when a player levels up in your custom system.")
@Examples({
    "on player level up:",
    "    send \"Congratulations! You are now level %event-number%!\" to player"
})
@Since("1.0")
public class EvtPlayerLevelUp extends SkriptEvent {
    
    static {
        Skript.registerEvent("Player Level Up", 
            EvtPlayerLevelUp.class,
            PlayerLevelUpEvent.class,  // Your custom Bukkit event
            "[on] player level[ing] up",
            "[on] player level increase"
        );
        
        // Register event values
        EventValues.registerEventValue(PlayerLevelUpEvent.class,
            Player.class,
            PlayerLevelUpEvent::getPlayer
        );
        
        EventValues.registerEventValue(PlayerLevelUpEvent.class,
            Number.class,
            event -> event.getNewLevel()
        );
    }
    
    @Override
    public boolean init(Literal<?>[] args, int matchedPattern, 
            ParseResult parseResult) {
        // No special initialization needed
        return true;
    }
    
    @Override
    public boolean check(Event event) {
        return event instanceof PlayerLevelUpEvent;
    }
    
    @Override
    public String toString(@Nullable Event event, boolean debug) {
        return "player level up";
    }
}

Best Practices

Always register relevant event values so users can access data from your event using expressions like event-player or event-string.
Make event patterns intuitive and similar to existing Skript patterns.
Provide examples and descriptions so users understand when and how to use your event.
Always check for null values in event data and handle them gracefully.

Next Steps

Testing

Learn how to test your custom events and syntax

Addon Development

Back to addon development overview

Build docs developers (and LLMs) love