Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Brewing recipes #161

Open
wants to merge 28 commits into
base: 1.19.4
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
0d973e0
brewing fuel registry
Platymemo Aug 5, 2022
b834560
quilt brewing recipes
Platymemo Aug 5, 2022
8cf04c8
Merge remote-tracking branch 'QuiltMC/1.19' into 1.19+brewing_recipes
Platymemo Aug 5, 2022
a00e0fa
fix incompatibility with vanilla clients
Platymemo Aug 5, 2022
d5789d4
test brewing recipe
Platymemo Aug 6, 2022
8b845fc
brewing recipes ready for review
Platymemo Aug 6, 2022
e55b31d
Merge remote-tracking branch 'QuiltMC/1.19' into 1.19+brewing_recipes
Platymemo Aug 6, 2022
e205201
quick fix
Platymemo Aug 6, 2022
40b60f5
apply licenses
Platymemo Aug 6, 2022
c42ad20
apply suggestions
Platymemo Aug 6, 2022
3581671
Apply javadoc suggestions
Platymemo Aug 9, 2022
312b54e
Extend max values for brewing fuel and brew time
Platymemo Aug 9, 2022
1b642b5
missed a suggestion
Platymemo Aug 9, 2022
e4bd994
Apply suggestions
Platymemo Aug 9, 2022
d2cd203
Merge remote-tracking branch 'QuiltMC/1.19' into brewing-recipes
Platymemo Aug 29, 2022
36424a4
Clear brewing ingredients on reload
Platymemo Aug 29, 2022
de4faf0
dont crash with vanilla recipes
Platymemo Sep 7, 2022
fb046cb
fix potion villager trades
Platymemo Sep 7, 2022
f072fd3
brewing docs
Platymemo Sep 7, 2022
0ac3b3a
more compatible mixin
Platymemo Sep 12, 2022
bf63a47
deduplicate recipe match checks
Platymemo Sep 20, 2022
494ce5b
Merge remote-tracking branch 'QuiltMC/1.19.4' into brewing-recipes
Platymemo Mar 31, 2023
984d0a5
update brewing recipes
Platymemo Apr 1, 2023
9d77336
Merge branch '1.19.4' into brewing-recipes
EnnuiL Apr 12, 2023
f19b914
Apply suggestions from code review
Platymemo Apr 13, 2023
56260fb
move recipe serializer registration to onInitialize
Platymemo Apr 13, 2023
80d2d31
move brewing recipes to the quilt namespace
Platymemo Apr 13, 2023
ce54b86
Merge branch '1.19.4' into brewing-recipes
EnnuiL Apr 22, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions library/data/recipe/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,10 @@ qslModule {
testmodOnly("resource_loader")
}
}
entrypoints {
init {
values = ["org.quiltmc.qsl.recipe.impl.RecipeImpl"]
}
}
accessWidener()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.quiltmc.qsl.recipe.api;

import net.minecraft.recipe.RecipeType;

import org.quiltmc.qsl.recipe.impl.AbstractBrewingRecipe;
import org.quiltmc.qsl.recipe.impl.RecipeImpl;

