Skip to main content

Overview

Skript allows addons to register custom syntax elements including conditions, effects, expressions, sections, and events. Each type has specific patterns and methods for registration.

Registration Timing

All syntax registration must occur during plugin initialization:
public static void checkAcceptRegistrations() {
    if (!isAcceptRegistrations() && !Skript.testing())
        throw new SkriptAPIException("Registration can only be done during plugin initialization");
}
Source: /src/main/java/ch/njol/skript/Skript.java:1416-1419

Effect Registration

Effects are executed unconditionally and typically perform actions.

Effect Base Class

public abstract class Effect extends Statement implements SyntaxRuntimeErrorProducer {
    
    protected Effect() {}
    
    /**
     * Executes this effect.
     * @param event The event with which this effect will be executed
     */
    protected abstract void execute(Event event);
    
    @Override
    public final boolean run(Event event) {
        execute(event);
        return true;
    }
}
Source: /src/main/java/ch/njol/skript/lang/Effect.java:21-44
import org.skriptlang.skript.registration.SyntaxInfo;
import org.skriptlang.skript.registration.SyntaxRegistry;

SyntaxRegistry registry = addon.syntaxRegistry();
registry.register(SyntaxRegistry.EFFECT, 
    SyntaxInfo.builder(MyEffect.class)
        .addPatterns("broadcast %objects% [(to|in) %-worlds%]")
        .build()
);

Legacy Registration (Deprecated)

public static <E extends Effect> void registerEffect(Class<E> effectClass, 
        String... patterns) throws IllegalArgumentException {
    checkAcceptRegistrations();
    skript.syntaxRegistry().register(SyntaxRegistry.EFFECT, 
        SyntaxInfo.builder(effectClass)
            .origin(getSyntaxOrigin(effectClass))
            .addPatterns(patterns)
            .build()
    );
}
Source: /src/main/java/ch/njol/skript/Skript.java:1582-1589

Real Example: Broadcast Effect

@Name("Broadcast")
@Description("Broadcasts a message to the server.")
@Example("broadcast \"Welcome %player% to the server!\"")
@Since("1.0")
public class EffBroadcast extends Effect {
    
    static {
        Skript.registerEffect(EffBroadcast.class, 
            "broadcast %objects% [(to|in) %-worlds%]");
    }
    
    private Expression<?> messageExpr;
    private Expression<World> worlds;
    
    @Override
    public boolean init(Expression<?>[] exprs, int matchedPattern, 
            Kleenean isDelayed, ParseResult parseResult) {
        messageExpr = LiteralUtils.defendExpression(exprs[0]);
        worlds = (Expression<World>) exprs[1];
        return LiteralUtils.canInitSafely(messageExpr);
    }
    
    @Override
    public void execute(Event event) {
        List<CommandSender> receivers = new ArrayList<>();
        if (worlds == null) {
            receivers.addAll(Bukkit.getOnlinePlayers());
            receivers.add(Bukkit.getConsoleSender());
        } else {
            for (World world : worlds.getArray(event))
                receivers.addAll(world.getPlayers());
        }
        // Send messages to receivers
    }
    
    @Override
    public String toString(@Nullable Event event, boolean debug) {
        return "broadcast " + messageExpr.toString(event, debug);
    }
}
Source: /src/main/java/ch/njol/skript/effects/EffBroadcast.java:40-110

Condition Registration

Conditions check whether certain criteria are met and return true or false.

Condition Base Class

public abstract class Condition extends Statement implements Conditional<Event> {
    
    private boolean negated;
    
    /**
     * Checks whether this condition is satisfied with the given event.
     * @param event the event to check
     * @return true if the condition is satisfied, false otherwise
     */
    public abstract boolean check(Event event);
    
    @Override
    public final boolean run(Event event) {
        return check(event);
    }
    
    public final boolean isNegated() {
        return negated;
    }
}
Source: /src/main/java/ch/njol/skript/lang/Condition.java:23-112

Condition Types (Priority)

public enum ConditionType {
    /** Conditions that contain other expressions */
    COMBINED(SyntaxInfo.COMBINED),
    
    /** Property conditions, e.g. "%properties% is/are data value[s]" */
    PROPERTY(PropertyCondition.DEFAULT_PRIORITY),
    
