Skip to content

Commit 91bfd07

Browse files
WunderoWundero
Wundero
authored and
Wundero
committed
Changes
Commented up Defaults to use for example placeholders. NoValueException null coverage. Updated Server placeholder with time in world. Fixed issue with overriding suggestions/message.
1 parent f804c4c commit 91bfd07

File tree

4 files changed

+166
-38
lines changed

4 files changed

+166
-38
lines changed

src/main/java/me/rojo8399/placeholderapi/NoValueException.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,14 @@ public NoValueException(Throwable cause) {
116116
}
117117

118118
public List<String> suggestions() {
119-
return suggestions;
119+
return suggestions == null ? new ArrayList<>() : suggestions;
120120
}
121121

122122
public Text getTextMessage() {
123123
if (this.message == null) {
124+
if(super.getMessage()==null) {
125+
return Text.EMPTY;
126+
}
124127
return Text.of(super.getMessage());
125128
}
126129
return this.message;

src/main/java/me/rojo8399/placeholderapi/impl/PlaceholderAPIPlugin.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -424,8 +424,9 @@ public void registerPlaceholders() {
424424
.version("2.0");
425425
case "server":
426426
return builder.description(Messages.get().placeholder.serverdesc.value)
427-
.tokens("unique_players", "online", "max_players", "motd", "cores", "tps", "ram_used",
428-
"ram_free", "ram_total", "ram_max", "uptime", "uptime_percent", "uptime_total")
427+
.tokens("time_[world]", "game_time_[world]", "unique_players", "online", "max_players", "motd",
428+
"cores", "tps", "ram_used", "ram_free", "ram_total", "ram_max", "uptime",
429+
"uptime_percent", "uptime_total")
429430
.version("2.0");
430431
case "sound":
431432
return builder.description(Messages.get().placeholder.sounddesc.value)

src/main/java/me/rojo8399/placeholderapi/impl/placeholder/Defaults.java

Lines changed: 157 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ of this software and associated documentation files (the "Software"), to deal
6868
import org.spongepowered.api.text.Text;
6969
import org.spongepowered.api.text.serializer.TextSerializers;
7070
import org.spongepowered.api.world.Locatable;
71+
import org.spongepowered.api.world.storage.WorldProperties;
7172

7273
import com.flowpowered.math.vector.Vector3d;
7374

@@ -83,13 +84,29 @@ of this software and associated documentation files (the "Software"), to deal
8384
import me.rojo8399.placeholderapi.impl.PlaceholderAPIPlugin;
8485
import me.rojo8399.placeholderapi.impl.configs.JavascriptManager;
8586
import me.rojo8399.placeholderapi.impl.configs.Messages;
87+
import me.rojo8399.placeholderapi.impl.utils.TypeUtils;
8688
import ninja.leaping.configurate.objectmapping.Setting;
8789
import ninja.leaping.configurate.objectmapping.serialize.ConfigSerializable;
8890

91+
/**
92+
* The default placeholders provided by the plugin. These also server as
93+
* examples of many, though not all, possibilities for the plugin to use as
94+
* placeholders.
95+
*
96+
* The class is a listener class AND a configurable class, allowing the
97+
* placeholders to be attached directly to PlaceholderAPI.
98+
*
99+
* @author Wundero
100+
*
101+
*/
89102
@Listening
90103
@ConfigSerializable
91104
public class Defaults {
92105

106+
/**
107+
* This class simply represents the service PlaceholderAPI provides, except
108+
* simplified in order to be used in JavaScript placeholders.
109+
*/
93110
public static class Service {
94111
private CommandSource o;
95112
private Player p;
@@ -125,6 +142,12 @@ public String value(String placeholder, String pattern) {
125142
}
126143
}
127144

145+
/**
146+
* Dynamic object used for configuring the "server" placeholder. Notice that the
147+
* fields inside this class do NOT need to be attached to any one placeholder;
148+
* the fields in this class do not know what placeholder is going to use them so
149+
* they do not need to be attached.
150+
*/
128151
@ConfigSerializable
129152
private static class Uptime {
130153

@@ -147,12 +170,26 @@ public String toString() {
147170
}
148171
}
149172

173+
/**
174+
* Pattern to match against a sound placeholder containing _all or all_.
175+
*/
150176
private static final Pattern ALLSOUND_PATTERN = Pattern.compile("([_]?all[_]?)", Pattern.CASE_INSENSITIVE);
151177

178+
/**
179+
* Runtime utility to convert to MB from bytes.
180+
*/
152181
private static int MB = 1024 * 1024;
153182

183+
/**
184+
* Current runtime; used by server placeholder.
185+
*/
154186
private static Runtime runtime = Runtime.getRuntime();
155187

188+
/*
189+
* Begin utilities methods: these methods are used throughout the class
190+
* but do not play major roles in explaining the plugin.
191+
*/
192+
156193
private static boolean between(double o, double min, double max) {
157194
return o >= min && o <= max;
158195
}
@@ -265,12 +302,22 @@ private static long getTime(Player player, TimeUnit unit, boolean round) {
265302
private EconomyService service;
266303
private UserStorageService storage = null;
267304

305+
/**
306+
* This field is what the object will use as a configurable field; fields
307+
* DIRECTLY inside of the object registered MUST be attached to a placeholder
308+
* using the Attach annotation. If they are not attached, they will be ignored
309+
* completely.
310+
*/
268311
@Setting
269-
@Attach("server")
312+
@Attach("server") // Use the id of the placeholder you would like to attach to. The actual
313+
// placeholder you attach to is arbitrary, however it must exist within this
314+
// class.
270315
private List<Uptime> uptimes = new ArrayList<>();
271316

272317
private Set<User> users = new HashSet<>();
273318

319+
// Since the object is instantiated before being passed to PlaceholderAPI, you
320+
// can do whatever for this.
274321
public Defaults(EconomyService service, JavascriptManager manager, PlaceholderService s) {
275322
if (service != null) {
276323
eco = true;
@@ -316,6 +363,43 @@ private boolean contains(Player p) {
316363
return users.stream().map(u -> u.getUniqueId()).anyMatch(p.getUniqueId()::equals);
317364
}
318365

366+
/**
367+
* This is the first placeholder in the object. This handles how the placeholder
368+
* "economy" parses. I return an object to allow me to return any value without
369+
* having to make a nice supertype.
370+
*
371+
* The parameters in this method are annotated with Nullable. If they have this
372+
* annotation, they can have null values and you should handle accordingly; if
373+
* they do not have this annotation, you can safely assume those parameters will
374+
* NEVER be null.
375+
*
376+
* @param token
377+
* This parameter is the part in the placeholder after the first _.
378+
* For example, {economy_balance} gives a token of "balance". The
379+
* 'fix = true' parameter tells PlaceholderAPI to make the token
380+
* lower case and remove excess characters.
381+
*
382+
* For the token, you can request types other than String if you
383+
* want. If you know your token will have multiple _ separated
384+
* sections, you can request a String[]. If you want a number, you
385+
* can request a Double or an Integer. PlaceholderAPI will handle the
386+
* conversion for you. PlaceholderAPI will also try it's best to cast
387+
* to a given type but, if the type does not have nice
388+
* deserialization options, may not ever parse the placeholder. If
389+
* you want the placeholder to parse to any object type, you must
390+
* register a type serializer into PlaceholderService.
391+
* @param player
392+
* This parameter is the Source parameter, or who the placeholder is
393+
* replacing for. In this case, it would be who's balance to draw
394+
* from.
395+
* @return whatever the value of the placeholder is.
396+
* @throws NoValueException
397+
* - Throw this exception if you do not want the placeholder
398+
* replaced. For example, "{economy_a}" should throw a
399+
* NoValueException so that PlaceholderAPI helps the user fix issues
400+
* with their placeholders. If you return null, PlaceholderAPI will
401+
* fill with an empty string.
402+
*/
319403
@Placeholder(id = "economy")
320404
public Object economy(@Token(fix = true) @Nullable String token, @Nullable @Source User player)
321405
throws NoValueException {
@@ -432,6 +516,27 @@ private long getUptimeMillis() {
432516
.reduce((long) Sponge.getServer().getRunningTimeTicks() * 50, (a, b) -> a + b);
433517
}
434518

519+
/**
520+
* This is another example of a placeholder. This one is simple in comparison to
521+
* the others.
522+
*
523+
* The Relational annotation forces this to be used with the rel_ prefix in the
524+
* placeholder, like this: {rel_rank_greater_than}. This provides no guarantees
525+
* that it will actually need or use both source and observer. Again, all
526+
* parameters here are null-safe (will not call the method if those parameters
527+
* are null).
528+
*
529+
* @param token
530+
* This is the token, like in all other placeholders.
531+
* @param underrank
532+
* This is the player comparing. For instance, if you say source >
533+
* observer, source will be a child of observer and thus have more
534+
* permissions.
535+
* @param overrank
536+
* This is the player being compared to.
537+
* @return Whether the expression, greater_than or less_than, returns true.
538+
* @throws NoValueException
539+
*/
435540
@Placeholder(id = "rank")
436541
@Relational
437542
public Boolean isAbove(@Token String token, @Source User underrank, @Observer User overrank)
@@ -473,6 +578,10 @@ public Object js(@Nullable @Source Player player, @Nullable @Observer CommandSou
473578
return manager.eval(engine, token);
474579
}
475580

581+
/**
582+
* Listener is registered because of the @Listening annotation. No need to
583+
* attach it to your plugin, PlaceholderAPI will do this for you.
584+
*/
476585
@Listener
477586
public void newEco(ChangeServiceProviderEvent event) {
478587
if (event.getService().equals(EconomyService.class)) {
@@ -557,13 +666,14 @@ public Object normalPlayer(@Source Player p, @Token(fix = true) @Nullable String
557666
case "remaining_air":
558667
return p.getOrElse(Keys.REMAINING_AIR, 300);
559668
case "item_in_main_hand":
669+
// ItemStack return types are parsed nicely
560670
return p.getItemInHand(HandTypes.MAIN_HAND).orElse(ItemStackSnapshot.NONE.createStack());
561671
case "item_in_off_hand":
562672
return p.getItemInHand(HandTypes.OFF_HAND).orElse(ItemStackSnapshot.NONE.createStack());
563673
case "walk_speed":
564674
return p.getOrElse(Keys.WALKING_SPEED, 1.0);
565675
case "time_played_seconds":
566-
return getTime(p, TimeUnit.SECONDS, true);
676+
return getTime(p, TimeUnit.SECONDS, true); // Instant and Duration return types are serialized
567677
case "time_played_minutes":
568678
return getTime(p, TimeUnit.MINUTES, true);
569679
case "time_played_ticks":
@@ -622,31 +732,6 @@ public void onStopping(GameStoppingEvent event) {
622732
Store.get().get("server", false).ifPresent(Expansion::saveConfig);
623733
}
624734

625-
/*
626-
* @Placeholder(id = "playerlist") public List<Player> list(@Nullable @Token(fix
627-
* = true) String token) { if (token == null) { return
628-
* Sponge.getServer().getOnlinePlayers().stream() .filter(p ->
629-
* !p.getOrElse(Keys.VANISH_PREVENTS_TARGETING,
630-
* false)).collect(Collectors.toList()); } Stream<Player> out =
631-
* Sponge.getServer().getOnlinePlayers().stream() .filter(p ->
632-
* !p.getOrElse(Keys.VANISH_PREVENTS_TARGETING, false)); if
633-
* (PERM.matcher(token).find()) { Matcher m = PERM.matcher(token); while
634-
* (m.find()) { String permission = m.group(1); out = out.filter(p ->
635-
* p.hasPermission(permission)); } } if (WORLD.matcher(token).find()) { Matcher
636-
* m = WORLD.matcher(token); while (m.find()) { String world = m.group(1); out =
637-
* out.filter(p -> p.getWorld().getName().toLowerCase().startsWith(world)); } }
638-
* // TODO: /* better token matching data key boolean filter -> load key from t
639-
* - IS_FLYING, for example data key numeric filter -> load key again, but also
640-
* load comparator (>, >=, <, <=, =) and number - Health > 10, for example -
641-
* sort greatest value for key??? better idea??: placeholder value filter ->
642-
* load placeholder from key, load value from key, load comparator sanity checks
643-
* (no > for boolean, no value present = true/0/max int, depending, no comp
644-
* present: =) sort by highest comparison limiter, top X players if available ->
645-
* sort by highest comp then alphabetically
646-
*//*
647-
* return out.collect(Collectors.toList()); }
648-
*/
649-
650735
private void putCur(Currency c) {
651736
currencies.put(c.getName().toLowerCase().replace(" ", ""), c);
652737
}
@@ -739,6 +824,46 @@ public Object relPlayer(@Source Player one, @Observer CommandSource two, @Token(
739824

740825
@Placeholder(id = "server")
741826
public Object server(@Token(fix = true) String identifier) throws NoValueException {
827+
boolean gt = false;
828+
if (identifier.startsWith("time")) {
829+
gt = true;
830+
identifier = identifier.substring("time".length());
831+
}
832+
if (identifier.startsWith("game_time")) {
833+
gt = true;
834+
identifier = identifier.substring("game_time".length());
835+
}
836+
if (gt) {
837+
Optional<WorldProperties> w = Sponge.getServer().getDefaultWorld();
838+
if (!identifier.isEmpty()) {
839+
String world = identifier.substring(1);
840+
if (world.isEmpty()) {
841+
if (w.isPresent()) {
842+
return w.get().getWorldTime() % 24000;
843+
} else {
844+
String id1 = identifier;
845+
throw new NoValueException(Messages.get().misc.invalid.t("world"),
846+
Sponge.getServer().getAllWorldProperties().stream().map(wp -> wp.getWorldName())
847+
.filter(n -> TypeUtils.closeTo(id1.replaceFirst("_", ""), n))
848+
.map(s -> "time_" + s).collect(Collectors.toList()));
849+
}
850+
}
851+
WorldProperties wp = Sponge.getServer().getWorld(world).map(wo -> wo.getProperties()).orElse(null);
852+
if (wp != null) {
853+
return wp.getWorldTime() % 24000;
854+
}
855+
} else {
856+
if (w.isPresent()) {
857+
return w.get().getWorldTime() % 24000;
858+
}
859+
}
860+
String id1 = identifier;
861+
throw new NoValueException(Messages.get().misc.invalid.t("world"),
862+
Sponge.getServer().getAllWorldProperties().stream().map(wp -> wp.getWorldName())
863+
.filter(n -> n.equalsIgnoreCase(w.get().getWorldName())
864+
|| TypeUtils.closeTo(id1.replaceFirst("_", ""), n))
865+
.map(s -> "time_" + s).collect(Collectors.toList()));
866+
}
742867
switch (identifier) {
743868
case "online":
744869
return Sponge.getServer().getOnlinePlayers().stream()
@@ -750,7 +875,7 @@ public Object server(@Token(fix = true) String identifier) throws NoValueExcepti
750875
case "motd":
751876
return Sponge.getServer().getMotd();
752877
case "uptime":
753-
case "uptime_percent":
878+
case "uptime_percent": // Uptime config item used here.
754879
long um = this.getUptimeMillis();
755880
long dm = this.getDowntimeMillis();
756881
NumberFormat fmt = NumberFormat.getPercentInstance();
@@ -830,16 +955,14 @@ public void sync() {
830955
}
831956
}
832957

958+
/**
959+
* This method does not need parameters, but can still be called. LocalDateTime
960+
* is also nicely serialized.
961+
*/
833962
@Placeholder(id = "time")
834963
public LocalDateTime time() {
835964
return LocalDateTime.now();
836965
}
837-
/*
838-
* private static final Pattern PERM = Pattern.compile(
839-
* "perm(?:ission)?\\_([A-Za-z0-9*\\-]+(?:\\.[A-Za-z0-9*\\-]+)+)",
840-
* Pattern.CASE_INSENSITIVE), WORLD =
841-
* Pattern.compile("world\\_([A-Za-z0-9\\_\\-]+)", Pattern.CASE_INSENSITIVE);
842-
*/
843966

844967
public int unique() {
845968
return users.size();

src/main/java/me/rojo8399/placeholderapi/impl/placeholder/Store.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,8 @@ public Object parse(String id, boolean relational, Object src, Object obs, Optio
316316
return exp.parse(exp.convertSource(src), exp.convertObserver(obs), token);
317317
}
318318
} catch (NoValueException e) {
319-
throw new NoValueException(e.getMessage(), exp.getSuggestions(token.orElse(null)));
319+
throw new NoValueException(e.getTextMessage(),
320+
e.suggestions().isEmpty() ? exp.getSuggestions(token.orElse(null)) : e.suggestions());
320321
}
321322
throw new NoValueException(Messages.get().placeholder.invalidSrcObs.t());
322323
}

0 commit comments

Comments
 (0)