Posted May 10, 20223 yr Hey Guys, i'm trying to add a new recipe type (fletching) to my mod and i'm currenty struggling on implementing the crafting system. The result item appears if one ingredient (of multiple) has been inserted and then removed. My Menu: Spoiler public class FletchingTableMenu extends AbstractContainerMenu { private final ContainerLevelAccess levelAccess; private final InvWrapper playerInvWrapper; private final FletchingContainer craftingContainer = new FletchingContainer(this); private final ResultContainer resultContainer = new ResultContainer(); private int lastHotBarIndex, lastInventoryIndex, lastCraftingIndex, resultIndex; private final Player player; public FletchingTableMenu(int pContainerId, ContainerLevelAccess levelAccess, Inventory inventory) { super(Vanilla_PlusMenuTypes.FLETCHING_TABLE_MENU, pContainerId); this.levelAccess = levelAccess; this.playerInvWrapper = new InvWrapper(inventory); this.player = inventory.player; setupFletchingTableSlots(); setupPlayerSlots(); } @Override public ItemStack quickMoveStack(Player pPlayer, int pIndex) { ItemStack slotItemStack = slots.get(pIndex).getItem(); if (slotItemStack.isEmpty()) return ItemStack.EMPTY; if (pIndex < resultIndex) { //Handle Fletching Table Inventory Shift-Click slotItemStack = moveToPlayerInventory(slotItemStack); } else { //Handle Inventory Shift-Click slotItemStack = moveToFletchingTableInventory(slotItemStack); } Slot slot = slots.get(pIndex); slot.set(slotItemStack); slot.setChanged(); return ItemStack.EMPTY; } private void setupFletchingTableSlots() { int slotIndex = 0; int baseY = 21; //left slots for (int i = 0; i < 3; i++) { addSlot(new FilteredSlot(this.craftingContainer, slotIndex++, 29, baseY + 21 * i)); } //right slots for (int i = 0; i < 3; i++) { addSlot(new FilteredSlot(this.craftingContainer, slotIndex++, 50, baseY + 21 * i)); } lastCraftingIndex = slots.size(); //result this.addSlot(new ResultSlot(player, craftingContainer, this.resultContainer, slotIndex, 111, 41)); resultIndex = slots.size(); } private void setupPlayerSlots() { int index = 0; //Hot bar for (int col = 0; col < 9; col++) { addSlot(new SlotItemHandler(this.playerInvWrapper, index++, 8 + 18 * col, 100 + 18 * 3)); } lastHotBarIndex = slots.size(); //Inventory for (int row = 0; row < 3; row++) { for (int col = 0; col < 9; col++) { addSlot(new SlotItemHandler(this.playerInvWrapper, index++, 8 + 18 * col, 96 + 18 * row)); } } lastInventoryIndex = slots.size(); } @Override public boolean stillValid(Player pPlayer) { return stillValid(this.levelAccess, pPlayer, Blocks.FLETCHING_TABLE); } private ItemStack moveToPlayerInventory(int slotIndex, ItemStack itemStack) { Slot currentCurrentSlot = getSlot(slotIndex); //Check if slot exist if (currentCurrentSlot == null) return itemStack; //Try to place Item into the Player Slot if (currentCurrentSlot.mayPlace(itemStack)) { itemStack = currentCurrentSlot.safeInsert(itemStack); currentCurrentSlot.setChanged(); } return itemStack; } private ItemStack moveToPlayerInventory(ItemStack stack) { //Check Space in hot-bar for (int i = resultIndex; i < lastHotBarIndex; i++) { stack = moveToPlayerInventory(i, stack); //Check if there are no items left if (stack.isEmpty()) { break; } } //Check Space in Inventory if (!stack.isEmpty()) { for (int i = lastHotBarIndex; i < lastInventoryIndex; i++) { stack = moveToPlayerInventory(i, stack); //Check if there are no items left if (stack.isEmpty()) { break; } } } return stack; } private ItemStack moveToFletchingTableInventory(ItemStack stack) { //Handle Player Inventory Shift-Click for (int i = 0; i < lastCraftingIndex; i++) { Slot currentCurrentSlot = getSlot(i); //Check if slot exist if (currentCurrentSlot == null) break; //Try to place Item into the Player Slot if (currentCurrentSlot.mayPlace(stack)) { stack = currentCurrentSlot.safeInsert(stack); currentCurrentSlot.setChanged(); } } return stack; } @Override public void slotsChanged(Container pInventory) { levelAccess.execute((level, pos) -> { slotChangedCraftingGrid(this, level, this.player, craftingContainer, resultContainer); }); } protected static void slotChangedCraftingGrid(FletchingTableMenu menu, Level level, Player player, FletchingContainer craftingContainer, ResultContainer resultContainer) { if(player instanceof ServerPlayer serverPlayer){ ItemStack itemstack = ItemStack.EMPTY; Optional<FletchingRecipe> optional = level.getServer().getRecipeManager().getRecipeFor(Vanilla_PlusRecipeTypeRegistry.FLETCHING_RECIPE.get(), craftingContainer, level); if (optional.isPresent()) { FletchingRecipe fletchingRecipe = optional.get(); if (resultContainer.setRecipeUsed(level, serverPlayer, fletchingRecipe)) { itemstack = fletchingRecipe.assemble(craftingContainer); } } resultContainer.setItem(0, itemstack); menu.setRemoteSlot(0, itemstack); serverPlayer.connection.send(new ClientboundContainerSetSlotPacket(menu.containerId, menu.incrementStateId(), 0, itemstack)); } } } Recipe Serializer: Spoiler public class FletchingRecipeSerializer implements RecipeSerializer<FletchingRecipe> { private ResourceLocation registryName; @Override public FletchingRecipe fromJson(ResourceLocation pRecipeId, JsonObject pSerializedRecipe) { Map<String, Ingredient> ingredientMap = keyFromJson(GsonHelper.getAsJsonObject(pSerializedRecipe, "key")); String[] patternArray = shrink(patternFromJson(GsonHelper.getAsJsonArray(pSerializedRecipe, "pattern"))); int i = patternArray[0].length(); int j = patternArray.length; NonNullList<Ingredient> dissolvePattern = dissolvePattern(patternArray, ingredientMap, i, j); ItemStack result = ShapedRecipe.itemStackFromJson(GsonHelper.getAsJsonObject(pSerializedRecipe, "result")); return new FletchingRecipe(dissolvePattern, result, pRecipeId); } @Nullable @Override public FletchingRecipe fromNetwork(ResourceLocation pRecipeId, FriendlyByteBuf pBuffer) { int i = pBuffer.readVarInt(); int j = pBuffer.readVarInt(); String s = pBuffer.readUtf(); NonNullList<Ingredient> dissolvePattern = NonNullList.withSize(i * j, Ingredient.EMPTY); for (int k = 0; k < dissolvePattern.size(); ++k) { dissolvePattern.set(k, Ingredient.fromNetwork(pBuffer)); } ItemStack result = pBuffer.readItem(); return new FletchingRecipe(dissolvePattern, result, pRecipeId); } @Override public void toNetwork(FriendlyByteBuf pBuffer, FletchingRecipe pRecipe) { pBuffer.writeVarInt(2); pBuffer.writeVarInt(3); for (Ingredient ingredient : pRecipe.getIngredients()) { ingredient.toNetwork(pBuffer); } pBuffer.writeItem(pRecipe.getResultItem()); } @Override public RecipeSerializer<?> setRegistryName(ResourceLocation name) { registryName=name; return this; } @Nullable @Override public ResourceLocation getRegistryName() { return registryName; } @Override public Class<RecipeSerializer<?>> getRegistryType() { return ForgeRegistries.RECIPE_SERIALIZERS.getRegistrySuperType(); } static Map<String, Ingredient> keyFromJson(JsonObject pKeyEntry) { Map<String, Ingredient> map = Maps.newHashMap(); for (Map.Entry<String, JsonElement> entry : pKeyEntry.entrySet()) { if (entry.getKey().length() != 1) { throw new JsonSyntaxException("Invalid key entry: '" + entry.getKey() + "' is an invalid symbol (must be 1 character only)."); } if (" ".equals(entry.getKey())) { throw new JsonSyntaxException("Invalid key entry: ' ' is a reserved symbol."); } map.put(entry.getKey(), Ingredient.fromJson(entry.getValue())); } map.put(" ", Ingredient.EMPTY); return map; } static String[] patternFromJson(JsonArray pPatternArray) { String[] astring = new String[pPatternArray.size()]; if (astring.length > 3) { throw new JsonSyntaxException("Invalid pattern: too many rows, " + 3 + " is maximum"); } else if (astring.length == 0) { throw new JsonSyntaxException("Invalid pattern: empty pattern not allowed"); } else { for (int i = 0; i < astring.length; ++i) { String s = GsonHelper.convertToString(pPatternArray.get(i), "pattern[" + i + "]"); if (s.length() > 2) { throw new JsonSyntaxException("Invalid pattern: too many columns, " + 2 + " is maximum"); } if (i > 0 && astring[0].length() != s.length()) { throw new JsonSyntaxException("Invalid pattern: each row must be the same width"); } astring[i] = s; } return astring; } } static String[] shrink(String... pToShrink) { int i = Integer.MAX_VALUE; int j = 0; int k = 0; int l = 0; for (int i1 = 0; i1 < pToShrink.length; ++i1) { String s = pToShrink[i1]; i = Math.min(i, firstNonSpace(s)); int j1 = lastNonSpace(s); j = Math.max(j, j1); if (j1 < 0) { if (k == i1) { ++k; } ++l; } else { l = 0; } } if (pToShrink.length == l) { return new String[0]; } else { String[] astring = new String[pToShrink.length - l - k]; for (int k1 = 0; k1 < astring.length; ++k1) { astring[k1] = pToShrink[k1 + k].substring(i, j + 1); } return astring; } } private static int firstNonSpace(String pEntry) { int i; for (i = 0; i < pEntry.length() && pEntry.charAt(i) == ' '; ++i) { } return i; } private static int lastNonSpace(String pEntry) { int i; for (i = pEntry.length() - 1; i >= 0 && pEntry.charAt(i) == ' '; --i) { } return i; } static NonNullList<Ingredient> dissolvePattern(String[] pPattern, Map<String, Ingredient> pKeys, int pPatternWidth, int pPatternHeight) { NonNullList<Ingredient> nonnulllist = NonNullList.withSize(pPatternWidth * pPatternHeight, Ingredient.EMPTY); Set<String> set = Sets.newHashSet(pKeys.keySet()); set.remove(" "); for (int i = 0; i < pPattern.length; ++i) { for (int j = 0; j < pPattern[i].length(); ++j) { String s = pPattern[i].substring(j, j + 1); Ingredient ingredient = pKeys.get(s); if (ingredient == null) { throw new JsonSyntaxException("Pattern references symbol '" + s + "' but it's not defined in the key"); } set.remove(s); nonnulllist.set(j + pPatternWidth * i, ingredient); } } if (!set.isEmpty()) { throw new JsonSyntaxException("Key defines symbols that aren't used in pattern: " + set); } else { return nonnulllist; } } Recipe: Spoiler @RequiredArgsConstructor public class FletchingRecipe implements Recipe<FletchingContainer> { final NonNullList<Ingredient> recipeItems; final ItemStack result; @Getter private final ResourceLocation id; @Override public boolean matches(FletchingContainer pCraftingInventory, Level pLevel) { for (int i = 0; i < pCraftingInventory.getWidth(); ++i) { for (int j = 0; j < pCraftingInventory.getHeight(); ++j) { int k = i - 2; int l = j - 3; Ingredient ingredient = Ingredient.EMPTY; if (k >= 0 && l >= 0 && k < 2 && l < 3) { ingredient = this.recipeItems.get(k + l * 2); } if (!ingredient.test(pCraftingInventory.getItem(i + j * pCraftingInventory.getWidth()))) { return false; } } } return true; } @Override public ItemStack assemble(FletchingContainer pContainer) { return this.getResultItem().copy(); } @Override public boolean canCraftInDimensions(int pWidth, int pHeight) { return pWidth >= 2 && pHeight >= 3; } @Override public ItemStack getResultItem() { return result; } @Override public RecipeSerializer<?> getSerializer() { return Vanilla_PlusRecipeSerializers.FLETCHING_RECIPE; } @Override public RecipeType<?> getType() { return Vanilla_PlusRecipeTypeRegistry.FLETCHING_RECIPE.get(); } } test recipe json: Spoiler { "type": "vanilla_plus:fletching_recipe", "pattern": [ "F ", "S ", "A " ], "key": { "F": { "item": "minecraft:flint" }, "S": { "tag": "forge:rods/wooden" }, "A": { "tag": "forge:feathers" } }, "result": { "item": "minecraft:arrow", "count": 8 } }
May 11, 20223 yr Author 1 hour ago, diesieben07 said: Git repo https://github.com/ManasMods/vanilla_plus
May 12, 20223 yr Author On 5/11/2022 at 11:04 AM, diesieben07 said: Why? The client knows its dimension already. Well, good point. On 5/11/2022 at 11:04 AM, diesieben07 said: You completely overwriting the jukebox BlockEntity with your own using mixins is not a good idea. You should instead modify the existing one to your needs. I'm gonna do that in an update. I'm happy if something works somehow ^^" On 5/11/2022 at 11:04 AM, diesieben07 said: I'll have to look at the actual issue later when I am off work. Nice thank you
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.