package net.minecraft.advancements; import com.google.common.collect.ImmutableList; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; import java.util.List; import java.util.Optional; import net.minecraft.commands.CacheableFunction; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerPlayer; import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.Recipe; import net.minecraft.world.level.storage.loot.LootParams; import net.minecraft.world.level.storage.loot.LootTable; import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets; import net.minecraft.world.level.storage.loot.parameters.LootContextParams; public record AdvancementRewards(int experience, List> loot, List>> recipes, Optional function) { public static final Codec CODEC = RecordCodecBuilder.create( instance -> instance.group( Codec.INT.optionalFieldOf("experience", 0).forGetter(AdvancementRewards::experience), LootTable.KEY_CODEC.listOf().optionalFieldOf("loot", List.of()).forGetter(AdvancementRewards::loot), Recipe.KEY_CODEC.listOf().optionalFieldOf("recipes", List.of()).forGetter(AdvancementRewards::recipes), CacheableFunction.CODEC.optionalFieldOf("function").forGetter(AdvancementRewards::function) ) .apply(instance, AdvancementRewards::new) ); public static final AdvancementRewards EMPTY = new AdvancementRewards(0, List.of(), List.of(), Optional.empty()); public void grant(ServerPlayer player) { player.giveExperiencePoints(this.experience); LootParams lootParams = new LootParams.Builder(player.serverLevel()) .withParameter(LootContextParams.THIS_ENTITY, player) .withParameter(LootContextParams.ORIGIN, player.position()) .create(LootContextParamSets.ADVANCEMENT_REWARD); boolean bl = false; for (ResourceKey resourceKey : this.loot) { for (ItemStack itemStack : player.server.reloadableRegistries().getLootTable(resourceKey).getRandomItems(lootParams)) { if (player.addItem(itemStack)) { player.level() .playSound( null, player.getX(), player.getY(), player.getZ(), SoundEvents.ITEM_PICKUP, SoundSource.PLAYERS, 0.2F, ((player.getRandom().nextFloat() - player.getRandom().nextFloat()) * 0.7F + 1.0F) * 2.0F ); bl = true; } else { ItemEntity itemEntity = player.drop(itemStack, false); if (itemEntity != null) { itemEntity.setNoPickUpDelay(); itemEntity.setTarget(player.getUUID()); } } } } if (bl) { player.containerMenu.broadcastChanges(); } if (!this.recipes.isEmpty()) { player.awardRecipesByKey(this.recipes); } MinecraftServer minecraftServer = player.server; this.function .flatMap(cacheableFunction -> cacheableFunction.get(minecraftServer.getFunctions())) .ifPresent( commandFunction -> minecraftServer.getFunctions().execute(commandFunction, player.createCommandSourceStack().withSuppressedOutput().withPermission(2)) ); } public static class Builder { private int experience; private final ImmutableList.Builder> loot = ImmutableList.builder(); private final ImmutableList.Builder>> recipes = ImmutableList.builder(); private Optional function = Optional.empty(); /** * Creates a new builder with the given amount of experience as a reward */ public static AdvancementRewards.Builder experience(int experience) { return new AdvancementRewards.Builder().addExperience(experience); } /** * Adds the given amount of experience. (Not a direct setter) */ public AdvancementRewards.Builder addExperience(int experience) { this.experience += experience; return this; } public static AdvancementRewards.Builder loot(ResourceKey lootTable) { return new AdvancementRewards.Builder().addLootTable(lootTable); } public AdvancementRewards.Builder addLootTable(ResourceKey lootTable) { this.loot.add(lootTable); return this; } public static AdvancementRewards.Builder recipe(ResourceKey> recipe) { return new AdvancementRewards.Builder().addRecipe(recipe); } public AdvancementRewards.Builder addRecipe(ResourceKey> recipe) { this.recipes.add(recipe); return this; } public static AdvancementRewards.Builder function(ResourceLocation functionId) { return new AdvancementRewards.Builder().runs(functionId); } public AdvancementRewards.Builder runs(ResourceLocation functionId) { this.function = Optional.of(functionId); return this; } public AdvancementRewards build() { return new AdvancementRewards(this.experience, this.loot.build(), this.recipes.build(), this.function.map(CacheableFunction::new)); } } }