    /** Conditions whose pattern matches (almost) everything */
    PATTERN_MATCHES_EVERYTHING(SyntaxInfo.PATTERN_MATCHES_EVERYTHING);
}
Source: /src/main/java/ch/njol/skript/lang/Condition.java:30-65

Modern Registration

registry.register(SyntaxRegistry.CONDITION,
    SyntaxInfo.builder(MyCondition.class)
        .priority(PropertyCondition.DEFAULT_PRIORITY)
        .addPatterns("%offlineplayers% (is|are) [a[n]] op[erator][s]")
        .build()
);

Legacy Registration

public static <E extends Condition> void registerCondition(Class<E> conditionClass, 
        ConditionType type, String... patterns) throws IllegalArgumentException {
    checkAcceptRegistrations();
    skript.syntaxRegistry().register(SyntaxRegistry.CONDITION, 
        SyntaxInfo.builder(conditionClass)
            .priority(type.priority())
            .origin(getSyntaxOrigin(conditionClass))
            .addPatterns(patterns)
            .build()
    );
}
Source: /src/main/java/ch/njol/skript/Skript.java:1562-1570

Real Example: Is Operator Condition

@Name("Is Operator")
@Description("Checks whether a player is a server operator.")
@Example("player is an operator")
@Since("2.7")
public class CondIsOp extends PropertyCondition<OfflinePlayer> {
    
    static {
        register(CondIsOp.class, "[[a] server|an] op[erator][s]", "offlineplayers");
    }
    
    @Override
    public boolean check(OfflinePlayer player) {
        return player.isOp();
    }
    
    @Override
    protected String getPropertyName() {
        return "op";
    }
}
Source: /src/main/java/ch/njol/skript/conditions/CondIsOp.java:14-30

Expression Registration

Expressions return values that can be used in other syntax elements.

Modern Registration

registry.register(SyntaxRegistry.EXPRESSION,
    SyntaxInfo.Expression.builder(ExprAI.class, Boolean.class)
        .addPatterns("(ai|artificial intelligence) of %livingentities%")
        .build()
);

Legacy Registration

public static <E extends Expression<T>, T> void registerExpression(
        Class<E> expressionClass, Class<T> returnType, 
        ExpressionType type, String... patterns) throws IllegalArgumentException {
    checkAcceptRegistrations();
    skript.syntaxRegistry().register(SyntaxRegistry.EXPRESSION, 
        SyntaxInfo.Expression.builder(expressionClass, returnType)
            .priority(type.priority())
            .origin(getSyntaxOrigin(expressionClass))
            .addPatterns(patterns)
            .build()
    );
}
Source: /src/main/java/ch/njol/skript/Skript.java:1675-1685

Real Example: AI Expression

@Name("Entity AI")
@Description("Returns whether an entity has AI.")
@Example("set artificial intelligence of target entity to false")
@Since("2.5")
public class ExprAI extends SimplePropertyExpression<LivingEntity, Boolean> {
    
    static {
        register(ExprAI.class, Boolean.class, 
            "(ai|artificial intelligence)", "livingentities");
    }
    
    @Override
    @Nullable
    public Boolean convert(LivingEntity entity) {
        return entity.hasAI();
    }
    
    @Nullable
    @Override
    public Class<?>[] acceptChange(Changer.ChangeMode mode) {
        return mode == Changer.ChangeMode.SET ? 
            CollectionUtils.array(Boolean.class) : null;
    }
    
    @Override
    public void change(Event event, @Nullable Object[] delta, 
            Changer.ChangeMode mode) {
        if (delta == null || delta[0] == null)
            return;
        boolean value = (Boolean) delta[0];
        for (LivingEntity entity : getExpr().getArray(event)) {
            entity.setAI(value);
        }
    }
    
    @Override
    public Class<? extends Boolean> getReturnType() {
        return Boolean.class;
    }
    
    @Override
    protected String getPropertyName() {
        return "artificial intelligence";
    }
}
Source: /src/main/java/ch/njol/skript/expressions/ExprAI.java:19-57

Event Registration

Events define when a trigger should be executed.

Modern Registration

import org.skriptlang.skript.bukkit.registration.BukkitSyntaxInfos;

