Skip to content

Commit 1d7630a

Browse files
committed
Add overloads to UnsafeValues#loadAdvancement
1 parent 76fb506 commit 1d7630a

2 files changed

Lines changed: 137 additions & 17 deletions

File tree

paper-api/src/main/java/org/bukkit/UnsafeValues.java

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
import com.google.common.collect.Multimap;
44
import io.papermc.paper.entity.EntitySerializationFlag;
55
import io.papermc.paper.registry.RegistryKey;
6+
import java.util.List;
7+
import java.util.Map;
8+
import net.kyori.adventure.key.Key;
69
import org.bukkit.advancement.Advancement;
710
import org.bukkit.attribute.Attribute;
811
import org.bukkit.attribute.AttributeModifier;
9-
import org.bukkit.block.Biome;
1012
import org.bukkit.block.data.BlockData;
1113
import org.bukkit.damage.DamageSource;
1214
import org.bukkit.damage.DamageType;
@@ -23,7 +25,6 @@
2325
import org.jetbrains.annotations.ApiStatus;
2426
import org.jetbrains.annotations.NotNull;
2527
import org.jetbrains.annotations.Nullable;
26-
import java.util.Map;
2728

2829
/**
2930
* This interface provides value conversions that may be specific to a
@@ -81,7 +82,46 @@ public interface UnsafeValues {
8182
* @param advancement representation of the advancement
8283
* @return the loaded advancement or null if an error occurred
8384
*/
84-
Advancement loadAdvancement(NamespacedKey key, String advancement);
85+
default Advancement loadAdvancement(NamespacedKey key, String advancement) {
86+
return loadAdvancement(key, advancement, true);
87+
}
88+
89+
/**
90+
* Load an advancement represented by the specified string into the server.
91+
* The advancement format is governed by Minecraft and has no specified
92+
* layout.
93+
* <br>
94+
* It is currently a JSON object, as described by the <a href="https://minecraft.wiki/w/Advancements">Minecraft wiki</a>.
95+
* <br>
96+
* Loaded advancements will only be stored and persisted across server restarts
97+
* and reloads, if the {@code persist} parameter is set to true.
98+
* <br>
99+
* Callers should be prepared for {@link Exception} to be thrown.
100+
*
101+
* @param key the unique advancement key
102+
* @param advancement representation of the advancement
103+
* @param persist whether to store this advancement in the bukkit datapack for persistence
104+
* @return the loaded advancement or null if an error occurred
105+
*/
106+
Advancement loadAdvancement(Key key, String advancement, boolean persist);
107+
108+
/**
109+
* Load multiple advancements represented by the specified strings into the server.
110+
* The advancement format is governed by Minecraft and has no specified
111+
* layout.
112+
* <br>
113+
* It is currently a JSON object, as described by the <a href="https://minecraft.wiki/w/Advancements">Minecraft wiki</a>.
114+
* <br>
115+
* Loaded advancements will only be stored and persisted across server restarts
116+
* and reloads, if the {@code persist} parameter is set to true.
117+
* <br>
118+
* Callers should be prepared for {@link Exception} to be thrown.
119+
*
120+
* @param advancements the advancements to register. The key is the unique advancement key and the value is the advancement's JSON representation
121+
* @param persist whether to store this advancement in the bukkit datapack for persistence
122+
* @return list of all successfully loaded advancements
123+
*/
124+
List<Advancement> loadAdvancements(Map<Key, String> advancements, boolean persist);
85125

