package net.minecraft.advancements; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; import java.time.Instant; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.Map.Entry; import java.util.stream.Collectors; import net.minecraft.Util; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.chat.Component; import net.minecraft.util.ExtraCodecs; import org.jetbrains.annotations.Nullable; public class AdvancementProgress implements Comparable { private static final DateTimeFormatter OBTAINED_TIME_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss Z", Locale.ROOT); private static final Codec OBTAINED_TIME_CODEC = ExtraCodecs.temporalCodec(OBTAINED_TIME_FORMAT) .xmap(Instant::from, instant -> instant.atZone(ZoneId.systemDefault())); private static final Codec> CRITERIA_CODEC = Codec.unboundedMap(Codec.STRING, OBTAINED_TIME_CODEC) .xmap( map -> Util.mapValues(map, CriterionProgress::new), map -> (Map)map.entrySet() .stream() .filter(entry -> ((CriterionProgress)entry.getValue()).isDone()) .collect(Collectors.toMap(Entry::getKey, entry -> (Instant)Objects.requireNonNull(((CriterionProgress)entry.getValue()).getObtained()))) ); public static final Codec CODEC = RecordCodecBuilder.create( instance -> instance.group( CRITERIA_CODEC.optionalFieldOf("criteria", Map.of()).forGetter(advancementProgress -> advancementProgress.criteria), Codec.BOOL.fieldOf("done").orElse(true).forGetter(AdvancementProgress::isDone) ) .apply(instance, (map, boolean_) -> new AdvancementProgress(new HashMap(map))) ); private final Map criteria; private AdvancementRequirements requirements = AdvancementRequirements.EMPTY; private AdvancementProgress(Map criteria) { this.criteria = criteria; } public AdvancementProgress() { this.criteria = Maps.newHashMap(); } public void update(AdvancementRequirements requirements) { Set set = requirements.names(); this.criteria.entrySet().removeIf(entry -> !set.contains(entry.getKey())); for (String string : set) { this.criteria.putIfAbsent(string, new CriterionProgress()); } this.requirements = requirements; } public boolean isDone() { return this.requirements.test(this::isCriterionDone); } public boolean hasProgress() { for (CriterionProgress criterionProgress : this.criteria.values()) { if (criterionProgress.isDone()) { return true; } } return false; } public boolean grantProgress(String criterionName) { CriterionProgress criterionProgress = (CriterionProgress)this.criteria.get(criterionName); if (criterionProgress != null && !criterionProgress.isDone()) { criterionProgress.grant(); return true; } else { return false; } } public boolean revokeProgress(String criterionName) { CriterionProgress criterionProgress = (CriterionProgress)this.criteria.get(criterionName); if (criterionProgress != null && criterionProgress.isDone()) { criterionProgress.revoke(); return true; } else { return false; } } public String toString() { return "AdvancementProgress{criteria=" + this.criteria + ", requirements=" + this.requirements + "}"; } public void serializeToNetwork(FriendlyByteBuf buffer) { buffer.writeMap(this.criteria, FriendlyByteBuf::writeUtf, (friendlyByteBuf, criterionProgress) -> criterionProgress.serializeToNetwork(friendlyByteBuf)); } public static AdvancementProgress fromNetwork(FriendlyByteBuf buffer) { Map map = buffer.readMap(FriendlyByteBuf::readUtf, CriterionProgress::fromNetwork); return new AdvancementProgress(map); } @Nullable public CriterionProgress getCriterion(String criterionName) { return (CriterionProgress)this.criteria.get(criterionName); } private boolean isCriterionDone(String criterionName) { CriterionProgress criterionProgress = this.getCriterion(criterionName); return criterionProgress != null && criterionProgress.isDone(); } public float getPercent() { if (this.criteria.isEmpty()) { return 0.0F; } else { float f = this.requirements.size(); float g = this.countCompletedRequirements(); return g / f; } } @Nullable public Component getProgressText() { if (this.criteria.isEmpty()) { return null; } else { int i = this.requirements.size(); if (i <= 1) { return null; } else { int j = this.countCompletedRequirements(); return Component.translatable("advancements.progress", j, i); } } } private int countCompletedRequirements() { return this.requirements.count(this::isCriterionDone); } public Iterable getRemainingCriteria() { List list = Lists.newArrayList(); for (Entry entry : this.criteria.entrySet()) { if (!((CriterionProgress)entry.getValue()).isDone()) { list.add((String)entry.getKey()); } } return list; } public Iterable getCompletedCriteria() { List list = Lists.newArrayList(); for (Entry entry : this.criteria.entrySet()) { if (((CriterionProgress)entry.getValue()).isDone()) { list.add((String)entry.getKey()); } } return list; } @Nullable public Instant getFirstProgressDate() { return (Instant)this.criteria.values().stream().map(CriterionProgress::getObtained).filter(Objects::nonNull).min(Comparator.naturalOrder()).orElse(null); } public int compareTo(AdvancementProgress other) { Instant instant = this.getFirstProgressDate(); Instant instant2 = other.getFirstProgressDate(); if (instant == null && instant2 != null) { return 1; } else if (instant != null && instant2 == null) { return -1; } else { return instant == null && instant2 == null ? 0 : instant.compareTo(instant2); } } }