registry.register(BukkitSyntaxInfos.Event.KEY,
    BukkitSyntaxInfos.Event.builder(MyEvent.class, "My Event")
        .addBukkitEvents(PlayerJoinEvent.class)
        .addPatterns("[on] player join[ing]")
        .build()
);

Legacy Registration

public static <E extends SkriptEvent> SkriptEventInfo<E> registerEvent(
        String name, Class<E> eventClass, 
        Class<? extends Event>[] events, String... patterns) {
    checkAcceptRegistrations();
    for (int i = 0; i < patterns.length; i++)
        patterns[i] = BukkitSyntaxInfos.fixPattern(patterns[i]);
    var legacy = new SkriptEventInfo.ModernSkriptEventInfo<>(
        name, patterns, eventClass, "", events);
    skript.syntaxRegistry().register(BukkitSyntaxInfos.Event.KEY, legacy);
    return legacy;
}
Source: /src/main/java/ch/njol/skript/Skript.java:1753-1762

Section Registration

Sections are syntax elements that can contain other code blocks.
public static <E extends Section> void registerSection(Class<E> sectionClass, 
        String... patterns) throws IllegalArgumentException {
    checkAcceptRegistrations();
    skript.syntaxRegistry().register(SyntaxRegistry.SECTION, 
        SyntaxInfo.builder(sectionClass)
            .origin(getSyntaxOrigin(sectionClass))
            .addPatterns(patterns)
            .build()
    );
}
Source: /src/main/java/ch/njol/skript/Skript.java:1602-1609 Example Section:
public class SecLoop extends Section {
    @Override
    public boolean init(Expression<?>[] exprs, int matchedPattern, 
                       Kleenean isDelayed, ParseResult parseResult, 
                       SectionNode sectionNode, List<TriggerItem> triggerItems) {
        // Initialize the section
        loadCode(sectionNode);
        return true;
    }
    
    @Override
    protected TriggerItem walk(Event event) {
        // Execute the section's code
        return walk(event, true);
    }
}

// Register the section
Skript.registerSection(SecLoop.class, 
    "loop %number% times",
    "repeat %number% times"
);

Structure Registration

Structures are top-level syntax elements that define script structure (like functions, commands, options).

Registration Methods

// Simple structure registration
public static <E extends Structure> void registerStructure(
    Class<E> structureClass, String... patterns)

// Structure with priority and entry container
public static <E extends Structure> void registerStructure(
    Class<E> structureClass, 
    Priority priority,
    String... patterns)

// Simple structure (shorthand)
public static <E extends Structure> void registerSimpleStructure(
    Class<E> structureClass, String... patterns)
Source: /src/main/java/ch/njol/skript/Skript.java:1770-1825 Example Structure:
public class StructFunction extends Structure {
    @Override
    public boolean init(Literal<?>[] args, int matchedPattern, 
                       ParseResult parseResult, EntryContainer entryContainer) {
        // Parse function definition
        String functionName = parseResult.regexes.get(0).group();
        // ... initialize function
        return true;
    }
    
    @Override
    public boolean load() {
        // Load the structure into Skript
        return true;
    }
    
    @Override
    public String toString(@Nullable Event e, boolean debug) {
        return "function definition";
    }
}

// Register the structure
Skript.registerStructure(StructFunction.class,
    "function <.+>\\(.*\\)(?: :: <.+>)?:"
);
Structure Priority: Structures can have different priorities to control parsing order:
  • Priority.LATE - Parsed after other structures
  • Default priority - Standard parsing order
  • Priority.EARLY - Parsed before other structures
// Register with specific priority
Skript.registerStructure(StructOptions.class, 
    Priority.EARLY,
    "options:"
);

Pattern Syntax

Skript patterns support various features:
  • %type% - Required expression of a type
  • %-type% - Optional expression
  • [optional] - Optional literal text
  • (option1|option2) - Multiple options
  • <.+> - Regex patterns

Best Practices

Use descriptive patterns

Make patterns readable and intuitive for users

Support variations

Use (option1|option2) for common synonyms

Document your syntax

Use @Name, @Description, @Example annotations

Test thoroughly

Write comprehensive tests for all patterns

Build docs developers (and LLMs) love