86126
/**
87127
* Delete an advancement which was loaded and saved by

paper-server/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java

Lines changed: 94 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import ca.spottedleaf.moonrise.common.PlatformHooks;
44
import com.google.common.base.Preconditions;
5+
import com.google.common.collect.ImmutableMap;
56
import com.google.common.collect.Multimap;
67
import com.google.common.io.Files;
78
import com.google.gson.JsonElement;
@@ -12,6 +13,8 @@
1213
import com.mojang.logging.LogUtils;
1314
import com.mojang.serialization.Dynamic;
1415
import com.mojang.serialization.JsonOps;
16+
import io.papermc.paper.adventure.PaperAdventure;
17+
import io.papermc.paper.entity.EntitySerializationFlag;
1518
import io.papermc.paper.registry.RegistryKey;
1619
import java.io.File;
1720
import java.io.IOException;
@@ -25,9 +28,12 @@
2528
import java.util.Set;
2629
import java.util.logging.Level;
2730
import java.util.stream.Stream;
28-
import io.papermc.paper.entity.EntitySerializationFlag;
31+
import net.kyori.adventure.key.Key;
2932
import net.minecraft.SharedConstants;
3033
import net.minecraft.advancements.AdvancementHolder;
34+
import net.minecraft.advancements.AdvancementNode;
35+
import net.minecraft.advancements.AdvancementTree;
36+
import net.minecraft.advancements.TreeNodePosition;
3137
import net.minecraft.commands.Commands;
3238
import net.minecraft.commands.arguments.item.ItemParser;
3339
import net.minecraft.core.registries.BuiltInRegistries;
@@ -38,6 +44,7 @@
3844
import net.minecraft.nbt.StringTag;
3945
import net.minecraft.nbt.Tag;
4046
import net.minecraft.nbt.TagParser;
47+
import net.minecraft.resources.RegistryOps;
4148
import net.minecraft.resources.ResourceLocation;
4249
import net.minecraft.server.MinecraftServer;
4350
import net.minecraft.server.level.ServerLevel;
@@ -299,9 +306,9 @@ private static File getBukkitDataPackFolder() {
299306
}
300307

301308
@Override
302-
public Advancement loadAdvancement(NamespacedKey key, String advancement) {
303-
Preconditions.checkArgument(Bukkit.getAdvancement(key) == null, "Advancement %s already exists", key);
304-
ResourceLocation resourceKey = CraftNamespacedKey.toMinecraft(key);
309+
public Advancement loadAdvancement(Key key, String advancement, boolean persist) {
310+
ResourceLocation resourceKey = PaperAdventure.asVanilla(key);
311+
Preconditions.checkArgument(MinecraftServer.getServer().getAdvancements().get(resourceKey) == null, "Advancement %s already exists", key);
305312

306313
JsonElement jsonelement = JsonParser.parseString(advancement);
307314
final net.minecraft.resources.RegistryOps<JsonElement> ops = CraftRegistry.getMinecraftRegistry().createSerializationContext(JsonOps.INSTANCE); // Paper - use RegistryOps
@@ -326,30 +333,101 @@ public Advancement loadAdvancement(NamespacedKey key, String advancement) {
326333
}
327334
}
328335

329-
Advancement bukkit = Bukkit.getAdvancement(key);
330-
331-
if (bukkit != null) {
332-
File file = new File(CraftMagicNumbers.getBukkitDataPackFolder(), "data" + File.separator + key.getNamespace() + File.separator + "advancements" + File.separator + key.getKey() + ".json");
333-
file.getParentFile().mkdirs();
336+
AdvancementHolder nmsAdvancement = MinecraftServer.getServer().getAdvancements().get(resourceKey);
337+
if (nmsAdvancement != null) {
338+
if (persist) {
339+
File file = new File(CraftMagicNumbers.getBukkitDataPackFolder(), "data" + File.separator + key.namespace() + File.separator + "advancements" + File.separator + key.value() + ".json");
340+
file.getParentFile().mkdirs();
334341

335-
try {
336-
Files.write(advancement, file, StandardCharsets.UTF_8);
337-
} catch (IOException ex) {
338-
Bukkit.getLogger().log(Level.SEVERE, "Error saving advancement " + key, ex);
342+
try {
343+
Files.write(advancement, file, StandardCharsets.UTF_8);
344+
} catch (IOException ex) {
345+
Bukkit.getLogger().log(Level.SEVERE, "Error saving advancement " + key, ex);
346+
}
339347
}
340348

341349
MinecraftServer.getServer().getPlayerList().getPlayers().forEach(player -> {
342350
player.getAdvancements().reload(MinecraftServer.getServer().getAdvancements());
343351
player.getAdvancements().flushDirty(player, false);
344352
});
345353

346-
return bukkit;
354+
return nmsAdvancement.toBukkit();
347355
}
348356
}
349357

350358
return null;
351359
}
352360

361+
@Override
362+
public List<Advancement> loadAdvancements(final Map<Key, String> advancements, final boolean persist) {
363+
for (Map.Entry<Key, String> entry : advancements.entrySet()) {
364+
Preconditions.checkArgument(MinecraftServer.getServer().getAdvancements().get(PaperAdventure.asVanilla(entry.getKey())) == null, "Advancement %s already exists", entry.getKey());
365+
}
366+
367+
final List<LoadAdvancementEntry> mappedAdvancements = new ArrayList<>(advancements.size());
368+
for (final Map.Entry<Key, String> entry : advancements.entrySet()) {
369+
mappedAdvancements.add(new LoadAdvancementEntry(entry.getKey(), entry.getValue(), PaperAdventure.asVanilla(entry.getKey()), JsonParser.parseString(entry.getValue())));
370+
}
371+
372+
final List<Advancement> outAdvancements = new ArrayList<>(mappedAdvancements.size());
373+
final ImmutableMap.Builder<ResourceLocation, AdvancementHolder> mapBuilder = ImmutableMap.builder();
374+
mapBuilder.putAll(MinecraftServer.getServer().getAdvancements().advancements);
375+
376+
final RegistryOps<JsonElement> ops = CraftRegistry.getMinecraftRegistry().createSerializationContext(JsonOps.INSTANCE);
377+
final List<AdvancementHolder> advancementHolders = new ArrayList<>(mappedAdvancements.size());
378+
for (final LoadAdvancementEntry entry : mappedAdvancements) {
379+
final net.minecraft.advancements.Advancement advancement = net.minecraft.advancements.Advancement.CODEC.parse(ops, entry.nmsAdvancement()).getOrThrow(JsonParseException::new);
380+
if (advancement == null) {
381+
continue;
382+
}
383+
384+
final AdvancementHolder holder = new AdvancementHolder(entry.nmsResourceLocation(), advancement);
385+
mapBuilder.put(entry.nmsResourceLocation(), holder);
386+
advancementHolders.add(holder);
387+
}
388+
389+
MinecraftServer.getServer().getAdvancements().advancements = mapBuilder.build();
390+
391+
final AdvancementTree tree = MinecraftServer.getServer().getAdvancements().tree();
392+
tree.addAll(advancementHolders);
393+
394+
for (LoadAdvancementEntry entry : mappedAdvancements) {
395+
// recalculate advancement position
396+
final AdvancementNode node = tree.get(entry.nmsResourceLocation());
397+
if (node != null) {
398+
final AdvancementNode root = node.root();
399+
if (root.holder().value().display().isPresent()) {
400+
TreeNodePosition.run(root);
401+
}
402+
}
403+
404+
AdvancementHolder nmsAdvancement = MinecraftServer.getServer().getAdvancements().get(entry.nmsResourceLocation);
405+
if (nmsAdvancement != null) {
406+
if (persist) {
407+
File file = new File(CraftMagicNumbers.getBukkitDataPackFolder(), "data" + File.separator + entry.key().namespace() + File.separator + "advancements" + File.separator + entry.key().value() + ".json");
408+
file.getParentFile().mkdirs();
409+
410+
try {
411+
Files.write(entry.rawAdvancement(), file, StandardCharsets.UTF_8);
412+
} catch (IOException ex) {
413+
Bukkit.getLogger().log(Level.SEVERE, "Error saving advancement " + entry.key(), ex);
414+
}
415+
}
416+
417+
outAdvancements.add(nmsAdvancement.toBukkit());
418+
}
419+
}
420+
421+
if (!outAdvancements.isEmpty()) {
422+
MinecraftServer.getServer().getPlayerList().getPlayers().forEach(player -> {
423+
player.getAdvancements().reload(MinecraftServer.getServer().getAdvancements());
424+
player.getAdvancements().flushDirty(player, false);
425+
});
426+
}
427+
428+
return outAdvancements;
429+
}
430+
353431
@Override
354432
public boolean removeAdvancement(NamespacedKey key) {
355433
File file = new File(CraftMagicNumbers.getBukkitDataPackFolder(), "data" + File.separator + key.getNamespace() + File.separator + "advancements" + File.separator + key.getKey() + ".json");
@@ -860,4 +938,6 @@ public io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager<org.bukkit.
860938
public org.bukkit.inventory.ItemStack createEmptyStack() {
861939
return CraftItemStack.asCraftMirror(null);
862940
}
941+
942+
private record LoadAdvancementEntry(Key key, String rawAdvancement, ResourceLocation nmsResourceLocation, JsonElement nmsAdvancement) {}
863943
}

0 commit comments

Comments
 (0)