388 lines
16 KiB
Java
388 lines
16 KiB
Java
package net.minecraft.server;
|
|
|
|
import java.lang.reflect.Type;
|
|
import com.google.gson.GsonBuilder;
|
|
import org.apache.logging.log4j.LogManager;
|
|
import net.minecraft.network.protocol.game.ClientboundSelectAdvancementsTabPacket;
|
|
import net.minecraft.network.protocol.Packet;
|
|
import java.util.Collection;
|
|
import net.minecraft.network.protocol.game.ClientboundUpdateAdvancementsPacket;
|
|
import net.minecraft.advancements.CriterionProgress;
|
|
import net.minecraft.advancements.CriterionTriggerInstance;
|
|
import net.minecraft.advancements.Criterion;
|
|
import net.minecraft.network.chat.Component;
|
|
import net.minecraft.network.chat.TranslatableComponent;
|
|
import net.minecraft.world.level.GameRules;
|
|
import java.io.Writer;
|
|
import java.io.OutputStream;
|
|
import java.io.OutputStreamWriter;
|
|
import com.google.common.base.Charsets;
|
|
import java.io.FileOutputStream;
|
|
import java.util.stream.Stream;
|
|
import java.io.IOException;
|
|
import java.util.stream.Collector;
|
|
import java.util.stream.Collectors;
|
|
import java.util.function.Function;
|
|
import java.util.Comparator;
|
|
import com.google.gson.JsonParseException;
|
|
import com.google.gson.JsonElement;
|
|
import net.minecraft.SharedConstants;
|
|
import net.minecraft.util.datafix.DataFixTypes;
|
|
import com.mojang.datafixers.types.DynamicOps;
|
|
import com.mojang.datafixers.Dynamic;
|
|
import com.google.gson.internal.Streams;
|
|
import com.mojang.datafixers.types.JsonOps;
|
|
import java.io.Reader;
|
|
import com.google.gson.stream.JsonReader;
|
|
import java.io.StringReader;
|
|
import com.google.common.io.Files;
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.util.List;
|
|
import com.google.common.collect.Lists;
|
|
import java.util.Iterator;
|
|
import net.minecraft.advancements.CriterionTrigger;
|
|
import net.minecraft.advancements.CriteriaTriggers;
|
|
import com.google.common.collect.Sets;
|
|
import com.google.common.collect.Maps;
|
|
import javax.annotation.Nullable;
|
|
import net.minecraft.server.level.ServerPlayer;
|
|
import java.util.Set;
|
|
import net.minecraft.advancements.Advancement;
|
|
import java.io.File;
|
|
import net.minecraft.advancements.AdvancementProgress;
|
|
import net.minecraft.resources.ResourceLocation;
|
|
import java.util.Map;
|
|
import com.google.gson.reflect.TypeToken;
|
|
import com.google.gson.Gson;
|
|
import org.apache.logging.log4j.Logger;
|
|
|
|
public class PlayerAdvancements {
|
|
private static final Logger LOGGER;
|
|
private static final Gson GSON;
|
|
private static final TypeToken<Map<ResourceLocation, AdvancementProgress>> TYPE_TOKEN;
|
|
private final MinecraftServer server;
|
|
private final File file;
|
|
private final Map<Advancement, AdvancementProgress> advancements;
|
|
private final Set<Advancement> visible;
|
|
private final Set<Advancement> visibilityChanged;
|
|
private final Set<Advancement> progressChanged;
|
|
private ServerPlayer player;
|
|
@Nullable
|
|
private Advancement lastSelectedTab;
|
|
private boolean isFirstPacket;
|
|
|
|
public PlayerAdvancements(final MinecraftServer minecraftServer, final File file, final ServerPlayer xe) {
|
|
this.advancements = Maps.newLinkedHashMap();
|
|
this.visible = Sets.newLinkedHashSet();
|
|
this.visibilityChanged = Sets.newLinkedHashSet();
|
|
this.progressChanged = Sets.newLinkedHashSet();
|
|
this.isFirstPacket = true;
|
|
this.server = minecraftServer;
|
|
this.file = file;
|
|
this.player = xe;
|
|
this.load();
|
|
}
|
|
|
|
public void setPlayer(final ServerPlayer xe) {
|
|
this.player = xe;
|
|
}
|
|
|
|
public void stopListening() {
|
|
for (final CriterionTrigger<?> ab3 : CriteriaTriggers.all()) {
|
|
ab3.removePlayerListeners(this);
|
|
}
|
|
}
|
|
|
|
public void reload() {
|
|
this.stopListening();
|
|
this.advancements.clear();
|
|
this.visible.clear();
|
|
this.visibilityChanged.clear();
|
|
this.progressChanged.clear();
|
|
this.isFirstPacket = true;
|
|
this.lastSelectedTab = null;
|
|
this.load();
|
|
}
|
|
|
|
private void registerListeners() {
|
|
for (final Advancement u3 : this.server.getAdvancements().getAllAdvancements()) {
|
|
this.registerListeners(u3);
|
|
}
|
|
}
|
|
|
|
private void ensureAllVisible() {
|
|
final List<Advancement> list2 = Lists.newArrayList();
|
|
for (final Map.Entry<Advancement, AdvancementProgress> entry4 : this.advancements.entrySet()) {
|
|
if (entry4.getValue().isDone()) {
|
|
list2.add(entry4.getKey());
|
|
this.progressChanged.add(entry4.getKey());
|
|
}
|
|
}
|
|
for (final Advancement u4 : list2) {
|
|
this.ensureVisibility(u4);
|
|
}
|
|
}
|
|
|
|
private void checkForAutomaticTriggers() {
|
|
for (final Advancement u3 : this.server.getAdvancements().getAllAdvancements()) {
|
|
if (u3.getCriteria().isEmpty()) {
|
|
this.award(u3, "");
|
|
u3.getRewards().grant(this.player);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void load() {
|
|
if (this.file.isFile()) {
|
|
try (final JsonReader jsonReader2 = new JsonReader(new StringReader(Files.toString(this.file, StandardCharsets.UTF_8)))) {
|
|
jsonReader2.setLenient(false);
|
|
Dynamic<JsonElement> dynamic4 = (Dynamic<JsonElement>)new Dynamic((DynamicOps)JsonOps.INSTANCE, Streams.parse(jsonReader2));
|
|
if (!dynamic4.get("DataVersion").asNumber().isPresent()) {
|
|
dynamic4 = (Dynamic<JsonElement>)dynamic4.set("DataVersion", dynamic4.createInt(1343));
|
|
}
|
|
dynamic4 = (Dynamic<JsonElement>)this.server.getFixerUpper().update(DataFixTypes.ADVANCEMENTS.getType(), (Dynamic)dynamic4, dynamic4.get("DataVersion").asInt(0), SharedConstants.getCurrentVersion().getWorldVersion());
|
|
dynamic4 = (Dynamic<JsonElement>)dynamic4.remove("DataVersion");
|
|
final Map<ResourceLocation, AdvancementProgress> map5 = PlayerAdvancements.GSON.<Map<ResourceLocation, AdvancementProgress>>getAdapter(PlayerAdvancements.TYPE_TOKEN).fromJsonTree((JsonElement)dynamic4.getValue());
|
|
if (map5 == null) {
|
|
throw new JsonParseException("Found null for advancements");
|
|
}
|
|
final Stream<Map.Entry<ResourceLocation, AdvancementProgress>> stream6 = map5.entrySet().stream().sorted(Comparator.comparing(Map.Entry::getValue));
|
|
for (final Map.Entry<ResourceLocation, AdvancementProgress> entry8 : stream6.collect(Collectors.<Map.Entry<ResourceLocation, AdvancementProgress>>toList())) {
|
|
final Advancement u9 = this.server.getAdvancements().getAdvancement(entry8.getKey());
|
|
if (u9 == null) {
|
|
PlayerAdvancements.LOGGER.warn("Ignored advancement '{}' in progress file {} - it doesn't exist anymore?", entry8.getKey(), this.file);
|
|
}
|
|
else {
|
|
this.startProgress(u9, entry8.getValue());
|
|
}
|
|
}
|
|
}
|
|
catch (JsonParseException jsonParseException2) {
|
|
PlayerAdvancements.LOGGER.error("Couldn't parse player advancements in {}", this.file, jsonParseException2);
|
|
}
|
|
catch (IOException iOException2) {
|
|
PlayerAdvancements.LOGGER.error("Couldn't access player advancements in {}", this.file, iOException2);
|
|
}
|
|
}
|
|
this.checkForAutomaticTriggers();
|
|
this.ensureAllVisible();
|
|
this.registerListeners();
|
|
}
|
|
|
|
public void save() {
|
|
final Map<ResourceLocation, AdvancementProgress> map2 = Maps.newHashMap();
|
|
for (final Map.Entry<Advancement, AdvancementProgress> entry4 : this.advancements.entrySet()) {
|
|
final AdvancementProgress w5 = entry4.getValue();
|
|
if (w5.hasProgress()) {
|
|
map2.put(entry4.getKey().getId(), w5);
|
|
}
|
|
}
|
|
if (this.file.getParentFile() != null) {
|
|
this.file.getParentFile().mkdirs();
|
|
}
|
|
final JsonElement jsonElement3 = PlayerAdvancements.GSON.toJsonTree(map2);
|
|
jsonElement3.getAsJsonObject().addProperty("DataVersion", SharedConstants.getCurrentVersion().getWorldVersion());
|
|
try (final OutputStream outputStream4 = new FileOutputStream(this.file);
|
|
final Writer writer6 = new OutputStreamWriter(outputStream4, Charsets.UTF_8.newEncoder())) {
|
|
PlayerAdvancements.GSON.toJson(jsonElement3, writer6);
|
|
}
|
|
catch (IOException iOException4) {
|
|
PlayerAdvancements.LOGGER.error("Couldn't save player advancements to {}", this.file, iOException4);
|
|
}
|
|
}
|
|
|
|
public boolean award(final Advancement u, final String string) {
|
|
boolean boolean4 = false;
|
|
final AdvancementProgress w5 = this.getOrStartProgress(u);
|
|
final boolean boolean5 = w5.isDone();
|
|
if (w5.grantProgress(string)) {
|
|
this.unregisterListeners(u);
|
|
this.progressChanged.add(u);
|
|
boolean4 = true;
|
|
if (!boolean5 && w5.isDone()) {
|
|
u.getRewards().grant(this.player);
|
|
if (u.getDisplay() != null && u.getDisplay().shouldAnnounceChat() && this.player.level.getGameRules().getBoolean(GameRules.RULE_ANNOUNCE_ADVANCEMENTS)) {
|
|
this.server.getPlayerList().broadcastMessage(new TranslatableComponent("chat.type.advancement." + u.getDisplay().getFrame().getName(), new Object[] { this.player.getDisplayName(), u.getChatComponent() }));
|
|
}
|
|
}
|
|
}
|
|
if (w5.isDone()) {
|
|
this.ensureVisibility(u);
|
|
}
|
|
return boolean4;
|
|
}
|
|
|
|
public boolean revoke(final Advancement u, final String string) {
|
|
boolean boolean4 = false;
|
|
final AdvancementProgress w5 = this.getOrStartProgress(u);
|
|
if (w5.revokeProgress(string)) {
|
|
this.registerListeners(u);
|
|
this.progressChanged.add(u);
|
|
boolean4 = true;
|
|
}
|
|
if (!w5.hasProgress()) {
|
|
this.ensureVisibility(u);
|
|
}
|
|
return boolean4;
|
|
}
|
|
|
|
private void registerListeners(final Advancement u) {
|
|
final AdvancementProgress w3 = this.getOrStartProgress(u);
|
|
if (w3.isDone()) {
|
|
return;
|
|
}
|
|
for (final Map.Entry<String, Criterion> entry5 : u.getCriteria().entrySet()) {
|
|
final CriterionProgress aa6 = w3.getCriterion(entry5.getKey());
|
|
if (aa6 != null) {
|
|
if (aa6.isDone()) {
|
|
continue;
|
|
}
|
|
final CriterionTriggerInstance ac7 = entry5.getValue().getTrigger();
|
|
if (ac7 == null) {
|
|
continue;
|
|
}
|
|
final CriterionTrigger<CriterionTriggerInstance> ab8 = CriteriaTriggers.<CriterionTriggerInstance>getCriterion(ac7.getCriterion());
|
|
if (ab8 == null) {
|
|
continue;
|
|
}
|
|
ab8.addPlayerListener(this, new CriterionTrigger.Listener<CriterionTriggerInstance>(ac7, u, entry5.getKey()));
|
|
}
|
|
}
|
|
}
|
|
|
|
private void unregisterListeners(final Advancement u) {
|
|
final AdvancementProgress w3 = this.getOrStartProgress(u);
|
|
for (final Map.Entry<String, Criterion> entry5 : u.getCriteria().entrySet()) {
|
|
final CriterionProgress aa6 = w3.getCriterion(entry5.getKey());
|
|
if (aa6 != null) {
|
|
if (!aa6.isDone() && !w3.isDone()) {
|
|
continue;
|
|
}
|
|
final CriterionTriggerInstance ac7 = entry5.getValue().getTrigger();
|
|
if (ac7 == null) {
|
|
continue;
|
|
}
|
|
final CriterionTrigger<CriterionTriggerInstance> ab8 = CriteriaTriggers.<CriterionTriggerInstance>getCriterion(ac7.getCriterion());
|
|
if (ab8 == null) {
|
|
continue;
|
|
}
|
|
ab8.removePlayerListener(this, new CriterionTrigger.Listener<CriterionTriggerInstance>(ac7, u, entry5.getKey()));
|
|
}
|
|
}
|
|
}
|
|
|
|
public void flushDirty(final ServerPlayer xe) {
|
|
if (this.isFirstPacket || !this.visibilityChanged.isEmpty() || !this.progressChanged.isEmpty()) {
|
|
final Map<ResourceLocation, AdvancementProgress> map3 = Maps.newHashMap();
|
|
final Set<Advancement> set4 = Sets.newLinkedHashSet();
|
|
final Set<ResourceLocation> set5 = Sets.newLinkedHashSet();
|
|
for (final Advancement u7 : this.progressChanged) {
|
|
if (this.visible.contains(u7)) {
|
|
map3.put(u7.getId(), this.advancements.get(u7));
|
|
}
|
|
}
|
|
for (final Advancement u7 : this.visibilityChanged) {
|
|
if (this.visible.contains(u7)) {
|
|
set4.add(u7);
|
|
}
|
|
else {
|
|
set5.add(u7.getId());
|
|
}
|
|
}
|
|
if (this.isFirstPacket || !map3.isEmpty() || !set4.isEmpty() || !set5.isEmpty()) {
|
|
xe.connection.send(new ClientboundUpdateAdvancementsPacket(this.isFirstPacket, set4, set5, map3));
|
|
this.visibilityChanged.clear();
|
|
this.progressChanged.clear();
|
|
}
|
|
}
|
|
this.isFirstPacket = false;
|
|
}
|
|
|
|
public void setSelectedTab(@Nullable final Advancement u) {
|
|
final Advancement u2 = this.lastSelectedTab;
|
|
if (u != null && u.getParent() == null && u.getDisplay() != null) {
|
|
this.lastSelectedTab = u;
|
|
}
|
|
else {
|
|
this.lastSelectedTab = null;
|
|
}
|
|
if (u2 != this.lastSelectedTab) {
|
|
this.player.connection.send(new ClientboundSelectAdvancementsTabPacket((this.lastSelectedTab == null) ? null : this.lastSelectedTab.getId()));
|
|
}
|
|
}
|
|
|
|
public AdvancementProgress getOrStartProgress(final Advancement u) {
|
|
AdvancementProgress w3 = this.advancements.get(u);
|
|
if (w3 == null) {
|
|
w3 = new AdvancementProgress();
|
|
this.startProgress(u, w3);
|
|
}
|
|
return w3;
|
|
}
|
|
|
|
private void startProgress(final Advancement u, final AdvancementProgress w) {
|
|
w.update(u.getCriteria(), u.getRequirements());
|
|
this.advancements.put(u, w);
|
|
}
|
|
|
|
private void ensureVisibility(final Advancement u) {
|
|
final boolean boolean3 = this.shouldBeVisible(u);
|
|
final boolean boolean4 = this.visible.contains(u);
|
|
if (boolean3 && !boolean4) {
|
|
this.visible.add(u);
|
|
this.visibilityChanged.add(u);
|
|
if (this.advancements.containsKey(u)) {
|
|
this.progressChanged.add(u);
|
|
}
|
|
}
|
|
else if (!boolean3 && boolean4) {
|
|
this.visible.remove(u);
|
|
this.visibilityChanged.add(u);
|
|
}
|
|
if (boolean3 != boolean4 && u.getParent() != null) {
|
|
this.ensureVisibility(u.getParent());
|
|
}
|
|
for (final Advancement u2 : u.getChildren()) {
|
|
this.ensureVisibility(u2);
|
|
}
|
|
}
|
|
|
|
private boolean shouldBeVisible(Advancement u) {
|
|
for (int integer3 = 0; u != null && integer3 <= 2; u = u.getParent(), ++integer3) {
|
|
if (integer3 == 0 && this.hasCompletedChildrenOrSelf(u)) {
|
|
return true;
|
|
}
|
|
if (u.getDisplay() == null) {
|
|
return false;
|
|
}
|
|
final AdvancementProgress w4 = this.getOrStartProgress(u);
|
|
if (w4.isDone()) {
|
|
return true;
|
|
}
|
|
if (u.getDisplay().isHidden()) {
|
|
return false;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private boolean hasCompletedChildrenOrSelf(final Advancement u) {
|
|
final AdvancementProgress w3 = this.getOrStartProgress(u);
|
|
if (w3.isDone()) {
|
|
return true;
|
|
}
|
|
for (final Advancement u2 : u.getChildren()) {
|
|
if (this.hasCompletedChildrenOrSelf(u2)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static {
|
|
LOGGER = LogManager.getLogger();
|
|
GSON = new GsonBuilder().registerTypeAdapter(AdvancementProgress.class, new AdvancementProgress.Serializer()).registerTypeAdapter(ResourceLocation.class, new ResourceLocation.Serializer()).setPrettyPrinting().create();
|
|
TYPE_TOKEN = new TypeToken<Map<ResourceLocation, AdvancementProgress>>() {};
|
|
}
|
|
}
|