Skip to content

Commit 0f0594f

Browse files
committed
feat: add DFU codecs for adventure components and styles
1 parent fea9929 commit 0f0594f

19 files changed

+1162
-0
lines changed

dfu/src/main/java/net/kyori/adventure/dfu/Codecs.java

Lines changed: 364 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package net.kyori.adventure.dfu.component;
2+
3+
import com.mojang.serialization.MapCodec;
4+
import net.kyori.adventure.text.Component;
5+
6+
public class ComponentType<T extends Component> {
7+
private final String id;
8+
private final MapCodec<T> codec;
9+
10+
public ComponentType(final String id, final MapCodec<T> codec) {
11+
this.id = id;
12+
this.codec = codec;
13+
}
14+
15+
public MapCodec<T> getCodec() {
16+
return codec;
17+
}
18+
19+
public String getId() {
20+
return id;
21+
}
22+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package net.kyori.adventure.dfu.component;
2+
3+
import net.kyori.adventure.text.Component;
4+
import net.kyori.adventure.text.KeybindComponent;
5+
import net.kyori.adventure.text.NBTComponent;
6+
import net.kyori.adventure.text.ScoreComponent;
7+
import net.kyori.adventure.text.SelectorComponent;
8+
import net.kyori.adventure.text.TextComponent;
9+
import net.kyori.adventure.text.TranslatableComponent;
10+
11+
public class ComponentTypes {
12+
public static final ComponentType<TextComponent> TEXT = TextType.INSTANCE;
13+
public static final ComponentType<TranslatableComponent> TRANSLATABLE = TranslatableType.INSTANCE;
14+
public static final ComponentType<KeybindComponent> KEYBIND = KeybindType.INSTANCE;
15+
public static final ComponentType<ScoreComponent> SCORE = ScoreType.INSTANCE;
16+
public static final ComponentType<SelectorComponent> SELECTOR = SelectorType.INSTANCE;
17+
public static final ComponentType<NBTComponent> TYPE = NBTComponentType.INSTANCE;
18+
19+
public static final ComponentType<?>[] ALL = new ComponentType[]{TEXT, TRANSLATABLE, KEYBIND, SCORE, SELECTOR, TYPE};
20+
21+
public static ComponentType<?> from(Component component) {
22+
if (component instanceof TextComponent) {
23+
return TEXT;
24+
} else if (component instanceof TranslatableComponent) {
25+
return TRANSLATABLE;
26+
} else if (component instanceof KeybindComponent) {
27+
return KEYBIND;
28+
} else if (component instanceof ScoreComponent) {
29+
return SCORE;
30+
} else if (component instanceof SelectorComponent) {
31+
return SELECTOR;
32+
} else if (component instanceof NBTComponent) {
33+
return TYPE;
34+
} else {
35+
return null;
36+
}
37+
}
38+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package net.kyori.adventure.dfu.component;
2+
3+
import com.mojang.serialization.Codec;
4+
import com.mojang.serialization.MapCodec;
5+
import com.mojang.serialization.codecs.RecordCodecBuilder;
6+
import net.kyori.adventure.text.Component;
7+
import net.kyori.adventure.text.KeybindComponent;
8+
9+
public class KeybindType extends ComponentType<KeybindComponent> {
10+
public static final MapCodec<KeybindComponent> CODEC = RecordCodecBuilder.mapCodec(instance ->
11+
instance.group(Codec.STRING.fieldOf("keybind").forGetter(content -> content.keybind()))
12+
.apply(instance, Component::keybind));
13+
14+
public static final KeybindType INSTANCE = new KeybindType(CODEC);
15+
16+
public KeybindType(final MapCodec<KeybindComponent> codec) {
17+
super("keybind", codec);
18+
}
19+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package net.kyori.adventure.dfu.component;
2+
3+
import com.mojang.serialization.Codec;
4+
import com.mojang.serialization.MapCodec;
5+
import com.mojang.serialization.codecs.RecordCodecBuilder;
6+
import java.util.Optional;
7+
import net.kyori.adventure.dfu.AdventureCodecs;
8+
import net.kyori.adventure.dfu.component.nbt.NBTSource;
9+
import net.kyori.adventure.dfu.component.nbt.NBTSourceType;
10+
import net.kyori.adventure.text.Component;
11+
import net.kyori.adventure.text.NBTComponent;
12+
13+
public class NBTComponentType extends ComponentType<NBTComponent> {
14+
15+
public static final MapCodec<NBTComponent> CODEC = RecordCodecBuilder.mapCodec(instance ->
16+
instance.group(
17+
Codec.STRING.fieldOf("nbt").forGetter(nbtComponent -> nbtComponent.nbtPath()),
18+
Codec.BOOL.lenientOptionalFieldOf("interpret", false).forGetter(nbtComponent -> nbtComponent.interpret()),
19+
AdventureCodecs.COMPONENT.lenientOptionalFieldOf("separator").forGetter(nbtComponent -> Optional.ofNullable(nbtComponent.separator())),
20+
NBTSourceType.CODEC.forGetter(NBTSource::from)
21+
).apply(instance, NBTComponentType::of));
22+
23+
public static final NBTComponentType INSTANCE = new NBTComponentType(CODEC);
24+
25+
private NBTComponentType(final MapCodec<NBTComponent> codec) {
26+
super("nbt", codec);
27+
}
28+
29+
private static NBTComponent of(String nbtPath, Boolean interpret, Optional<Component> separator, NBTSource nbtSource) {
30+
return ((NBTComponent) nbtSource.builder()
31+
.interpret(interpret)
32+
.nbtPath(nbtPath)
33+
.separator(separator.orElse(null))
34+
.build());
35+
}
36+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package net.kyori.adventure.dfu.component;
2+
3+
import com.mojang.serialization.Codec;
4+
import com.mojang.serialization.MapCodec;
5+
import com.mojang.serialization.codecs.RecordCodecBuilder;
6+
import net.kyori.adventure.text.Component;
7+
import net.kyori.adventure.text.ScoreComponent;
8+
9+
public class ScoreType extends ComponentType<ScoreComponent> {
10+
public static final MapCodec<ScoreComponent> INNER_CODEC = RecordCodecBuilder.mapCodec(instance ->
11+
instance.group(
12+
Codec.STRING.fieldOf("name").forGetter(ScoreComponent::name),
13+
Codec.STRING.fieldOf("objective").forGetter(ScoreComponent::objective)
14+
).apply(instance, Component::score));
15+
public static final MapCodec<ScoreComponent> CODEC = INNER_CODEC.fieldOf("score");
16+
17+
public static final ScoreType INSTANCE = new ScoreType(CODEC);
18+
19+
private ScoreType(final MapCodec<ScoreComponent> codec) {
20+
super("score", codec);
21+
}
22+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package net.kyori.adventure.dfu.component;
2+
3+
import com.mojang.serialization.Codec;
4+
import com.mojang.serialization.MapCodec;
5+
import com.mojang.serialization.codecs.RecordCodecBuilder;
6+
import java.util.Optional;
7+
import net.kyori.adventure.dfu.AdventureCodecs;
8+
import net.kyori.adventure.text.Component;
9+
import net.kyori.adventure.text.SelectorComponent;
10+
11+
public class SelectorType extends ComponentType<SelectorComponent> {
12+
public static final MapCodec<SelectorComponent> CODEC = RecordCodecBuilder.mapCodec(instance ->
13+
instance.group(
14+
Codec.STRING.fieldOf("selector").forGetter(SelectorComponent::pattern),
15+
AdventureCodecs.COMPONENT.optionalFieldOf("separator").forGetter(selectorComponent -> Optional.ofNullable(selectorComponent.separator()))
16+
).apply(instance, (selector, separator) -> Component.selector(selector, separator.orElse(null))));
17+
18+
public static final SelectorType INSTANCE = new SelectorType(CODEC);
19+
20+
private SelectorType(final MapCodec<SelectorComponent> codec) {
21+
super("selector", codec);
22+
}
23+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package net.kyori.adventure.dfu.component;
2+
3+
import com.mojang.serialization.Codec;
4+
import com.mojang.serialization.MapCodec;
5+
import com.mojang.serialization.codecs.RecordCodecBuilder;
6+
import net.kyori.adventure.text.Component;
7+
import net.kyori.adventure.text.TextComponent;
8+
9+
public class TextType extends ComponentType<TextComponent> {
10+
public static final MapCodec<TextComponent> CODEC = RecordCodecBuilder.mapCodec(instance ->
11+
instance.group(Codec.STRING.fieldOf("text").forGetter(TextComponent::content))
12+
.apply(instance, Component::text));
13+
14+
public static final TextType INSTANCE = new TextType(CODEC);
15+
16+
public TextType(final MapCodec<TextComponent> codec) {
17+
super("text", codec);
18+
}
19+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package net.kyori.adventure.dfu.component;
2+
3+
import com.mojang.datafixers.util.Either;
4+
import com.mojang.serialization.Codec;
5+
import com.mojang.serialization.DataResult;
6+
import com.mojang.serialization.MapCodec;
7+
import com.mojang.serialization.codecs.RecordCodecBuilder;
8+
import java.util.ArrayList;
9+
import java.util.List;
10+
import java.util.Objects;
11+
import java.util.Optional;
12+
import net.kyori.adventure.dfu.AdventureCodecs;
13+
import net.kyori.adventure.dfu.Codecs;
14+
import net.kyori.adventure.text.Component;
15+
import net.kyori.adventure.text.TranslatableComponent;
16+
import net.kyori.adventure.text.TranslationArgument;
17+
import org.jetbrains.annotations.Nullable;
18+
19+
public class TranslatableType extends ComponentType<TranslatableComponent> {
20+
public static final TranslationArgument[] EMPTY_ARGUMENTS = new TranslationArgument[0];
21+
private static final Codec<Object> OBJECT_ARGUMENT_CODEC = Codecs.BASIC_OBJECT.validate(TranslatableType::validate);
22+
private static final Codec<Object> ARGUMENT_CODEC = Codec.either(OBJECT_ARGUMENT_CODEC, AdventureCodecs.COMPONENT)
23+
.xmap(either ->
24+
either.map(object -> object, text -> Objects.requireNonNullElse(text.toString(), text)),
25+
argument -> {
26+
Either<Object, Component> either;
27+
if (argument instanceof Component) {
28+
Component text = (Component) argument;
29+
either = Either.right(text);
30+
} else {
31+
either = Either.left(argument);
32+
}
33+
return either;
34+
});
35+
public static final MapCodec<TranslatableComponent> CODEC = RecordCodecBuilder.mapCodec(instance ->
36+
instance.group(
37+
Codec.STRING.fieldOf("translate")
38+
.forGetter(TranslatableComponent::key),
39+
Codec.STRING.lenientOptionalFieldOf("fallback")
40+
.forGetter(content -> Optional.ofNullable(content.fallback())),
41+
ARGUMENT_CODEC.listOf().optionalFieldOf("with")
42+
.forGetter(content -> toOptionalList(content.arguments()))
43+
).apply(instance, TranslatableType::of));
44+
45+
public static final TranslatableType INSTANCE = new TranslatableType(CODEC);
46+
47+
private TranslatableType(final MapCodec<TranslatableComponent> codec) {
48+
super("translatable", codec);
49+
}
50+
51+
private static boolean isPrimitive(@Nullable Object object) {
52+
return object instanceof String || object instanceof Number || object instanceof Boolean;
53+
}
54+
55+
private static DataResult<Object> validate(@Nullable Object object) {
56+
if (!isPrimitive(object)) {
57+
return DataResult.error(() -> "This value needs to be parsed as component");
58+
}
59+
return DataResult.success(object);
60+
}
61+
62+
private static Optional<List<Object>> toOptionalList(List<?> args) {
63+
return args.isEmpty() ? Optional.empty() : Optional.of(new ArrayList<>(args));
64+
}
65+
66+
private static TranslationArgument[] toArray(Optional<List<Object>> args) {
67+
if (args.isEmpty()) {
68+
return EMPTY_ARGUMENTS;
69+
}
70+
final List<Object> objects = args.get();
71+
final TranslationArgument[] array = new TranslationArgument[objects.size()];
72+
for (int i = 0; i < objects.size(); i++) {
73+
final Object object = objects.get(i);
74+
if (object instanceof TranslationArgument)
75+
array[i] = (TranslationArgument) object;
76+
else if (isPrimitive(object)) {
77+
if (object instanceof String) {
78+
array[i] = TranslationArgument.component(Component.text(object.toString()));
79+
} else if (object instanceof Number) {
80+
array[i] = TranslationArgument.numeric((Number) object);
81+
} else if (object instanceof Boolean) {
82+
array[i] = TranslationArgument.bool((Boolean) object);
83+
} else if (object instanceof Component) {
84+
array[i] = TranslationArgument.component((Component) object);
85+
}
86+
}
87+
}
88+
return array;
89+
}
90+
91+
92+
private static TranslatableComponent of(String key, Optional<String> fallback, Optional<List<Object>> args) {
93+
return Component.translatable(key, fallback.orElse(null), toArray(args));
94+
}
95+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package net.kyori.adventure.dfu.component.nbt;
2+
3+
import com.mojang.serialization.Codec;
4+
import com.mojang.serialization.MapCodec;
5+
import com.mojang.serialization.codecs.RecordCodecBuilder;
6+
import net.kyori.adventure.text.BlockNBTComponent;
7+
import net.kyori.adventure.text.Component;
8+
import net.kyori.adventure.text.NBTComponentBuilder;
9+
10+
public final class BlockNBTSourceType extends NBTSource {
11+
public static final MapCodec<BlockNBTSourceType> CODEC = RecordCodecBuilder.mapCodec(instance ->
12+
instance.group(Codec.STRING.fieldOf("block").forGetter(BlockNBTSourceType::rawPos))
13+
.apply(instance, BlockNBTSourceType::new));
14+
public static final NBTSourceType TYPE = new NBTSourceType("block", CODEC);
15+
private final String rawPos;
16+
17+
public BlockNBTSourceType(String rawPos) {
18+
this.rawPos = rawPos;
19+
}
20+
21+
@Override
22+
public NBTComponentBuilder builder() {
23+
return Component.blockNBT()
24+
.pos(BlockNBTComponent.Pos.fromString(rawPos));
25+
}
26+
27+
@Override
28+
public NBTSourceType type() {
29+
return TYPE;
30+
}
31+
32+
public String rawPos() {
33+
return rawPos;
34+
}
35+
}

0 commit comments

Comments
 (0)