218 lines
		
	
	
	
		
			8.6 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			218 lines
		
	
	
	
		
			8.6 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| package net.minecraft.advancements;
 | |
| 
 | |
| import com.google.common.collect.ImmutableMap;
 | |
| import com.mojang.serialization.Codec;
 | |
| import com.mojang.serialization.DataResult;
 | |
| import com.mojang.serialization.codecs.RecordCodecBuilder;
 | |
| import java.util.Map;
 | |
| import java.util.Optional;
 | |
| import java.util.function.Consumer;
 | |
| import net.minecraft.ChatFormatting;
 | |
| import net.minecraft.advancements.critereon.CriterionValidator;
 | |
| import net.minecraft.core.ClientAsset;
 | |
| import net.minecraft.core.HolderGetter;
 | |
| import net.minecraft.network.FriendlyByteBuf;
 | |
| import net.minecraft.network.RegistryFriendlyByteBuf;
 | |
| import net.minecraft.network.chat.Component;
 | |
| import net.minecraft.network.chat.ComponentUtils;
 | |
| import net.minecraft.network.chat.HoverEvent;
 | |
| import net.minecraft.network.chat.Style;
 | |
| import net.minecraft.network.codec.ByteBufCodecs;
 | |
| import net.minecraft.network.codec.StreamCodec;
 | |
| import net.minecraft.resources.ResourceLocation;
 | |
| import net.minecraft.util.ProblemReporter;
 | |
| import net.minecraft.world.item.ItemStack;
 | |
| import net.minecraft.world.level.ItemLike;
 | |
| import org.jetbrains.annotations.Nullable;
 | |
| 
 | |
