180 lines
6.4 KiB
Java
180 lines
6.4 KiB
Java
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
|
//
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package dan200.computercraft.shared.recipe.function;
|
|
|
|
import com.mojang.serialization.MapCodec;
|
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
|
import dan200.computercraft.shared.ModRegistry;
|
|
import net.minecraft.core.component.DataComponentPatch;
|
|
import net.minecraft.core.component.DataComponentType;
|
|
import net.minecraft.network.RegistryFriendlyByteBuf;
|
|
import net.minecraft.network.codec.ByteBufCodecs;
|
|
import net.minecraft.network.codec.StreamCodec;
|
|
import net.minecraft.world.inventory.CraftingContainer;
|
|
import net.minecraft.world.item.ItemStack;
|
|
import net.minecraft.world.item.crafting.Ingredient;
|
|
import net.minecraft.world.level.ItemLike;
|
|
import net.minecraft.world.level.storage.loot.functions.CopyComponentsFunction;
|
|
|
|
import javax.annotation.Nullable;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.Optional;
|
|
import java.util.Set;
|
|
|
|
/**
|
|
* A {@link RecipeFunction} that copies components from one of the ingredients to the result.
|
|
* <p>
|
|
* This has the same behaviour as {@linkplain CopyComponentsFunction}, but operating within the scope of recipes.
|
|
*/
|
|
public final class CopyComponents implements RecipeFunction {
|
|
public static final MapCodec<CopyComponents> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
|
|
Ingredient.CODEC_NONEMPTY.fieldOf("from").forGetter(x -> x.from),
|
|
DataComponentType.CODEC.listOf().optionalFieldOf("include").forGetter(x -> x.include),
|
|
DataComponentType.CODEC.listOf().optionalFieldOf("exclude").forGetter(x -> x.exclude)
|
|
).apply(instance, CopyComponents::new));
|
|
|
|
public static final StreamCodec<RegistryFriendlyByteBuf, CopyComponents> STREAM_CODEC = StreamCodec.composite(
|
|
Ingredient.CONTENTS_STREAM_CODEC, x -> x.from,
|
|
ByteBufCodecs.optional(DataComponentType.STREAM_CODEC.apply(ByteBufCodecs.list())), x -> x.include,
|
|
ByteBufCodecs.optional(DataComponentType.STREAM_CODEC.apply(ByteBufCodecs.list())), x -> x.exclude,
|
|
CopyComponents::new
|
|
);
|
|
|
|
private final Ingredient from;
|
|
private final Optional<List<DataComponentType<?>>> include;
|
|
private final Optional<List<DataComponentType<?>>> exclude;
|
|
|
|
private final @Nullable Set<DataComponentType<?>> includeSet;
|
|
private final @Nullable Set<DataComponentType<?>> excludeSet;
|
|
|
|
/**
|
|
* Create a new {@link CopyComponents} that copies all components from an ingredient.
|
|
*
|
|
* @param from The ingredient to copy from.
|
|
*/
|
|
public CopyComponents(Ingredient from) {
|
|
this(from, Optional.empty(), Optional.empty());
|
|
}
|
|
|
|
/**
|
|
* Create a new {@link CopyComponents} that copies all components from an ingredient.
|
|
*
|
|
* @param from The ingredient to copy from.
|
|
*/
|
|
public CopyComponents(ItemLike from) {
|
|
this(Ingredient.of(from));
|
|
}
|
|
|
|
private CopyComponents(Ingredient from, Optional<List<DataComponentType<?>>> include, Optional<List<DataComponentType<?>>> exclude) {
|
|
this.from = from;
|
|
this.include = include.map(List::copyOf);
|
|
this.exclude = exclude.map(List::copyOf);
|
|
|
|
includeSet = include.map(Set::copyOf).orElse(null);
|
|
excludeSet = exclude.map(Set::copyOf).orElse(null);
|
|
}
|
|
|
|
@Override
|
|
public Type<?> getType() {
|
|
return ModRegistry.RecipeFunctions.COPY_COMPONENTS.get();
|
|
}
|
|
|
|
@Override
|
|
public ItemStack apply(CraftingContainer container, ItemStack result) {
|
|
for (var item : container.getItems()) {
|
|
if (from.test(item)) {
|
|
applyPatch(item.getComponentsPatch(), result);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private void applyPatch(DataComponentPatch patch, ItemStack result) {
|
|
if (includeSet == null && excludeSet == null) {
|
|
result.applyComponents(patch);
|
|
return;
|
|
}
|
|
|
|
// Only apply components in the include set (if present) and not in the exclude set (if present).
|
|
for (var component : patch.entrySet()) {
|
|
var type = component.getKey();
|
|
if ((includeSet == null || includeSet.contains(type)) && (excludeSet == null || !excludeSet.contains(type))) {
|
|
unsafeSetComponent(result, type, component.getValue().orElse(null));
|
|
}
|
|
}
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
private static <T> void unsafeSetComponent(ItemStack stack, DataComponentType<?> type, @Nullable T value) {
|
|
stack.set((DataComponentType<T>) type, value);
|
|
}
|
|
|
|
/**
|
|
* Create a new {@link CopyComponents} builder, that copies components from an ingredient.
|
|
*
|
|
* @param ingredient The ingredient to copy from.
|
|
* @return The builder.
|
|
*/
|
|
public static Builder builder(Ingredient ingredient) {
|
|
return new Builder(ingredient);
|
|
}
|
|
|
|
/**
|
|
* Create a new {@link CopyComponents} builder, that copies components from an ingredient.
|
|
*
|
|
* @param ingredient The ingredient to copy from.
|
|
* @return The builder.
|
|
*/
|
|
public static Builder builder(ItemLike ingredient) {
|
|
return new Builder(Ingredient.of(ingredient));
|
|
}
|
|
|
|
public static final class Builder {
|
|
private final Ingredient from;
|
|
private @Nullable List<DataComponentType<?>> include;
|
|
private @Nullable List<DataComponentType<?>> exclude;
|
|
|
|
private Builder(Ingredient from) {
|
|
this.from = from;
|
|
}
|
|
|
|
/**
|
|
* Only copy the specified component.
|
|
*
|
|
* @param type The component to include.
|
|
* @return {@code this}, for chaining.
|
|
*/
|
|
public Builder include(DataComponentType<?> type) {
|
|
if (this.include == null) include = new ArrayList<>();
|
|
include.add(type);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Exclude a component from being copied.
|
|
*
|
|
* @param type The component to exclude.
|
|
* @return {@code this}, for chaining.
|
|
*/
|
|
public Builder exclude(DataComponentType<?> type) {
|
|
if (exclude == null) exclude = new ArrayList<>();
|
|
exclude.add(type);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Build the resulting {@link CopyComponents} instance.
|
|
*
|
|
* @return The constructed {@link CopyComponents} recipe function.
|
|
*/
|
|
public CopyComponents build() {
|
|
return new CopyComponents(from, Optional.ofNullable(include), Optional.ofNullable(exclude));
|
|
}
|
|
}
|
|
}
|