-
Notifications
You must be signed in to change notification settings - Fork 15
Adding expansions
Wundero edited this page Sep 5, 2017
·
17 revisions
There are two primary ways to add an expansion to PlaceholderAPI. Because PlaceholderAPI provides a service, PlaceholderService
, it is fairly simple to add placeholders.
@Plugin(name = "Example", id = "example", version = "1.0", dependencies = {
@Dependency(id = "placeholderapi", version = "[4.0,)") })
// @Listening -> Include this annotation if the class is not a plugin class but
// you want to register listeners.
@ConfigSerializable // Include this annotation if you want @Setting annotated fields filled by
// PlaceholderAPI's config.
public class Example { // The class to load placeholders from, if using load or loadAll, MUST be public.
PlaceholderService s; // Make sure you add PlaceholderAPI to your gradle/maven dependencies
@Listener
public void onStart(GameStartingServerEvent e) {
// I use provideUnchecked but it is recommended to use provide instead, unless
// you can guarantee the existence of the service
s = Sponge.getServiceManager().provideUnchecked(PlaceholderService.class);
// The load all method acquires every method that is annotated with @Placeholder
// in the 'holder' class, the first object provided, and creates a builder for
// it. This is particularly nice because you can stream the builders nicely and
// it can handle numerous placeholders at once.
s.loadAll(this, this).stream().map(builder -> { // This is one way of loading placeholders, you could also load
// each one individually or create from other sources.
switch (builder.getId()) {
case "multi":
return builder.tokens("a", "b", null).description("Parse the token for a player!");
case "msg":
return builder.description("Send a message!");
case "random":
return builder.description("Generate a random number, from 0 to the number provided"
+ ", or if no number is given, either 0 or 1.");
}
return builder;
}).map(builder -> builder.author("Wundero").version("1.0")).forEach(builder -> {
try {
builder.buildAndRegister(); // buildAndRegister takes care of everything you want for you, so this is
// all that is needed.
} catch (Exception ex) {
ex.printStackTrace();
}
});
// If you do not load from an object, you must specify plugin, id and function
// for it to work.
// The generic types here are necessary, and the builder() method will throw an
// exception if they are not provided. They are not required for the load and
// loadAll methods since they are provided by the methods being loaded.
// The builder provides numerous parsing method functions by default.
try {
// The generic types of the Source and the Observer (the first two types) must
// be one of, or a subclass/subinterface of:
// Locatable, MessageReceiver, DataHolder or Subject.
// The return type can be anything.
s.<CommandSource, MessageReceiver, Void>builder().id("function").plugin(this).author("Wundero")
.description("Execute a function.").consumeDual((s, r) -> r.sendMessage(Text.of(s.getName())))
.buildAndRegister();
} catch (Exception ex) {
ex.printStackTrace();
}
}
// Any method annotated with the @Placeholder annotation must also be public.
// Since the class is annotated with ConfigSerializable AND we called the
// load/loadAll method on it, this will be filled.
@Setting
private String internalMsg = "Hello!";
@Placeholder(id = "random") // Id must be specified, however this annotation and id are the only two
// requirements.
public int rand(@Token @Nullable Integer range) { // If the token's class is not a String or Optional<String>, an
// attempt will be made to parse the token. If the attempt
// fails, the token will be null.
// If the nullable annotation is not specified, the parameter is GUARANTEED to
// be non-null. (The placeholder will simply not parse if the parameter is null)
if (range == null || range < 1) {
return (int) Math.round(Math.random());
}
return (int) Math.round((Math.random() * range));
}
// The return type can be anything, primitives, arrays, objects, void, you name
// it.
@Placeholder(id = "source") // using '_' or ' ' is not recommended and should be avoided.
public String getName(@Source CommandSource source) {
return source.getName();
}
@Placeholder(id = "constant") // Ids must be unique within their own relational or non-relational group.
public String constant() { // No parameters need to be specified.
return "Wahoo!";
}
@Placeholder(id = "msg")
public void msg(@Source MessageReceiver src, @Nullable @Token String message) {
if (message == null) {
message = internalMsg;
}
message = message.replace("_", " ");
src.sendMessage(Text.of(TextColors.AQUA, message));
}
@Placeholder(id = "msg")
@Relational // This annotation means that it will use the rel_msg placeholder as the placeholder. No other
// guarantees are made, however relational and non-relational placeholder ids are stored separately, so it is possible
// to have msg as both a relational and a non-relational id.
public void relMsg(@Source CommandSource src, @Observer @Nullable MessageReceiver sendTo,
@Nullable @Token Text message) {
if (sendTo == null) {
sendTo = src;
}
if (message == null) {
message = Text.of(TextColors.GREEN, src.getName() + " says hello!");
}
sendTo.sendMessage(message);
}
@Placeholder(id = "multi") // "Fix" means to make the string all lower case.
public Object multi(@Source Player src, @Nullable @Token(fix = true) String token) {
if (token == null) {
return src.getName();
}
switch (token) {
case "a":
return src.getName() + " A";
case "b":
return src.firstPlayed();
}
return null;
}
}
There are additional methods for adding extra functionality, such as reload listeners and separation of config/listener objects from the placeholder, provided by the builder as well. See the builder's documentation on each method to see if you need or want to use it.