ПЕРЕДУМОВИ
Спершу переконайтеся, що ви виконали процес налаштування datagen.
Налаштування
По-перше, нам потрібно створити свого постачальника. Створіть клас, який extends FabricAdvancementProvider, і заповніть базові методи:
java
public class FabricDocsReferenceAdvancementProvider extends FabricAdvancementProvider {
protected FabricDocsReferenceAdvancementProvider(FabricDataOutput output, CompletableFuture<RegistryWrapper.WrapperLookup> registryLookup) {
super(output, registryLookup);
}
@Override
public void generateAdvancement(RegistryWrapper.WrapperLookup wrapperLookup, Consumer<AdvancementEntry> consumer) {
}
}1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
Щоб завершити налаштування, додайте цього провайдера до своєї DataGeneratorEntrypoint у методі onInitializeDataGenerator.
java
pack.addProvider(FabricDocsReferenceAdvancementProvider::new);1
Структура досягнення
Досягнення складається з кількох різних компонентів. Разом із вимогами, які називаються «критерієм», він може мати:
AdvancementDisplay, який повідомляє грі, як показувати до досягнення гравцям,AdvancementRequirements, які є списками списків критеріїв, які вимагають заповнення принаймні одного критерію з кожного підсписку,AdvancementRewards, які гравець отримує за виконання досягнення.CriterionMerger, який повідомляє досягненню, як обробляти кілька критеріїв, і- Батьківський
Advancement, який організовує ієрархію, яку ви бачите на екрані «Досягнення».
Прості досягнення
Ось просте досягнення для отримання ґрунту:
java
No lines matched.1
WARNING
Під час створення записів про досягнення пам’ятайте, що функція приймає Identifier досягнення у форматі String!
Вивід JSON
json
{
"criteria": {
"got_dirt": {
"conditions": {
"items": [
{
"items": "minecraft:dirt"
}
]
},
"trigger": "minecraft:inventory_changed"
}
},
"display": {
"background": "minecraft:textures/gui/advancements/backgrounds/adventure.png",
"description": "Now make a house from it",
"icon": {
"count": 1,
"id": "minecraft:dirt"
},
"title": "Your First Dirt Block"
},
"requirements": [
[
"got_dirt"
]
],
"sends_telemetry_event": true
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Ще один приклад
Щоб зрозуміти, додамо ще одне досягнення. Ми попрактикуємося додавати нагороди, використовувати кілька критеріїв і призначати батьківські досягнення:
java
final RegistryWrapper.Impl<Item> itemLookup = wrapperLookup.getOrThrow(RegistryKeys.ITEM);
AdvancementEntry appleAndBeef = Advancement.Builder.create()
.parent(getDirt)
.display(
Items.APPLE,
Text.literal("Apple and Beef"),
Text.literal("Ate an apple and beef"),
null, // Children don't need a background, the root advancement takes care of that
AdvancementFrame.CHALLENGE,
true,
true,
false
)
.criterion("ate_apple", ConsumeItemCriterion.Conditions.item(itemLookup, Items.APPLE))
.criterion("ate_cooked_beef", ConsumeItemCriterion.Conditions.item(itemLookup, Items.COOKED_BEEF))
.build(consumer, FabricDocsReference.MOD_ID + ":apple_and_beef");1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Власні критерії
WARNING
У той час як datagen може бути на стороні клієнта, Criterions і Predicates знаходяться в основному вихідному наборі (обидві сторони), оскільки сервер повинен ініціювати та оцінювати їх.
Визначення
criterion (у множині: criteria) — це те, що гравець може зробити (або що може статися з гравцем), що може бути зараховано для досягнення. У грі є багато критеріїв, які можна знайти в пакеті net.minecraft.advancement.criterion. Як правило, вам знадобиться новий критерій, лише якщо ви запровадите в гру спеціальну механіку.
Conditions оцінюються за критеріями. Критерій зараховується, лише якщо виконуються всі відповідні умови. Умови зазвичай виражаються присудком.
Predicate – це те, що приймає значення та повертає boolean. Наприклад, Predicate<Item> може повернути true, якщо предмет є діамантом, тоді як Predicate<LivingEntity> може повернути true, якщо сутність не є ворожою до селян.
Створення власних критеріїв
По-перше, нам знадобиться нова механіка для впровадження. Скажімо гравцеві, який інструмент він використовував щоразу, коли ламав блок.
java
public class FabricDocsReferenceDatagenAdvancement implements ModInitializer {
@Override
public void onInitialize() {
HashMap<Item, Integer> tools = new HashMap<>();
PlayerBlockBreakEvents.AFTER.register(((world, player, blockPos, blockState, blockEntity) -> {
if (player instanceof ServerPlayerEntity serverPlayer) { // Only triggers on the server side
Item item = player.getMainHandStack().getItem();
Integer usedCount = tools.getOrDefault(item, 0);
usedCount++;
tools.put(item, usedCount);
serverPlayer.sendMessage(Text.of("You've used \"" + item + "\" as a tool " + usedCount + " times!"));
}
}));
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Зауважте, що цей код дійсно поганий. HashMap не зберігається ніде постійно, тому він буде скидатися кожного разу, коли гра перезапускається. Це просто для того, щоб похизуватися критеріями. Почніть гру та спробуйте!
Далі створимо наш спеціальний критерій, UseToolCriterion. Йому знадобиться власний клас Conditions, тому ми створимо їх обидва одночасно:
java
public class UseToolCriterion extends AbstractCriterion<UseToolCriterion.Conditions> {
@Override
public Codec<Conditions> getConditionsCodec() {
return Conditions.CODEC;
}
public record Conditions(Optional<LootContextPredicate> playerPredicate) implements AbstractCriterion.Conditions {
public static Codec<UseToolCriterion.Conditions> CODEC = LootContextPredicate.CODEC.optionalFieldOf("player")
.xmap(Conditions::new, Conditions::player).codec();
@Override
public Optional<LootContextPredicate> player() {
return playerPredicate;
}
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Вау, це багато! Розберімо це.
UseToolCriterion– цеAbstractCriterion, до якого можуть застосовуватисяУмови.Conditionsмають полеplayerPredicate. УсіConditionsповинні мати предикат гравця (технічноLootContextPredicate).Conditionsтакож маютьCODEC. ЦейКодекє просто кодеком для його одного поля,playerPredicate, з додатковими інструкціями для перетворення між ними (xmap).
INFO
Щоб дізнатися більше про кодеки, перегляньте сторінку кодеки.
Нам знадобиться спосіб перевірити, чи виконуються умови. Нумо додаймо допоміжний метод до Conditions:
java
public boolean requirementsMet() {
return true; // AbstractCriterion#trigger helpfully checks the playerPredicate for us.
}1
2
3
4
2
3
4
Тепер, коли ми маємо критерій і його умови, нам потрібен спосіб його запустити. Додайте метод запуску до UseToolCriterion:
java
public void trigger(ServerPlayerEntity player) {
trigger(player, Conditions::requirementsMet);
}1
2
3
4
2
3
4
Майже готово! Далі нам потрібен екземпляр нашого критерію для роботи. Помістімо його в новий клас під назвою ModCriteria.
java
public class ModCriteria {
public static final UseToolCriterion USE_TOOL = Criteria.register(FabricDocsReference.MOD_ID + ":use_tool", new UseToolCriterion());
}1
2
3
4
2
3
4
Щоб переконатися, що наші критерії ініціалізуються в потрібний час, додайте порожній метод init:
java
// :::datagen-advancements:mod-criteria
public static final UseToolCriterion USE_TOOL = Criteria.register(FabricDocsReference.MOD_ID + ":use_tool", new UseToolCriterion());
// :::datagen-advancements:mod-criteria
// :::datagen-advancements:new-mod-criteria
public static final ParameterizedUseToolCriterion PARAMETERIZED_USE_TOOL = Criteria.register(FabricDocsReference.MOD_ID + ":parameterized_use_tool", new ParameterizedUseToolCriterion());
// :::datagen-advancements:mod-criteria1
2
3
4
5
6
7
2
3
4
5
6
7
І викличте це у своєму ініціалізаторі моду:
java
ModCriteria.init();1
Нарешті, нам потрібно запустити наші критерії. Додайте це туди, де ми надіслали повідомлення гравцеві в основному класі мода.
java
ModCriteria.USE_TOOL.trigger(serverPlayer);1
Ваш новий блискучий критерій готовий до використання! Нумо додамо до нашого постачальника:
java
AdvancementEntry breakBlockWithTool = Advancement.Builder.create()
.parent(getDirt)
.display(
Items.DIAMOND_SHOVEL,
Text.literal("Not a Shovel"),
Text.literal("That's not a shovel (probably)"),
null,
AdvancementFrame.GOAL,
true,
true,
false
)
.criterion("break_block_with_tool", ModCriteria.USE_TOOL.create(new UseToolCriterion.Conditions(Optional.empty())))
.build(consumer, FabricDocsReference.MOD_ID + ":break_block_with_tool");1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
Запустіть завдання datagen ще раз, і ви отримаєте нове досягнення, з яким можна грати!
Умови з параметрами
Це все добре, але що, якщо ми хочемо надати досягнення лише після виконання певної роботи 5 разів? А чому б не ще один у 10 разів? Для цього нам потрібно надати умові параметр. Ви можете залишитися з UseToolCriterion, або ви можете слідувати разом із новим ParameterizedUseToolCriterion. На практиці ви повинні мати лише параметризований, але ми збережемо обидва для цього підручника.
Попрацюймо знизу вгору. Нам потрібно буде перевірити, чи виконуються вимоги, тому відредагуємо наш метод Conditions#requirementsMet:
java
public boolean requirementsMet(int totalTimes) {
return totalTimes > requiredTimes; // AbstractCriterion#trigger helpfully checks the playerPredicate for us.
}1
2
3
4
2
3
4
requiredTimes не існує, тому зробіть його параметром Conditions:
java
public record Conditions(Optional<LootContextPredicate> playerPredicate, int requiredTimes) implements AbstractCriterion.Conditions {
@Override
public Optional<LootContextPredicate> player() {
return playerPredicate;
}
// :::datagen-advancements:new-requirements-met
public boolean requirementsMet(int totalTimes) {
return totalTimes > requiredTimes; // AbstractCriterion#trigger helpfully checks the playerPredicate for us.
}
// :::datagen-advancements:new-requirements-met
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Тепер наш кодек неправильний. Напишімо новий кодек для нових змін:
java
public static Codec<ParameterizedUseToolCriterion.Conditions> CODEC = RecordCodecBuilder.create(instance -> instance.group(
LootContextPredicate.CODEC.optionalFieldOf("player").forGetter(Conditions::player),
Codec.INT.fieldOf("requiredTimes").forGetter(Conditions::requiredTimes)
).apply(instance, Conditions::new));
// :::datagen-advancements:new-parameter
@Override
public Optional<LootContextPredicate> player() {
return playerPredicate;
}
// :::datagen-advancements:new-requirements-met
public boolean requirementsMet(int totalTimes) {
return totalTimes > requiredTimes; // AbstractCriterion#trigger helpfully checks the playerPredicate for us.
}
// :::datagen-advancements:new-requirements-met
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Рухаючись далі, тепер нам потрібно виправити наш метод trigger:
java
public void trigger(ServerPlayerEntity player, int totalTimes) {
trigger(player, conditions -> conditions.requirementsMet(totalTimes));
}1
2
3
4
2
3
4
Якщо ви створили новий критерій, нам потрібно додати його до ModCriteria
java
public static final ParameterizedUseToolCriterion PARAMETERIZED_USE_TOOL = Criteria.register(FabricDocsReference.MOD_ID + ":parameterized_use_tool", new ParameterizedUseToolCriterion());
// :::datagen-advancements:mod-criteria
// :::datagen-advancements:mod-criteria-init
public static void init() {
}1
2
3
4
5
6
7
2
3
4
5
6
7
І назвіть це в нашому головному класі, там же, де старий:
java
ModCriteria.PARAMETERIZED_USE_TOOL.trigger(serverPlayer, usedCount);1
Додайте досягнення до свого постачальника:
java
AdvancementEntry breakBlockWithToolFiveTimes = Advancement.Builder.create()
.parent(breakBlockWithTool)
.display(
Items.GOLDEN_SHOVEL,
Text.literal("Not a Shovel Still"),
Text.literal("That's still not a shovel (probably)"),
null,
AdvancementFrame.GOAL,
true,
true,
false
)
.criterion("break_block_with_tool_five_times", ModCriteria.PARAMETERIZED_USE_TOOL.create(new ParameterizedUseToolCriterion.Conditions(Optional.empty(), 5)))
.build(consumer, FabricDocsReference.MOD_ID + ":break_block_with_tool_five_times");1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
Запустіть datagen ще раз, і ви нарешті закінчили!




