Skip to content

Commit e7af55f

Browse files
Add serializer for text components (#8538)
1 parent ce75dc9 commit e7af55f

File tree

5 files changed

+160
-57
lines changed

5 files changed

+160
-57
lines changed

src/main/java/org/skriptlang/skript/bukkit/text/TextModule.java

Lines changed: 3 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,13 @@
11
package org.skriptlang.skript.bukkit.text;
22

3-
import ch.njol.skript.classes.ClassInfo;
4-
import ch.njol.skript.classes.Parser;
5-
import ch.njol.skript.expressions.base.EventValueExpression;
6-
import ch.njol.skript.lang.ParseContext;
73
import ch.njol.skript.registrations.Classes;
8-
import net.kyori.adventure.audience.Audience;
94
import net.kyori.adventure.text.Component;
10-
import org.bukkit.command.CommandSender;
115
import org.skriptlang.skript.addon.AddonModule;
126
import org.skriptlang.skript.addon.HierarchicalAddonModule;
137
import org.skriptlang.skript.addon.SkriptAddon;
148
import org.skriptlang.skript.bukkit.text.elements.effects.*;
159
import org.skriptlang.skript.bukkit.text.elements.expressions.*;
10+
import org.skriptlang.skript.bukkit.text.types.*;
1611
import org.skriptlang.skript.lang.arithmetic.Arithmetics;
1712
import org.skriptlang.skript.lang.arithmetic.Operator;
1813
import org.skriptlang.skript.lang.comparator.Comparators;
@@ -26,57 +21,8 @@ public TextModule(AddonModule parentModule) {
2621

2722
@Override
2823
public void initSelf(SkriptAddon addon) {
29-
Classes.registerClass(new ClassInfo<>(Component.class, "textcomponent")
30-
.user("text ?components?")
31-
.name("Text Component")
32-
.description("Text components are used to represent how text is displayed in Minecraft.",
33-
"This includes colors, decorations, and more.")
34-
.examples("\"<red><bold>This text is red and bold!\"")
35-
.since("2.15")
36-
.parser(new Parser<>() {
37-
@Override
38-
public boolean canParse(ParseContext context) {
39-
return false;
40-
}
41-
42-
@Override
43-
public String toString(Component component, int flags) {
44-
return TextComponentParser.instance().toString(component);
45-
}
46-
47-
@Override
48-
public String toVariableNameString(Component component) {
49-
return "textcomponent:" + component;
50-
}
51-
}));
52-
53-
Classes.registerClass(new ClassInfo<>(Audience.class, "audience")
54-
.user("audiences?")
55-
.name("Audience")
56-
.description("An audience is a receiver of media, such as individual players, the console, or groups of players (such as those on a team or in a world).")
57-
.examples("send \"Hello world!\" to the player",
58-
"send action bar \"ALERT! A horde of zombies has overrun the central village.\" to the world")
59-
.since("2.15")
60-
// note: CommandSender is purposefully used here as there may be many Audiences in a single event
61-
// for example, there is a conflict in events with Player and World (e.g. all player events)
62-
// we continue to use CommandSender for retaining existing behavior
63-
.defaultExpression(new EventValueExpression<>(CommandSender.class))
64-
.parser(new Parser<>() {
65-
@Override
66-
public boolean canParse(ParseContext context) {
67-
return false;
68-
}
69-
70-
@Override
71-
public String toString(Audience audience, int flags) {
72-
return "audience";
73-
}
74-
75-
@Override
76-
public String toVariableNameString(Audience audience) {
77-
return "audience";
78-
}
79-
}));
24+
Classes.registerClass(new TextComponentClassInfo());
25+
Classes.registerClass(new AudienceClassInfo());
8026

8127
Converters.registerConverter(String.class, Component.class,
8228
string -> TextComponentParser.instance().parseSafe(string));
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package org.skriptlang.skript.bukkit.text.types;
2+
3+
import ch.njol.skript.classes.ClassInfo;
4+
import ch.njol.skript.classes.Parser;
5+
import ch.njol.skript.expressions.base.EventValueExpression;
6+
import ch.njol.skript.lang.ParseContext;
7+
import net.kyori.adventure.audience.Audience;
8+
import org.bukkit.command.CommandSender;
9+
import org.jetbrains.annotations.ApiStatus;
10+
11+
@ApiStatus.Internal
12+
public final class AudienceClassInfo extends ClassInfo<Audience> {
13+
14+
public AudienceClassInfo() {
15+
super(Audience.class, "audience");
16+
this.user("audiences?")
17+
.name("Audience")
18+
.description("An audience is a receiver of media, such as individual players, the console, or groups of players (such as those on a team or in a world).")
19+
.examples("send \"Hello world!\" to the player",
20+
"send action bar \"ALERT! A horde of zombies has overrun the central village.\" to the world")
21+
.since("2.15")
22+
// note: CommandSender is purposefully used here as there may be many Audiences in a single event
23+
// for example, there is a conflict in events with Player and World (e.g. all player events)
24+
// we continue to use CommandSender for retaining existing behavior
25+
.defaultExpression(new EventValueExpression<>(CommandSender.class))
26+
.parser(new AudienceParser());
27+
}
28+
29+
private static final class AudienceParser extends Parser<Audience> {
30+
31+
@Override
32+
public boolean canParse(ParseContext context) {
33+
return false;
34+
}
35+
36+
@Override
37+
public String toString(Audience audience, int flags) {
38+
return "audience";
39+
}
40+
41+
@Override
42+
public String toVariableNameString(Audience audience) {
43+
return "audience:" + audience.hashCode();
44+
}
45+
46+
}
47+
48+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package org.skriptlang.skript.bukkit.text.types;
2+
3+
import ch.njol.skript.classes.ClassInfo;
4+
import ch.njol.skript.classes.Parser;
5+
import ch.njol.skript.classes.Serializer;
6+
import ch.njol.skript.lang.ParseContext;
7+
import ch.njol.yggdrasil.Fields;
8+
import net.kyori.adventure.text.Component;
9+
import net.kyori.adventure.text.serializer.json.JSONComponentSerializer;
10+
import org.jetbrains.annotations.ApiStatus;
11+
12+
import java.io.StreamCorruptedException;
13+
14+
@ApiStatus.Internal
15+
public final class TextComponentClassInfo extends ClassInfo<Component> {
16+
17+
public TextComponentClassInfo() {
18+
super(Component.class, "textcomponent");
19+
this.user("text ?components?")
20+
.name("Text Component")
21+
.description("Text components are used to represent how text is displayed in Minecraft.",
22+
"This includes colors, decorations, and more.")
23+
.examples("\"<red><bold>This text is red and bold!\"")
24+
.since("2.15")
25+
.parser(new TextComponentParser())
26+
.serializer(new TextComponentSerializer());
27+
}
28+
29+
private static final class TextComponentParser extends Parser<Component> {
30+
31+
@Override
32+
public boolean canParse(ParseContext context) {
33+
return false;
34+
}
35+
36+
@Override
37+
public String toString(Component component, int flags) {
38+
return org.skriptlang.skript.bukkit.text.TextComponentParser.instance().toString(component);
39+
}
40+
41+
@Override
42+
public String toVariableNameString(Component component) {
43+
return "textcomponent:" + component.hashCode();
44+
}
45+
46+
}
47+
48+
static final class TextComponentSerializer extends Serializer<Component> {
49+
50+
@Override
51+
public Fields serialize(Component component) {
52+
return Fields.singletonObject("json", JSONComponentSerializer.json().serialize(component));
53+
}
54+
55+
@Override
56+
protected Component deserialize(Fields fields) throws StreamCorruptedException {
57+
String json = fields.getObject("json", String.class);
58+
if (json == null) {
59+
throw new StreamCorruptedException();
60+
}
61+
return JSONComponentSerializer.json().deserialize(json);
62+
}
63+
64+
@Override
65+
public boolean mustSyncDeserialization() {
66+
return false;
67+
}
68+
69+
@Override
70+
protected boolean canBeInstantiated() {
71+
return false;
72+
}
73+
74+
}
75+
76+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package org.skriptlang.skript.bukkit.text.types;
2+
3+
import net.kyori.adventure.text.Component;
4+
import net.kyori.adventure.text.format.NamedTextColor;
5+
import net.kyori.adventure.text.format.Style;
6+
import net.kyori.adventure.text.format.TextDecoration;
7+
import org.junit.Test;
8+
import org.skriptlang.skript.bukkit.text.types.TextComponentClassInfo.TextComponentSerializer;
9+
10+
import java.io.StreamCorruptedException;
11+
import java.util.Set;
12+
13+
import static org.junit.Assert.*;
14+
15+
public class TextComponentSerializationTest {
16+
17+
@Test
18+
public void test() throws StreamCorruptedException {
19+
TextComponentSerializer serializer = new TextComponentSerializer();
20+
Component expected = Component.text("Hello ", Style.style(NamedTextColor.RED)
21+
.decorations(Set.of(TextDecoration.values()), false).decoration(TextDecoration.BOLD, true))
22+
.append(Component.text("world!", Style.style(NamedTextColor.BLUE, TextDecoration.BOLD.withState(false))));
23+
Component deserialized = serializer.deserialize(serializer.serialize(expected));
24+
assertEquals(expected, deserialized);
25+
}
26+
27+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,8 @@
11
test "component equality":
22
assert (formatted "<rainbow>Hello!") is (formatted "<rainbow>Hello!") with "components with virtual components failed comparison"
3+
4+
test "component serialization":
5+
# verify no warning
6+
parse:
7+
set {x} to line 1 of lore of {_item}
8+
assert last parse logs are not set

0 commit comments

Comments
 (0)