| public record Advancement(
 | |
| 	Optional<ResourceLocation> parent,
 | |
| 	Optional<DisplayInfo> display,
 | |
| 	AdvancementRewards rewards,
 | |
| 	Map<String, Criterion<?>> criteria,
 | |
| 	AdvancementRequirements requirements,
 | |
| 	boolean sendsTelemetryEvent,
 | |
| 	Optional<Component> name
 | |
| ) {
 | |
| 	private static final Codec<Map<String, Criterion<?>>> CRITERIA_CODEC = Codec.unboundedMap(Codec.STRING, Criterion.CODEC)
 | |
| 		.validate(map -> map.isEmpty() ? DataResult.error(() -> "Advancement criteria cannot be empty") : DataResult.success(map));
 | |
| 	public static final Codec<Advancement> CODEC = RecordCodecBuilder.<Advancement>create(
 | |
| 			instance -> instance.group(
 | |
| 					ResourceLocation.CODEC.optionalFieldOf("parent").forGetter(Advancement::parent),
 | |
| 					DisplayInfo.CODEC.optionalFieldOf("display").forGetter(Advancement::display),
 | |
| 					AdvancementRewards.CODEC.optionalFieldOf("rewards", AdvancementRewards.EMPTY).forGetter(Advancement::rewards),
 | |
| 					CRITERIA_CODEC.fieldOf("criteria").forGetter(Advancement::criteria),
 | |
| 					AdvancementRequirements.CODEC.optionalFieldOf("requirements").forGetter(advancement -> Optional.of(advancement.requirements())),
 | |
| 					Codec.BOOL.optionalFieldOf("sends_telemetry_event", false).forGetter(Advancement::sendsTelemetryEvent)
 | |
| 				)
 | |
| 				.apply(instance, (optional, optional2, advancementRewards, map, optional3, boolean_) -> {
 | |
| 					AdvancementRequirements advancementRequirements = (AdvancementRequirements)optional3.orElseGet(() -> AdvancementRequirements.allOf(map.keySet()));
 | |
| 					return new Advancement(optional, optional2, advancementRewards, map, advancementRequirements, boolean_);
 | |
| 				})
 | |
| 		)
 | |
| 		.validate(Advancement::validate);
 | |
| 	public static final StreamCodec<RegistryFriendlyByteBuf, Advancement> STREAM_CODEC = StreamCodec.ofMember(Advancement::write, Advancement::read);
 | |
| 
 | |
| 	public Advancement(
 | |
| 		Optional<ResourceLocation> parent,
 | |
| 		Optional<DisplayInfo> display,
 | |
| 		AdvancementRewards rewards,
 | |
| 		Map<String, Criterion<?>> criteria,
 | |
| 		AdvancementRequirements requirements,
 | |
| 		boolean sendsTelemetryEvent
 | |
| 	) {
 | |
| 		this(parent, display, rewards, Map.copyOf(criteria), requirements, sendsTelemetryEvent, display.map(Advancement::decorateName));
 | |
| 	}
 | |
| 
 | |
| 	private static DataResult<Advancement> validate(Advancement advancement) {
 | |
| 		return advancement.requirements().validate(advancement.criteria().keySet()).map(advancementRequirements -> advancement);
 | |
| 	}
 | |
| 
 | |
| 	private static Component decorateName(DisplayInfo display) {
 | |
| 		Component component = display.getTitle();
 | |
| 		ChatFormatting chatFormatting = display.getType().getChatColor();
 | |
| 		Component component2 = ComponentUtils.mergeStyles(component.copy(), Style.EMPTY.withColor(chatFormatting)).append("\n").append(display.getDescription());
 | |
| 		Component component3 = component.copy().withStyle(style -> style.withHoverEvent(new HoverEvent.ShowText(component2)));
 | |
| 		return ComponentUtils.wrapInSquareBrackets(component3).withStyle(chatFormatting);
 | |
| 	}
 | |
| 
 | |
| 	public static Component name(AdvancementHolder advancement) {
 | |
| 		return (Component)advancement.value().name().orElseGet(() -> Component.literal(advancement.id().toString()));
 | |
| 	}
 | |
| 
 | |
| 	private void write(RegistryFriendlyByteBuf buffer) {
 | |
| 		buffer.writeOptional(this.parent, FriendlyByteBuf::writeResourceLocation);
 | |
| 		DisplayInfo.STREAM_CODEC.apply(ByteBufCodecs::optional).encode(buffer, this.display);
 | |
| 		this.requirements.write(buffer);
 | |
| 		buffer.writeBoolean(this.sendsTelemetryEvent);
 | |
| 	}
 | |
| 
 | |
| 	private static Advancement read(RegistryFriendlyByteBuf buffer) {
 | |
| 		return new Advancement(
 | |
| 			buffer.readOptional(FriendlyByteBuf::readResourceLocation),
 | |
| 			(Optional<DisplayInfo>)DisplayInfo.STREAM_CODEC.apply(ByteBufCodecs::optional).decode(buffer),
 | |
| 			AdvancementRewards.EMPTY,
 | |
| 			Map.of(),
 | |
| 			new AdvancementRequirements(buffer),
 | |
| 			buffer.readBoolean()
 | |
| 		);
 | |
| 	}
 | |
| 
 | |
| 	public boolean isRoot() {
 | |
| 		return this.parent.isEmpty();
 | |
| 	}
 | |
| 
 | |
| 	public void validate(ProblemReporter reporter, HolderGetter.Provider lootData) {
 | |
| 		this.criteria.forEach((string, criterion) -> {
 | |
| 			CriterionValidator criterionValidator = new CriterionValidator(reporter.forChild(new ProblemReporter.RootFieldPathElement(string)), lootData);
 | |
| 			criterion.triggerInstance().validate(criterionValidator);
 | |
| 		});
 | |
| 	}
 | |
| 
 | |
| 	public static class Builder {
 | |
| 		private Optional<ResourceLocation> parent = Optional.empty();
 | |
| 		private Optional<DisplayInfo> display = Optional.empty();
 | |
| 		private AdvancementRewards rewards = AdvancementRewards.EMPTY;
 | |
| 		private final ImmutableMap.Builder<String, Criterion<?>> criteria = ImmutableMap.builder();
 | |
| 		private Optional<AdvancementRequirements> requirements = Optional.empty();
 | |
| 		private AdvancementRequirements.Strategy requirementsStrategy = AdvancementRequirements.Strategy.AND;
 | |
| 		private boolean sendsTelemetryEvent;
 | |
| 
 | |
| 		public static Advancement.Builder advancement() {
 | |
| 			return new Advancement.Builder().sendsTelemetryEvent();
 | |
| 		}
 | |
| 
 | |
| 		public static Advancement.Builder recipeAdvancement() {
 | |
| 			return new Advancement.Builder();
 | |
| 		}
 | |
| 
 | |
| 		public Advancement.Builder parent(AdvancementHolder parent) {
 | |
| 			this.parent = Optional.of(parent.id());
 | |
| 			return this;
 | |
| 		}
 | |
| 
 | |
| 		@Deprecated(
 | |
| 			forRemoval = true
 | |
| 		)
 | |
| 		public Advancement.Builder parent(ResourceLocation parentId) {
 | |
| 			this.parent = Optional.of(parentId);
 | |
| 			return this;
 | |
| 		}
 | |
| 
 | |
| 		public Advancement.Builder display(
 | |
| 			ItemStack icon,
 | |
| 			Component title,
 | |
| 			Component description,
 | |
| 			@Nullable ResourceLocation background,
 | |
| 			AdvancementType type,
 | |
| 			boolean showToast,
 | |
| 			boolean announceChat,
 | |
| 			boolean hidden
 | |
| 		) {
 | |
| 			return this.display(new DisplayInfo(icon, title, description, Optional.ofNullable(background).map(ClientAsset::new), type, showToast, announceChat, hidden));
 | |
| 		}
 | |
| 
 | |
| 		public Advancement.Builder display(
 | |
| 			ItemLike icon,
 | |
| 			Component title,
 | |
| 			Component description,
 | |
| 			@Nullable ResourceLocation background,
 | |
| 			AdvancementType type,
 | |
| 			boolean showToast,
 | |
| 			boolean announceChat,
 | |
| 			boolean hidden
 | |
| 		) {
 | |
| 			return this.display(
 | |
| 				new DisplayInfo(
 | |
| 					new ItemStack(icon.asItem()), title, description, Optional.ofNullable(background).map(ClientAsset::new), type, showToast, announceChat, hidden
 | |
| 				)
 | |
| 			);
 | |
| 		}
 | |
| 
 | |
| 		public Advancement.Builder display(DisplayInfo display) {
 | |
| 			this.display = Optional.of(display);
 | |
| 			return this;
 | |
| 		}
 | |
| 
 | |
| 		public Advancement.Builder rewards(AdvancementRewards.Builder rewardsBuilder) {
 | |
| 			return this.rewards(rewardsBuilder.build());
 | |
| 		}
 | |
| 
 | |
| 		public Advancement.Builder rewards(AdvancementRewards rewards) {
 | |
| 			this.rewards = rewards;
 | |
| 			return this;
 | |
| 		}
 | |
| 
 | |
| 		public Advancement.Builder addCriterion(String key, Criterion<?> criterion) {
 | |
| 			this.criteria.put(key, criterion);
 | |
| 			return this;
 | |
| 		}
 | |
| 
 | |
| 		public Advancement.Builder requirements(AdvancementRequirements.Strategy requirementsStrategy) {
 | |
| 			this.requirementsStrategy = requirementsStrategy;
 | |
| 			return this;
 | |
| 		}
 | |
| 
 | |
| 		public Advancement.Builder requirements(AdvancementRequirements requirements) {
 | |
| 			this.requirements = Optional.of(requirements);
 | |
| 			return this;
 | |
| 		}
 | |
| 
 | |
| 		public Advancement.Builder sendsTelemetryEvent() {
 | |
| 			this.sendsTelemetryEvent = true;
 | |
| 			return this;
 | |
| 		}
 | |
| 
 | |
| 		public AdvancementHolder build(ResourceLocation id) {
 | |
| 			Map<String, Criterion<?>> map = this.criteria.buildOrThrow();
 | |
| 			AdvancementRequirements advancementRequirements = (AdvancementRequirements)this.requirements.orElseGet(() -> this.requirementsStrategy.create(map.keySet()));
 | |
| 			return new AdvancementHolder(id, new Advancement(this.parent, this.display, this.rewards, map, advancementRequirements, this.sendsTelemetryEvent));
 | |
| 		}
 | |
| 
 | |
| 		public AdvancementHolder save(Consumer<AdvancementHolder> output, String id) {
 | |
| 			AdvancementHolder advancementHolder = this.build(ResourceLocation.parse(id));
 | |
| 			output.accept(advancementHolder);
 | |
| 			return advancementHolder;
 | |
| 		}
 | |
| 	}
 | |
| }
 |