2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
- name: build
run: ./gradlew build
- name: capture build artifacts
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
with:
name: Artifacts
path: build/libs/
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jobs:
MODRINTH: ${{ secrets.MODRINTH }}
CHANGELOG: ${{ github.event.release.body }}
- name: Upload GitHub release
uses: AButler/upload-release-assets@v2.0
uses: AButler/upload-release-assets@v3.0
with:
files: 'build/libs/*.jar;!build/libs/*-sources.jar;!build/libs/*-dev.jar'
repo-token: ${{ secrets.GITHUB_TOKEN }}
4 changes: 2 additions & 2 deletions docs/user/default-placeholders.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ Prior to 1.19, arguments were separated with a slash (`/`) instead of space.

!!! tip inline end "Vanilla Statistics"

A list of `[statistic]`s can be found on the [Minecraft Wiki](https://minecraft.fandom.com/wiki/Statistics#List_of_custom_statistic_names)
A list of `[statistic]`s can be found on the [Minecraft Wiki](https://minecraft.wiki/w/Statistics#List_of_custom_statistic_names)

A list of `[type]`s can be found on the [Minecraft Wiki](https://minecraft.fandom.com/wiki/Statistics#Statistic_types_and_names)
A list of `[type]`s can be found on the [Minecraft Wiki](https://minecraft.wiki/w/Statistics#Statistic_types_and_names)

Examples: `%player:statistic play_time%`, `%player:statistic mined diamond_ore%`

Expand Down
11 changes: 11 additions & 0 deletions docs/user/mod-placeholders.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@ These placeholders are provided by other mods. Some are build in directly, while

## List of placeholders

### [AfkPlus](https://modrinth.com/mod/afkplus)

- `%afkplus:afk%` - Returns a configurable "[AFK]" tag if the player is marked as AFK.
- `%afkplus:name`/`%afkplus:display_name` - Returns a configurable replacement for `%player:displayname%` if the player is marked as AFK.
This allows for backport formatting, and it can fully support LuckPerms Prefixes, and other mods. By default it returns the standard
`%player:displayname%` when not AFK, which is also configurable.
- `%afkplus:duration` - Returns the (HH:mm:SS_ss) duration since a player went AFK, or nothing.
Configurable in a more human readable format, ie. (5 minutes, 3 seconds).
- `%afkplus:time` - Returns the time (yyyy-MM-dd_HH.mm.ss) since a player went AFK, or nothing.
- `%afkplus:reason` - Returns the reason why a player went AFK, or nothing.

### [Get Off My Lawn ReServed](https://pb4.eu/#get-off-my-lawn)

- `%goml:claim_owners%`/`%goml:claim_owners [no owners text]%` - Returns a list of claim owners.
Expand Down
2 changes: 1 addition & 1 deletion docs/user/text-format.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ and `[optional arg X]` are optional, fully formatted arguments you can pass.

!!! question inline end "Control Keys"

You can find a list of control keys on the [Minecraft Wiki](https://minecraft.fandom.com/wiki/Controls#Configurable_controls)
You can find a list of control keys on the [Minecraft Wiki](https://minecraft.wiki/w/Controls#Configurable_controls)

!!! note inline end

Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ org.gradle.jvmargs=-Xmx1G
fabric_version=0.89.0+1.20.2

# Mod Properties
mod_version = 2.2.0+1.20.2
mod_version = 2.2.1+1.20.2
maven_group = eu.pb4
archives_base_name = placeholder-api

Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public TextNode[] parseNodes(TextNode input) {
if (input instanceof LiteralNode literalNode) {
var list = new ArrayList<SubNode<?>>();
parseLiteral(literalNode, list::add);
return parseSubNodes(list.listIterator(), null, -1, false);
return parseSubNodes(list.listIterator(), null, -1);
} else if (input instanceof TranslatedNode translatedNode) {
var list = new ArrayList<>();
for (var arg : translatedNode.args()) {
Expand All @@ -98,7 +98,7 @@ public TextNode[] parseNodes(TextNode input) {
list.add(new SubNode<>(SubNodeType.TEXT_NODE, TextNode.asSingle(parseNodes(children))));
}
}
return new TextNode[]{parentTextNode.copyWith(parseSubNodes(list.listIterator(), null, -1, false), this)};
return new TextNode[]{parentTextNode.copyWith(parseSubNodes(list.listIterator(), null, -1), this)};
} else {
return new TextNode[]{input};
}
Expand All @@ -112,9 +112,7 @@ private void parseLiteral(LiteralNode literalNode, Consumer<SubNode<?>> consumer
var i = reader.read();
if (i == '\\' && reader.canRead()) {
var next = reader.read();
//if (next != '~' && next != '`' && next != '_' && next != '*' && next != '|') {
builder.append(i);
//}
builder.append(next);
continue;
}
Expand All @@ -140,7 +138,14 @@ private void parseLiteral(LiteralNode literalNode, Consumer<SubNode<?>> consumer
type = switch (i) {
case '`' -> SubNodeType.BACK_TICK;
case '*' -> SubNodeType.STAR;
case '_' -> SubNodeType.FLOOR;
case '_' -> {
if (reader.getCursor() == 1 || !reader.canRead()
|| Character.isWhitespace(reader.peek(-2))
|| Character.isWhitespace(reader.peek())) {
yield SubNodeType.FLOOR;
}
yield null;
}
case '(' -> SubNodeType.BRACKET_OPEN;
case ')' -> SubNodeType.BRACKET_CLOSE;
case '[' -> SubNodeType.SQR_BRACKET_OPEN;
Expand All @@ -166,7 +171,7 @@ private void parseLiteral(LiteralNode literalNode, Consumer<SubNode<?>> consumer
}
}

private TextNode[] parseSubNodes(ListIterator<SubNode<?>> nodes, @Nullable SubNodeType endAt, int count, boolean requireEmpty) {
private TextNode[] parseSubNodes(ListIterator<SubNode<?>> nodes, @Nullable SubNodeType endAt, int count) {
var out = new ArrayList<TextNode>();
int startIndex = nodes.nextIndex();
var builder = new StringBuilder();
Expand All @@ -176,16 +181,7 @@ private TextNode[] parseSubNodes(ListIterator<SubNode<?>> nodes, @Nullable SubNo
if (next.type == endAt) {
int foundCount = 1;

boolean endingOrSpace;
if (requireEmpty && nodes.hasNext()) {
var prev = nodes.next();
endingOrSpace = prev.type != SubNodeType.STRING || ((String) prev.value).startsWith(" ");
nodes.previous();
} else {
endingOrSpace = true;
}

if (foundCount == count && endingOrSpace) {
if (foundCount == count) {
if (!builder.isEmpty()) {
out.add(new LiteralNode(builder.toString()));
}
Expand All @@ -197,7 +193,7 @@ private TextNode[] parseSubNodes(ListIterator<SubNode<?>> nodes, @Nullable SubNo
while (nodes.hasNext()) {
if (nodes.next().type == endAt) {
if ((++foundCount) == count) {
if (requireEmpty && nodes.hasNext()) {
if (nodes.hasNext()) {
var prev = nodes.next();
nodes.previous();
if (prev.type == SubNodeType.STRING && !((String) prev.value).startsWith(" ")) {
Expand Down Expand Up @@ -231,7 +227,7 @@ private TextNode[] parseSubNodes(ListIterator<SubNode<?>> nodes, @Nullable SubNo
builder.append((String) next.value);
continue;
} else if (next.type == SubNodeType.BACK_TICK && this.allowedFormatting.contains(MarkdownFormat.QUOTE)) {
var value = parseSubNodes(nodes, next.type, 1, false);
var value = parseSubNodes(nodes, next.type, 1);

if (value != null) {
if (!builder.isEmpty()) {
Expand All @@ -242,7 +238,7 @@ private TextNode[] parseSubNodes(ListIterator<SubNode<?>> nodes, @Nullable SubNo
continue;
}
} else if (next.type == SubNodeType.SPOILER_LINE && this.allowedFormatting.contains(MarkdownFormat.SPOILER)) {
var value = parseSubNodes(nodes, next.type, 1, false);
var value = parseSubNodes(nodes, next.type, 1);

if (value != null) {
if (!builder.isEmpty()) {
Expand All @@ -253,7 +249,7 @@ private TextNode[] parseSubNodes(ListIterator<SubNode<?>> nodes, @Nullable SubNo
continue;
}
} else if (next.type == SubNodeType.DOUBLE_WAVY_LINE && this.allowedFormatting.contains(MarkdownFormat.STRIKETHROUGH)) {
var value = parseSubNodes(nodes, next.type, 1, false);
var value = parseSubNodes(nodes, next.type, 1);

if (value != null) {
if (!builder.isEmpty()) {
Expand All @@ -273,7 +269,7 @@ private TextNode[] parseSubNodes(ListIterator<SubNode<?>> nodes, @Nullable SubNo
if (nexter.type == next.type) {
two = true;
var i = nodes.nextIndex();
var value = parseSubNodes(nodes, next.type, 2, false);
var value = parseSubNodes(nodes, next.type, 2);

if (value != null) {
if (!builder.isEmpty()) {
Expand All @@ -299,7 +295,7 @@ private TextNode[] parseSubNodes(ListIterator<SubNode<?>> nodes, @Nullable SubNo
}

if (startingOrSpace) {
var value = parseSubNodes(nodes, next.type, 1, next.type == SubNodeType.FLOOR);
var value = parseSubNodes(nodes, next.type, 1);

if (value != null) {
if (!builder.isEmpty()) {
Expand All @@ -313,14 +309,14 @@ private TextNode[] parseSubNodes(ListIterator<SubNode<?>> nodes, @Nullable SubNo
}
} else if (next.type == SubNodeType.SQR_BRACKET_OPEN && this.allowedFormatting.contains(MarkdownFormat.URL) && nodes.hasNext()) {
var start = nodes.nextIndex();
var value = parseSubNodes(nodes, SubNodeType.SQR_BRACKET_CLOSE, 1, false);
var value = parseSubNodes(nodes, SubNodeType.SQR_BRACKET_CLOSE, 1);

if (value != null) {
if (nodes.hasNext()) {
var check = nodes.next().type == SubNodeType.BRACKET_OPEN;

if (check) {
var url = parseSubNodes(nodes, SubNodeType.BRACKET_CLOSE, 1, false);
var url = parseSubNodes(nodes, SubNodeType.BRACKET_CLOSE, 1);
if (url != null) {
if (!builder.isEmpty()) {
out.add(new LiteralNode(builder.toString()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ public static void register() {

Placeholders.register(new Identifier("player", "hunger"), (ctx, arg) -> {
if (ctx.hasPlayer()) {
return PlaceholderResult.value(String.format("%.0f", ctx.player().getHungerManager().getFoodLevel()));
return PlaceholderResult.value(String.valueOf(ctx.player().getHungerManager().getFoodLevel()));
} else {
return PlaceholderResult.invalid("No player!");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,11 @@ public static void register() {
textColors.add(color);
}
}
// We cannot have an empty list!
if (textColors.isEmpty()) {
return out.value(new ParentNode(out.nodes()));
}

return out.value(GradientNode.colorsHard(textColors, out.nodes()));

}
Expand Down