/* * Decompiled with CFR 0.152. * * Could not load the following classes: * com.google.gson.Gson * com.google.gson.GsonBuilder * com.google.gson.JsonElement * com.google.gson.JsonIOException * com.google.gson.JsonParseException * com.mojang.datafixers.DataFixer * com.mojang.logging.LogUtils * com.mojang.serialization.Codec * com.mojang.serialization.DynamicOps * com.mojang.serialization.JsonOps * org.jspecify.annotations.Nullable * org.slf4j.Logger */ package net.minecraft.server; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonElement; import com.google.gson.JsonIOException; import com.google.gson.JsonParseException; import com.mojang.datafixers.DataFixer; import com.mojang.logging.LogUtils; import com.mojang.serialization.Codec; import com.mojang.serialization.DynamicOps; import com.mojang.serialization.JsonOps; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.Writer; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.OpenOption; import java.nio.file.Path; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; import java.util.function.BiConsumer; import net.minecraft.advancements.Advancement; import net.minecraft.advancements.AdvancementHolder; import net.minecraft.advancements.AdvancementNode; import net.minecraft.advancements.AdvancementProgress; import net.minecraft.advancements.AdvancementTree; import net.minecraft.advancements.Criterion; import net.minecraft.advancements.CriterionProgress; import net.minecraft.advancements.CriterionTrigger; import net.minecraft.advancements.CriterionTriggerInstance; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.network.protocol.game.ClientboundSelectAdvancementsTabPacket; import net.minecraft.network.protocol.game.ClientboundUpdateAdvancementsPacket; import net.minecraft.resources.Identifier; import net.minecraft.server.ServerAdvancementManager; import net.minecraft.server.advancements.AdvancementVisibilityEvaluator; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.players.PlayerList; import net.minecraft.util.FileUtil; import net.minecraft.util.StrictJsonParser; import net.minecraft.util.datafix.DataFixTypes; import net.minecraft.world.level.gamerules.GameRules; import org.jspecify.annotations.Nullable; import org.slf4j.Logger; public class PlayerAdvancements { private static final Logger LOGGER = LogUtils.getLogger(); private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); private final PlayerList playerList; private final Path playerSavePath; private AdvancementTree tree; private final Map progress = new LinkedHashMap(); private final Set visible = new HashSet(); private final Set progressChanged = new HashSet(); private final Set rootsToUpdate = new HashSet(); private ServerPlayer player; private @Nullable AdvancementHolder lastSelectedTab; private boolean isFirstPacket = true; private final Codec codec; public PlayerAdvancements(DataFixer dataFixer, PlayerList playerList, ServerAdvancementManager manager, Path playerSavePath, ServerPlayer player) { this.playerList = playerList; this.playerSavePath = playerSavePath; this.player = player; this.tree = manager.tree(); int defaultVersion = 1343; this.codec = DataFixTypes.ADVANCEMENTS.wrapCodec(Data.CODEC, dataFixer, 1343); this.load(manager); } public void setPlayer(ServerPlayer player) { this.player = player; } public void stopListening() { for (CriterionTrigger criterionTrigger : BuiltInRegistries.TRIGGER_TYPES) { criterionTrigger.removePlayerListeners(this); } } public void reload(ServerAdvancementManager manager) { this.stopListening(); this.progress.clear(); this.visible.clear(); this.rootsToUpdate.clear(); this.progressChanged.clear(); this.isFirstPacket = true; this.lastSelectedTab = null; this.tree = manager.tree(); this.load(manager); } private void registerListeners(ServerAdvancementManager manager) { for (AdvancementHolder advancement : manager.getAllAdvancements()) { this.registerListeners(advancement); } } private void checkForAutomaticTriggers(ServerAdvancementManager manager) { for (AdvancementHolder holder : manager.getAllAdvancements()) { Advancement advancement = holder.value(); if (!advancement.criteria().isEmpty()) continue; this.award(holder, ""); advancement.rewards().grant(this.player); } } private void load(ServerAdvancementManager manager) { if (Files.isRegularFile(this.playerSavePath, new LinkOption[0])) { try (BufferedReader reader = Files.newBufferedReader(this.playerSavePath, StandardCharsets.UTF_8);){ JsonElement json = StrictJsonParser.parse(reader); Data data = (Data)this.codec.parse((DynamicOps)JsonOps.INSTANCE, (Object)json).getOrThrow(JsonParseException::new); this.applyFrom(manager, data); } catch (JsonIOException | IOException e) { LOGGER.error("Couldn't access player advancements in {}", (Object)this.playerSavePath, (Object)e); } catch (JsonParseException e) { LOGGER.error("Couldn't parse player advancements in {}", (Object)this.playerSavePath, (Object)e); } } this.checkForAutomaticTriggers(manager); this.registerListeners(manager); } public void save() { JsonElement json = (JsonElement)this.codec.encodeStart((DynamicOps)JsonOps.INSTANCE, (Object)this.asData()).getOrThrow(); try { FileUtil.createDirectoriesSafe(this.playerSavePath.getParent()); try (BufferedWriter outputWriter = Files.newBufferedWriter(this.playerSavePath, StandardCharsets.UTF_8, new OpenOption[0]);){ GSON.toJson(json, GSON.newJsonWriter((Writer)outputWriter)); } } catch (JsonIOException | IOException e) { LOGGER.error("Couldn't save player advancements to {}", (Object)this.playerSavePath, (Object)e); } } private void applyFrom(ServerAdvancementManager manager, Data data) { data.forEach((id, progress) -> { AdvancementHolder advancement = manager.get((Identifier)id); if (advancement == null) { LOGGER.warn("Ignored advancement '{}' in progress file {} - it doesn't exist anymore?", id, (Object)this.playerSavePath); return; } this.startProgress(advancement, (AdvancementProgress)progress); this.progressChanged.add(advancement); this.markForVisibilityUpdate(advancement); }); } private Data asData() { LinkedHashMap map = new LinkedHashMap(); this.progress.forEach((advancement, progress) -> { if (progress.hasProgress()) { map.put(advancement.id(), (AdvancementProgress)progress); } }); return new Data(map); } public boolean award(AdvancementHolder holder, String criterion) { boolean result = false; AdvancementProgress progress = this.getOrStartProgress(holder); boolean wasDone = progress.isDone(); if (progress.grantProgress(criterion)) { this.unregisterListeners(holder); this.progressChanged.add(holder); result = true; if (!wasDone && progress.isDone()) { holder.value().rewards().grant(this.player); holder.value().display().ifPresent(display -> { if (display.shouldAnnounceChat() && this.player.level().getGameRules().get(GameRules.SHOW_ADVANCEMENT_MESSAGES).booleanValue()) { this.playerList.broadcastSystemMessage(display.getType().createAnnouncement(holder, this.player), false); } }); } } if (!wasDone && progress.isDone()) { this.markForVisibilityUpdate(holder); } return result; } public boolean revoke(AdvancementHolder advancement, String criterion) { boolean result = false; AdvancementProgress progress = this.getOrStartProgress(advancement); boolean wasDone = progress.isDone(); if (progress.revokeProgress(criterion)) { this.registerListeners(advancement); this.progressChanged.add(advancement); result = true; } if (wasDone && !progress.isDone()) { this.markForVisibilityUpdate(advancement); } return result; } private void markForVisibilityUpdate(AdvancementHolder advancement) { AdvancementNode node = this.tree.get(advancement); if (node != null) { this.rootsToUpdate.add(node.root()); } } private void registerListeners(AdvancementHolder holder) { AdvancementProgress advancementProgress = this.getOrStartProgress(holder); if (advancementProgress.isDone()) { return; } for (Map.Entry> entry : holder.value().criteria().entrySet()) { CriterionProgress criterionProgress = advancementProgress.getCriterion(entry.getKey()); if (criterionProgress == null || criterionProgress.isDone()) continue; this.registerListener(holder, entry.getKey(), entry.getValue()); } } private void registerListener(AdvancementHolder holder, String key, Criterion criterion) { criterion.trigger().addPlayerListener(this, new CriterionTrigger.Listener(criterion.triggerInstance(), holder, key)); } private void unregisterListeners(AdvancementHolder holder) { AdvancementProgress advancementProgress = this.getOrStartProgress(holder); for (Map.Entry> entry : holder.value().criteria().entrySet()) { CriterionProgress criterionProgress = advancementProgress.getCriterion(entry.getKey()); if (criterionProgress == null || !criterionProgress.isDone() && !advancementProgress.isDone()) continue; this.removeListener(holder, entry.getKey(), entry.getValue()); } } private void removeListener(AdvancementHolder holder, String key, Criterion criterion) { criterion.trigger().removePlayerListener(this, new CriterionTrigger.Listener(criterion.triggerInstance(), holder, key)); } public void flushDirty(ServerPlayer player, boolean showAdvancements) { if (this.isFirstPacket || !this.rootsToUpdate.isEmpty() || !this.progressChanged.isEmpty()) { HashMap progress = new HashMap(); HashSet added = new HashSet(); HashSet removed = new HashSet(); for (AdvancementNode root : this.rootsToUpdate) { this.updateTreeVisibility(root, added, removed); } this.rootsToUpdate.clear(); for (AdvancementHolder holder : this.progressChanged) { if (!this.visible.contains(holder)) continue; progress.put(holder.id(), this.progress.get(holder)); } this.progressChanged.clear(); if (!(progress.isEmpty() && added.isEmpty() && removed.isEmpty())) { player.connection.send(new ClientboundUpdateAdvancementsPacket(this.isFirstPacket, added, removed, progress, showAdvancements)); } } this.isFirstPacket = false; } public void setSelectedTab(@Nullable AdvancementHolder holder) { AdvancementHolder old = this.lastSelectedTab; this.lastSelectedTab = holder != null && holder.value().isRoot() && holder.value().display().isPresent() ? holder : null; if (old != this.lastSelectedTab) { this.player.connection.send(new ClientboundSelectAdvancementsTabPacket(this.lastSelectedTab == null ? null : this.lastSelectedTab.id())); } } public AdvancementProgress getOrStartProgress(AdvancementHolder advancement) { AdvancementProgress progress = this.progress.get(advancement); if (progress == null) { progress = new AdvancementProgress(); this.startProgress(advancement, progress); } return progress; } private void startProgress(AdvancementHolder holder, AdvancementProgress progress) { progress.update(holder.value().requirements()); this.progress.put(holder, progress); } private void updateTreeVisibility(AdvancementNode root, Set added, Set removed) { AdvancementVisibilityEvaluator.evaluateVisibility(root, node -> this.getOrStartProgress(node.holder()).isDone(), (node, shouldBeVisible) -> { AdvancementHolder advancement = node.holder(); if (shouldBeVisible) { if (this.visible.add(advancement)) { added.add(advancement); if (this.progress.containsKey(advancement)) { this.progressChanged.add(advancement); } } } else if (this.visible.remove(advancement)) { removed.add(advancement.id()); } }); } private record Data(Map map) { public static final Codec CODEC = Codec.unboundedMap(Identifier.CODEC, AdvancementProgress.CODEC).xmap(Data::new, Data::map); public void forEach(BiConsumer consumer) { this.map.entrySet().stream().sorted(Map.Entry.comparingByValue()).forEach((? super T entry) -> consumer.accept((Identifier)entry.getKey(), (AdvancementProgress)entry.getValue())); } } }