public class Recipes {
public static final RecipeType<AbstractBrewingRecipe<?>> BREWING = RecipeImpl.BREWING;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
package org.quiltmc.qsl.recipe.impl;

import java.util.ArrayList;
import java.util.List;

import com.google.gson.JsonObject;

import net.minecraft.block.Blocks;
import net.minecraft.block.entity.BrewingStandBlockEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.recipe.Ingredient;
import net.minecraft.recipe.Recipe;
import net.minecraft.recipe.RecipeSerializer;
import net.minecraft.recipe.RecipeType;
import net.minecraft.util.Identifier;
import net.minecraft.util.JsonHelper;
import net.minecraft.util.collection.DefaultedList;
import net.minecraft.util.registry.Registry;
import net.minecraft.world.World;

import org.quiltmc.qsl.recipe.api.Recipes;
import org.quiltmc.qsl.recipe.api.serializer.QuiltRecipeSerializer;

public abstract class AbstractBrewingRecipe<T> implements Recipe<BrewingStandBlockEntity> {
public static final List<Ingredient> VALID_INGREDIENTS = new ArrayList<>();
final T input;
final Ingredient ingredient;
final T output;
final int fuel;
ItemStack ghostOutput;
private final Identifier id;

public AbstractBrewingRecipe(Identifier id, T input, Ingredient ingredient, T output, int fuel) {
this.id = id;
this.input = input;
this.ingredient = ingredient;
VALID_INGREDIENTS.add(ingredient);
this.output = output;
this.ghostOutput = new ItemStack(Items.POTION);
this.fuel = fuel;
}

@Override
public RecipeType<AbstractBrewingRecipe<?>> getType() {
return Recipes.BREWING;
}

@Override
public ItemStack craft(BrewingStandBlockEntity inventory) {
for (int i = 0; i < 3; i++) {
if (this.matches(i, inventory.getStack(i))) {
inventory.setStack(i, craft(i, inventory.getStack(i)));
}
}

return ItemStack.EMPTY;
}

/**
* Transforms the input {@link ItemStack} to the output {@link ItemStack}.
*
* @param slot the index of the slot
* @param input the {@link ItemStack} in the provided slot
* @return the output {@link ItemStack}
*/
protected abstract ItemStack craft(int slot, ItemStack input);

@Override
public boolean matches(BrewingStandBlockEntity inventory, World world) {
ItemStack ingredient = inventory.getStack(3);
if (this.ingredient.test(ingredient)) {
for (int i = 0; i < 3; ++i) {
ItemStack stack = inventory.getStack(i);
if (matches(i, stack)) {
return true;
}
}
}
return false;
}

/**
* Matches based on stacks in the potion slots.
*
* @param slot the index of the slot
* @param input the {@link ItemStack} in the provided slot
* @return {@code true} if the {@link ItemStack} is a valid {@link AbstractBrewingRecipe#input} for this recipe, or {@code false}
*/
public abstract boolean matches(int slot, ItemStack input);

/**
* @return how much fuel this recipe takes to craft
*/
public int getFuelUse() {
return fuel;
}

@Override
public boolean fits(int width, int height) {
return true;
}

@Override
public DefaultedList<ItemStack> getRemainder(BrewingStandBlockEntity inventory) {
// TODO integrate with custom recipe remainders
return Recipe.super.getRemainder(inventory);
}

@Override
public boolean isIgnoredInRecipeBook() {
return true;
}

@Override
public ItemStack createIcon() {
return new ItemStack(Blocks.BREWING_STAND);
}

@Override
public ItemStack getOutput() {
return this.ghostOutput;
}

@Override
public Identifier getId() {
return this.id;
}

static abstract class AbstractBrewingSerializer<T, R extends AbstractBrewingRecipe<T>> implements RecipeSerializer<R>, QuiltRecipeSerializer<R> {
private final RecipeFactory<T, ? extends R> recipeFactory;

AbstractBrewingSerializer(RecipeFactory<T, ? extends R> recipeFactory) {
this.recipeFactory = recipeFactory;
}

@Override
public R read(Identifier id, JsonObject json) {
Ingredient ingredient = Ingredient.fromJson(JsonHelper.getObject(json, "ingredient"));
T input = this.deserialize("input", json);
T output = this.deserialize("output", json);
int fuel = JsonHelper.getInt(json, "fuel", 1);
return this.recipeFactory.create(id, input, ingredient, output, fuel);
}

@Override
public R read(Identifier id, PacketByteBuf buf) {
Ingredient ingredient = Ingredient.fromPacket(buf);
T input = this.deserialize(buf);
T output = this.deserialize(buf);
int fuel = buf.readInt();
return this.recipeFactory.create(id, input, ingredient, output, fuel);
}

@Override
public void write(PacketByteBuf buf, R recipe) {
recipe.ingredient.write(buf);
this.serialize(recipe.input, buf);
this.serialize(recipe.output, buf);
buf.writeInt(recipe.fuel);
}

@Override
public JsonObject toJson(R recipe) {
JsonObject json = new JsonObject();
json.add("ingredient", recipe.ingredient.toJson());
json.addProperty("type", Registry.RECIPE_SERIALIZER.getId(recipe.getSerializer()).toString());
this.serialize(recipe.input, "input", json);
this.serialize(recipe.output, "output", json);
json.addProperty("fuel", recipe.fuel);
return json;
}

public abstract T deserialize(String element, JsonObject json);

public abstract T deserialize(PacketByteBuf buf);

public abstract void serialize(T value, String element, JsonObject json);

public abstract void serialize(T value, PacketByteBuf buf);

@FunctionalInterface
interface RecipeFactory<T, R extends AbstractBrewingRecipe<T>> {
R create(Identifier id, T input, Ingredient ingredient, T output, int fuel);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package org.quiltmc.qsl.recipe.impl;

import java.util.ArrayList;
import java.util.List;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException;

import net.minecraft.entity.effect.StatusEffect;
import net.minecraft.entity.effect.StatusEffectInstance;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtList;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.potion.Potion;
import net.minecraft.potion.PotionUtil;
import net.minecraft.recipe.Ingredient;
import net.minecraft.recipe.RecipeSerializer;
import net.minecraft.util.Identifier;
import net.minecraft.util.JsonHelper;
import net.minecraft.util.registry.Registry;

public class CustomPotionBrewingRecipe extends PotionBrewingRecipe {
private final List<StatusEffectInstance> statusEffects = new ArrayList<>();
public CustomPotionBrewingRecipe(Identifier id, Potion input, Ingredient ingredient, Potion output, int fuel) {
super(id, input, ingredient, output, fuel);
PotionUtil.setCustomPotionEffects(this.ghostOutput, this.statusEffects);
}

@Override
protected ItemStack craft(int slot, ItemStack input) {
ItemStack output = super.craft(slot, input);

var statusEffects = this.statusEffects;
if (input.getOrCreateNbt().contains("CustomPotionEffects")) {
statusEffects.addAll(PotionUtil.getCustomPotionEffects(input));
PotionUtil.setCustomPotionEffects(output, statusEffects);
}

PotionUtil.setCustomPotionEffects(output, statusEffects);
Platymemo marked this conversation as resolved.
Show resolved Hide resolved

return output;
}

@Override
public RecipeSerializer<CustomPotionBrewingRecipe> getSerializer() {
return RecipeImpl.CUSTOM_POTION_SERIALIZER;
}

static class Serializer extends PotionBrewingRecipe.Serializer<CustomPotionBrewingRecipe> {
Serializer(RecipeFactory<Potion, CustomPotionBrewingRecipe> recipeFactory) {
super(recipeFactory);
}

@Override
public CustomPotionBrewingRecipe read(Identifier id, JsonObject json) {
CustomPotionBrewingRecipe recipe = super.read(id, json);

if (json.has("effects")) {
recipe.statusEffects.clear();
for(JsonElement effectJson : JsonHelper.getArray(json, "effects")) {
StatusEffectInstance instance;
instance = readStatusEffectInstance(effectJson);
recipe.statusEffects.add(instance);
}
}

return recipe;
}

private StatusEffectInstance readStatusEffectInstance(JsonElement json) {
if (json instanceof JsonObject jsonObject) {
String string = JsonHelper.getString(jsonObject, "type");
StatusEffect statusEffect = Registry.STATUS_EFFECT
.getOrEmpty(new Identifier(string))
.orElseThrow(() -> new JsonSyntaxException("Unknown status effect '" + string + "'"));
int duration = JsonHelper.getInt(jsonObject, "duration", 20);
int amplifier = JsonHelper.getInt(jsonObject, "amplifier", 0);
boolean ambient = JsonHelper.getBoolean(jsonObject, "ambient", false);
boolean showParticles = JsonHelper.getBoolean(jsonObject, "show_particles", true);
boolean showIcon = JsonHelper.getBoolean(jsonObject, "show_icon", true);
return new StatusEffectInstance(statusEffect, duration, amplifier, ambient, showParticles, showIcon);
} else { // default to a basic 20 tick 0 amplifier status effect
String string = json.getAsString();
StatusEffect statusEffect = Registry.STATUS_EFFECT
.getOrEmpty(new Identifier(string))
.orElseThrow(() -> new JsonSyntaxException("Unknown status effect '" + string + "'"));
return new StatusEffectInstance(statusEffect, 20);
}
}

@Override
public void write(PacketByteBuf buf, CustomPotionBrewingRecipe recipe) {
super.write(buf, recipe);
NbtCompound nbt = new NbtCompound();
NbtList statusEffectsNbt = new NbtList();
recipe.statusEffects.forEach(effect -> {
NbtCompound effectNbt = new NbtCompound();
effect.writeNbt(effectNbt);
statusEffectsNbt.add(effectNbt);
});
nbt.put("CustomPotionEffects", statusEffectsNbt);
buf.writeNbt(nbt);
}

@Override
public CustomPotionBrewingRecipe read(Identifier id, PacketByteBuf buf) {
CustomPotionBrewingRecipe recipe = super.read(id, buf);
recipe.statusEffects.clear();
PotionUtil.getCustomPotionEffects(buf.readNbt(), recipe.statusEffects);
return recipe;
}
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package org.quiltmc.qsl.recipe.impl;

import com.google.gson.JsonObject;

import net.minecraft.item.ItemStack;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.potion.Potion;
import net.minecraft.potion.PotionUtil;
import net.minecraft.recipe.Ingredient;
import net.minecraft.recipe.RecipeSerializer;
import net.minecraft.util.Identifier;
import net.minecraft.util.JsonHelper;
import net.minecraft.util.registry.Registry;

public class PotionBrewingRecipe extends AbstractBrewingRecipe<Potion> {
public PotionBrewingRecipe(Identifier id, Potion input, Ingredient ingredient, Potion output, int fuel) {
super(id, input, ingredient, output, fuel);
PotionUtil.setPotion(this.ghostOutput, this.output);
}

@Override
protected ItemStack craft(int slot, ItemStack input) {
return PotionUtil.setPotion(input.copy(), this.output);
}

@Override
public boolean matches(int slot, ItemStack input) {
return !input.isEmpty() && PotionUtil.getPotion(input).equals(this.input);
}

@Override
public RecipeSerializer<? extends PotionBrewingRecipe> getSerializer() {
return RecipeImpl.POTION_SERIALIZER;
}

static class Serializer<R extends PotionBrewingRecipe> extends AbstractBrewingSerializer<Potion, R> {
Serializer(RecipeFactory<Potion, R> recipeFactory) {
super(recipeFactory);
}

@Override
public Potion deserialize(String element, JsonObject json) {
return Potion.byId(JsonHelper.getString(json, element, "empty"));
}

@Override
public Potion deserialize(PacketByteBuf buf) {
return Potion.byId(buf.readString());
}

@Override
public void serialize(Potion value, String element, JsonObject json) {
json.addProperty(element, Registry.POTION.getId(value).toString());
}

@Override
public void serialize(Potion value, PacketByteBuf buf) {
buf.writeString(Registry.POTION.getId(value).toString());
}
}
}
Loading