-
Welcome to skUnity!
Welcome to skUnity! This is a forum where members of the Skript community can communicate and interact. Skript Resource Creators can post their Resources for all to see and use.
If you haven't done so already, feel free to join our official Discord server to expand your level of interaction with the comminuty!
Now, what are you waiting for? Join the community now!
Dismiss Notice
This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.
Creating an Expression
-
Creating an Expression
Let's start with a SimpleExpression this is a class of Skript. A simple expression is something that returns a value, this can be a list or a single value. It can also be set, add, remove, delete and etc. So lets go through all the methods of a SimpleExpression one by one and create one ourselves.
First we need to create a new class, and a new Package to support our syntax. Let's create another package inside our elements package, so the name should be "me.limeglass.addon.elements.expressions" you right click the elements package to create a sub package of it.
So then after you created a sub package of elements. Create a new Class in the package "me.limeglass.addon.elements.expressions"
When naming expressions it is a Skript recommendation to call it ExprNAME. This is just how Skript likes to name it's expressions. Again it's your addon, but using these naming methods are highly recommended as it helps other developers understand your coding if someone reports an error or something.
So for my example i'm going to create a new class called ExprExample and it's going to extend SimpleExpression.
Now SimpleExpressions need a generic type. Which is the Object inside the SimpleExpression<stuff>
The "stuff" that goes inside this, should be your returning type. So if I wanted to return the name of a player.
With the syntax:
Code (Skript):- [the] name of %player%
This would be returning a String. Strings are text surrounded by "quotes". We need to extend this expression with a string. So our setup will look like this:
Code (Java):- package me.limeglass.addon.elements.expressions;
- import org.bukkit.event.Event;
- import org.eclipse.jdt.annotation.Nullable;
- import ch.njol.util.Kleenean;
- public class ExprExample extends SimpleExpression<String> {
- @Override
- public Class<? extends String> getReturnType() {
- //1
- return null;
- }
- @Override
- public boolean isSingle() {
- //2
- return true;
- }
- @Override
- public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parser) {
- //3
- return false;
- }
- @Override
- //4
- return null;
- }
- @Override
- @Nullable
- //5
- return null;
- }
- }
So now let's go over each method of this SimpleExpression.
getReturnType() method should return the class of the type you want to return. This copies the Generic type we specified in the extends portion. And we wanted a String. So it should return String.class
Code (Java):- @Override
- public Class<? extends String> getReturnType() {
- }
Code (Java):- @Override
- public boolean isSingle() {
- return true;
- }
The matchedPattern is a system that returns between 0-X X being the amount of strings your syntax supports.
isDelayed is a boolean to tell if it's as the event happens or a tick after the event has happened. This is for telling if the user has waited a second or not. Some times methods need to have no delay in them.
parser is a system to help with advanced syntax. I will go over this in the Tips and Tricks section.
Code (Java):- package me.limeglass.addon.elements.expressions;
- import org.bukkit.entity.Player;
- import org.bukkit.event.Event;
- import org.eclipse.jdt.annotation.Nullable;
- import ch.njol.util.Kleenean;
- public class ExprExample extends SimpleExpression<String> {
- private Expression<Player> player;
- @Override
- public Class<? extends String> getReturnType() {
- }
- @Override
- public boolean isSingle() {
- return true;
- }
- @SuppressWarnings("unchecked")
- @Override
- public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parser) {
- player = (Expression<Player>) exprs[0];
- return true;
- }
- @Override
- //Still needing to explain
- return null;
- }
- @Override
- @Nullable
- //Still needing to explain
- return null;
- }
- }
toString() this is a system to help debug errors that may arise from users reporting issues or anything in between. So we need something that will be helpful for us to understand what is going on.
Code (Java):
get() this is the main method we need. This is where all the juice is. Here we return what we want. In our case we want to get the name of the player. So let's first grab our player from our Expression<Player> then we return the name of the player. We need to return as an Array. This is just how Skript works. So here is how we can do that:
Code (Java):
Ok now you might be wondering: Why do I have this @Nullable thing and why is it red. This is an annotation that allows something like a method, field or parameter to be null. In order for Eclipse users to fix this you can hover over the @Nullable and click "Copy library with default null annotations to build path"
So now we need to register the Syntax of this expression. Well now I can tell you about that loadClasses() method we used in our onEnable(). This method will loop through all classes in the package we specified. So in our case, it will loop all the classes in the packages "elements" and when it Loops through all these classes. Skript is going to call a static method if it exists. So we can register our expression in that. Here is an example of it:
Code (Java):
SIMPLE
Expressions that only match simple text, e.g. "[the] player" (Contain no types)
COMBINED
Expressions that contain other expressions, e.g. "[the] distance between %location% and %location%" (Contains two or more types)
PROPERTY
Property expressions, e.g:
Code (Skript):- [the] data value of %items%
- #or
- %items%['s] data value
PATTERN_MATCHES_EVERYTHING
Expressions whose pattern matches (almost) everything, e.g. "[the] [event-]<.+>" (Used for calculating advanced syntax)
So you may be wondering now why I used COMBINED instead of PROPERTY, because clearly it states that COMBINED is only used for more than two types when we only have one lol. So you're right, I'm only using this for the example. You could also use SIMPLE. The syntax still registers regardless of the type you use, but you should be using the correct one based on your syntax. I will show you a better way of doing Property syntax with single types below. Using the PropertyExpression class.
But first lets take a look at what we have right now, so we're both on the same page:
Code (Java):- package me.limeglass.addon.elements.expressions;
- import org.bukkit.entity.Player;
- import org.bukkit.event.Event;
- import org.eclipse.jdt.annotation.Nullable;
- import ch.njol.util.Kleenean;
- public class ExprExample extends SimpleExpression<String> {
- static {
- }
- private Expression<Player> player;
- @Override
- public Class<? extends String> getReturnType() {
- }
- @Override
- public boolean isSingle() {
- return true;
- }
- @SuppressWarnings("unchecked")
- @Override
- public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parser) {
- player = (Expression<Player>) exprs[0];
- return true;
- }
- @Override
- return "Example expression with expression player: " + player.toString(event, debug);
- }
- @Override
- @Nullable
- Player p = player.getSingle(event);
- if (p != null) {
- }
- return null;
- }
- }
Then check mark your addon if it's not already, find a location to output the jar and click finish
So it works now. You can do
Code (Skript):- command /test:
- trigger:
- broadcast "%name of player%"
If you want to create another expression, you can create a new class in the elements.expressions package we just created.
Quick TIP: For some reason Skript doesn't like to handle Integers/int in the Skript API. It does weird mechanics, like expressions not working, can't register and other weirdness. So it's always best to work with Numbers. If something asks for an Integer, transfer and return it as a Number. If the user needs to set it as an Integer. Use Numbers as the input and parse it as an integer.
number.intValue();
So now lets get into how to set, add, remove and all that stuff. How can I add that to this expression? Well here is how we can start; there are two new methods we have to add to our expression we just made. These methods are acceptChange() and change() these two methods are what allow Skript to add functionality to our expressions.
So let's add these two methods. This is what our ExprExample class should look like now:
Code (Java):- package me.limeglass.addon.elements.expressions;
- import org.bukkit.entity.Player;
- import org.bukkit.event.Event;
- import org.eclipse.jdt.annotation.Nullable;
- import ch.njol.util.Kleenean;
- public class ExprExample extends SimpleExpression<String> {
- static {
- }
- private Expression<Player> player;
- @Override
- public Class<? extends String> getReturnType() {
- }
- @Override
- public boolean isSingle() {
- return true;
- }
- @SuppressWarnings("unchecked")
- @Override
- public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parser) {
- player = (Expression<Player>) exprs[0];
- return true;
- }
- @Override
- return "Example expression with expression player: " + player.toString(event, debug);
- }
- @Override
- @Nullable
- Player p = player.getSingle(event);
- if (p != null) {
- }
- return null;
- }
- @Override
- //Still explaining this
- }
- @Override
- public Class<?>[] acceptChange(final ChangeMode mode) {
- //Still explaining this
- return null;
- }
- }
DELETE = clear %expression%; delete %expression%
These allow you to do magical things to your expressions such as these Skript code snippets:
Code (Skript):- set name of player to "&6Example name"
- reset name of player
- remove "example" from name of player #Makes sense if your expression was a list
Code (Java):- @Override
- public Class<?>[] acceptChange(final ChangeMode mode) {
- if (mode == ChangeMode.DELETE || mode == ChangeMode.SET || mode == ChangeMode.RESET) {
- }
- return null;
- }
Now we need to execute what we promised to Skript that this Expression could allow. We do this in the change() method:
Code (Java):- @Override
- Player p = player.getSingle(event);
- if (p != null) {
- if (mode == ChangeMode.SET) {
- } else if (mode == ChangeMode.DELETE || mode == ChangeMode.RESET) {
- p.setDisplayName(p.getName()); //This will reset their display name back to their original name
- }
- }
- }
And that's how to create an expression yay!
Property Expressions:
Now let's get into a better way of doing single type expressions. Such as the PROPERTY ExpressionType example that was provided above.
These are called well, PropertyExpression's. There are two classes within Skript to implant this. SimplePropertyExpression and PropertyExpression. These make creating single type expressions much easier and quicker. In the following property expression, I will be using the class SimplePropertyExpression to make this tutorial simpler and I will be getting the time of a world, with changes.
So before I begin some may be wondering what the difference is between the two classes. The PropertyExpression was designed to make Expressions even simpler. This class turns a syntax into a single string, meaning no fancy optional symbols or anything. So for example if I set the getPropertyName() method of it to "time" and the input type is a World, the syntax would be:
Code (Skript):- time of %world%
- #and
- %world%'s time
So let's get started shall we. First you need to create a new class, and in our case it will be within the "expressions.elements" package for easy registration. Here is how it should look like:
Code (Java):- package me.limeglass.addon.elements.expressions;
- import org.bukkit.World;
- import org.eclipse.jdt.annotation.Nullable;
- public class ExprWorldTime extends SimplePropertyExpression<World, Number> {
- @Override
- public Class<? extends Number> getReturnType() {
- //still explaining
- return null;
- }
- @Override
- @Nullable
- //still explaining
- return null;
- }
- @Override
- //still explaining
- return null;
- }
- }
So in order to do this we can use the convert() method that is provided in this class, it will call the parameter which is the type you specified and now you have to return the other generic type that you told you would return, and lastly there is a toString() which as I explained above is helpful for debugging.
In a PropertyExpression there can only be 1 expression. Of course that expression may be single or a list. So in order to set it you use setExpr(insert expression) since this is using the SimplePropertyExpression, Skript automatically sets this up for us and returns it in the convert method. If you were using a more complex syntax and using PropertyExpression, you would use getExpr() to grab that expression in the get() method. So here how we use the convert() method in the SimplePropertyExpression:
Code (Java):- package me.limeglass.addon.elements.expressions;
- import org.bukkit.World;
- import org.eclipse.jdt.annotation.Nullable;
- public class ExprWorldTime extends SimplePropertyExpression<World, Number> {
- @Override
- public Class<? extends Number> getReturnType() {
- }
- @Override
- @Nullable
- return world.getTime();
- }
- @Override
- return "time";
- }
- }
Code (Skript):- [the] time of %world%
- #or
- %world%'s [the] time
And if you wanted to allow for multiple setting/adding/removing etc you would do the same as the above expression I explained earlier. You can also just call a method called register() in the static which is a shortcut for registering PropertyExpression's another reason to love them. The 4th parameter is the classinfo/type to be used such as %string%, %number%, %world% etc
Here is the finished syntax:
Code (Java):- package me.limeglass.addon.elements.expressions;
- import org.bukkit.World;
- import org.bukkit.event.Event;
- import org.eclipse.jdt.annotation.Nullable;
- import ch.njol.util.coll.CollectionUtils;
- public class ExprWorldTime extends SimplePropertyExpression<World, Number> {
- static {
- }
- @Override
- public Class<? extends Number> getReturnType() {
- }
- @Override
- @Nullable
- return world.getTime();
- }
- @Override
- return "time";
- }
- @Override
- if (delta != null) {
- World world = getExpr().getSingle(event);
- if (mode != ChangeMode.DELETE && mode != ChangeMode.RESET && world == null) return;
- switch (mode) {
- case ADD:
- world.setTime(world.getTime() + time);
- break;
- case DELETE:
- world.setTime(24000); //just before sunset
- break;
- case REMOVE:
- world.setTime(world.getTime() - time);
- break;
- case REMOVE_ALL:
- case RESET:
- world.setTime(24000);
- break;
- case SET:
- world.setTime(time);
- break;
- default:
- assert false;
- }
- }
- }
- @Override
- public Class<?>[] acceptChange(final ChangeMode mode) {
- }
- }
Next section is Creating an Effect https://forums.skunity.com/wiki/creating-an-effect/
Ronaldgameking and jaylawl like this.