Expressionable Placeholders
The Expressionable<T> interface allows placeholders to be dynamically evaluated using mathematical and logical operators directly from text tags. This eliminates the need to create dozens of specific placeholders for simple comparisons or arithmetic.
Available Operations
When a placeholder implements Expressionable, it can be targeted by the global abyssallib operator placeholders.
The standard syntax for an operation is: <placeholder:abyssallib:<operator>:<target_placeholder>:[argument]>
Operator | Category | Description |
|---|
add, sub, mul, div, mod, pow
| Mathematics | Standard arithmetic operations. Requires an argument. |
min, max
| Mathematics | Returns the minimum or maximum between the target and the argument. |
round, floor, ceil, abs
| Math (Unary) | Standard rounding and absolute value operations. Does not take an argument. |
sin, cos, tan, asin, acos, atan
| Trigonometry | Standard trigonometric functions. Does not take an argument. |
eq, neq, gt, lt, gte, lte
| Relational Logic | Equality and greater/less-than comparisons. Returns a boolean. Requires an argument. |
and, or, xor
| Boolean Logic | Standard logical operators. Returns a boolean. Requires an argument. |
not
| Boolean (Unary) | Inverts the boolean state of the target. Does not take an argument. |
Implementing a Custom Expressionable
While AbyssalLib provides abstract classes for basic types (AbstractDoublePlaceholder, AbstractStringPlaceholder, AbstractBooleanPlaceholder), you can implement Expressionable<T> on any custom placeholder.
For this example, we will create a PlayerVelocityPlaceholder that returns a Bukkit Vector. We will implement add to combine velocities, and gt (greater than) to compare the magnitude of the velocity against a threshold.
import com.github.darksoulq.abyssallib.server.placeholder.Placeholder;
import com.github.darksoulq.abyssallib.server.placeholder.PlaceholderArgument;
import com.github.darksoulq.abyssallib.server.placeholder.PlaceholderContext;
import com.github.darksoulq.abyssallib.server.placeholder.PlaceholderResult;
import com.github.darksoulq.abyssallib.server.placeholder.expression.Expressionable;
import net.kyori.adventure.key.Key;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
public class PlayerVelocityPlaceholder extends Placeholder<Vector> implements Expressionable<Vector> {
public PlayerVelocityPlaceholder(Key id) {
super(id, Vector.class);
}
@Override
public PlaceholderResult<Vector> resolve(PlaceholderContext context) {
Player player = context.getPlayer();
if (player == null) return PlaceholderResult.empty();
return PlaceholderResult.success(player.getVelocity());
}
@Override
public PlaceholderResult<Vector> add(PlaceholderContext ctx, PlaceholderArgument other) {
PlaceholderResult<Vector> res = resolve(ctx);
if (res.isEmpty() || res.isError()) return res;
// Attempt to parse the argument as a single scalar double to add to all axes
Double scalar = other.asDouble().getOrNull();
if (scalar != null) {
Vector current = res.getValue().clone();
return PlaceholderResult.success(current.add(new Vector(scalar, scalar, scalar)));
}
return PlaceholderResult.error("Invalid vector addition argument");
}
@Override
public PlaceholderResult<Boolean> gt(PlaceholderContext ctx, PlaceholderArgument other) {
PlaceholderResult<Vector> res = resolve(ctx);
if (res.isEmpty() || res.isError()) return PlaceholderResult.empty();
// Compare the vector's length (speed) against a provided double
Double threshold = other.asDouble().getOrNull();
if (threshold != null) {
return PlaceholderResult.success(res.getValue().length() > threshold);
}
return PlaceholderResult.empty();
}
// ... (Other Expressionable methods must be implemented, returning PlaceholderResult.empty() if unsupported) ...
@Override
public PlaceholderResult<Vector> sub(PlaceholderContext ctx, PlaceholderArgument other) { return PlaceholderResult.empty(); }
@Override public PlaceholderResult<Vector> mul(PlaceholderContext ctx, PlaceholderArgument other) { return PlaceholderResult.empty(); }
@Override public PlaceholderResult<Vector> div(PlaceholderContext ctx, PlaceholderArgument other) { return PlaceholderResult.empty(); }
// ... remaining methods omitted for brevity
}
Using Expressions in Text
Once your expressionable placeholder is registered, you can target it using the global abyssallib operator placeholders.
Assuming the PlayerVelocityPlaceholder is registered as abyssallib_example:velocity, here is how it is used in text:
Basic Resolution: Displays the vector directly (falling back to its toString() or your custom format() method). <placeholder:abyssallib_example:velocity>
Math Expression: Adds 0.5 to all axes of the player's current velocity. <placeholder:abyssallib:add:abyssallib_example:velocity:0.5>
Logic Expression: Evaluates to true if the player's total velocity magnitude is greater than 2.0. <placeholder:abyssallib:gt:abyssallib_example:velocity:2.0>
05 June 2026