2025-11-24 22:52:51 +03:00

327 lines
14 KiB
Java

/*
* 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<AdvancementHolder, AdvancementProgress> progress = new LinkedHashMap<AdvancementHolder, AdvancementProgress>();
private final Set<AdvancementHolder> visible = new HashSet<AdvancementHolder>();
private final Set<AdvancementHolder> progressChanged = new HashSet<AdvancementHolder>();
private final Set<AdvancementNode> rootsToUpdate = new HashSet<AdvancementNode>();
private ServerPlayer player;
private @Nullable AdvancementHolder lastSelectedTab;
private boolean isFirstPacket = true;
private final Codec<Data> 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<Identifier, AdvancementProgress> map = new LinkedHashMap<Identifier, AdvancementProgress>();
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<String, Criterion<?>> 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 <T extends CriterionTriggerInstance> void registerListener(AdvancementHolder holder, String key, Criterion<T> criterion) {
criterion.trigger().addPlayerListener(this, new CriterionTrigger.Listener<T>(criterion.triggerInstance(), holder, key));
}
private void unregisterListeners(AdvancementHolder holder) {
AdvancementProgress advancementProgress = this.getOrStartProgress(holder);
for (Map.Entry<String, Criterion<?>> 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 <T extends CriterionTriggerInstance> void removeListener(AdvancementHolder holder, String key, Criterion<T> criterion) {
criterion.trigger().removePlayerListener(this, new CriterionTrigger.Listener<T>(criterion.triggerInstance(), holder, key));
}
public void flushDirty(ServerPlayer player, boolean showAdvancements) {
if (this.isFirstPacket || !this.rootsToUpdate.isEmpty() || !this.progressChanged.isEmpty()) {
HashMap<Identifier, AdvancementProgress> progress = new HashMap<Identifier, AdvancementProgress>();
HashSet<AdvancementHolder> added = new HashSet<AdvancementHolder>();
HashSet<Identifier> removed = new HashSet<Identifier>();
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<AdvancementHolder> added, Set<Identifier> 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<Identifier, AdvancementProgress> map) {
public static final Codec<Data> CODEC = Codec.unboundedMap(Identifier.CODEC, AdvancementProgress.CODEC).xmap(Data::new, Data::map);
public void forEach(BiConsumer<Identifier, AdvancementProgress> consumer) {
this.map.entrySet().stream().sorted(Map.Entry.comparingByValue()).forEach((? super T entry) -> consumer.accept((Identifier)entry.getKey(), (AdvancementProgress)entry.getValue()));
}
}
}