Jump to content

X-Lomir

Members
  • Posts

    97
  • Joined

  • Last visited

Everything posted by X-Lomir

  1. I'm trying to add stone cutter recipes now and I have a couple of issues that I hope can be solved somehow. In the stone cutter GUI my recipes show up and they give the correct result, but the item displayed is always the vertical slab mimicking oak planks. This I know it's because getResultItem() is used to render the "clickable" item to select a recipe and because I set my resultItem to a default of a vertical slab mimicking oak planks, however I don't know how to fix. For blocks that can be stone cut into 2 different kind of slabs I don't know how to make 2 vertical slab recipes appear too. So far only 1 appears. I know that my code does nothing to make more than 1 appear, but the problem is that I don't know how to do that. Most updated code is here.
  2. Actually I just tried removing that line and the slabs don't appear in the search tab after searching something anymore.
  3. @SubscribeEvent(priority = EventPriority.LOWEST) public void onRecipesUpdatedEvent(RecipesUpdatedEvent event) { MutableSearchTree<ItemStack> creativeSearchTree = Minecraft.getInstance().getSearchTree(SearchRegistry.CREATIVE_NAMES); for(BlockState referringBlockState : VerticalSlabUtils.slabMap.values().stream().map(item -> Block.byItem(item).defaultBlockState()).toList()) { creativeSearchTree.add(VerticalSlabUtils.getVerticalSlabItem(referringBlockState)); } creativeSearchTree.refresh(); } Like this it works too, but only if I add manually each vertical slab item, refreshing alone doesn't do. P.S. I use the lowest priority to be the last one refreshing the tree, don't know if it's an actual good idea tho.
  4. public void onServerAboutToStartEvent(ServerAboutToStartEvent event) { Map<Item, Item> slabMap = new LinkedHashMap<Item, Item>(), blockMap = new HashMap<Item, Item>(); for (Item slab : ForgeRegistries.ITEMS.tags().getTag(ItemTags.SLABS)) { for (CraftingRecipe recipe : event.getServer().getRecipeManager().getAllRecipesFor(RecipeType.CRAFTING)) { NonNullList<Ingredient> ingredients = recipe.getIngredients(); if (recipe.getResultItem().is(slab) && isRecipeWithBlocks(ingredients) && sameIngredients(ingredients)) { ingredients.stream().findFirst().ifPresent(ingredient -> { for (ItemStack itemStack : ingredient.getItems()) { if (!(itemStack.toString().contains("chiseled") || itemStack.toString().contains("pillar"))) { slabMap.put(slab, itemStack.getItem()); } blockMap.put(itemStack.getItem(), slab); } if (!slabMap.containsKey(slab)) { slabMap.put(slab, ingredient.getItems()[0].getItem()); } }); } } } VerticalSlabUtils.slabMap = ImmutableMap.copyOf(slabMap); VerticalSlabUtils.blockMap = ImmutableMap.copyOf(blockMap); MutableSearchTree<ItemStack> creativeSearchTree = Minecraft.getInstance().getSearchTree(SearchRegistry.CREATIVE_NAMES); for(BlockState referringBlockState : VerticalSlabUtils.slabMap.values().stream().map(item -> Block.byItem(item).defaultBlockState()).toList()) { creativeSearchTree.add(VerticalSlabUtils.getVerticalSlabItem(referringBlockState)); } creativeSearchTree.refresh(); } I moved the part of code adding vertical slabs to the search tree in the handler for ServerAboutToStartEvent, so it only gets called once. Still not the best in terms of efficiency as I have to loop through all the slabs after I have already done so. I would have liked to add each vertical slab to the search tree when I'm also putting the slab item in the map, however I can't do that because the hover name of a vertical slab depends on the static slabMap itself, which is null until I create it from the copy of the other one. This should be better than refreshing the search tree every time in fillItemCategory, but I'm still not sure if it's the best possible.
  5. So I did this: public class RecipeUpdateEventHandler { @SubscribeEvent(priority = EventPriority.LOWEST) public void onRecipesUpdatedEvent(RecipesUpdatedEvent event) { Minecraft.getInstance().getSearchTree(SearchRegistry.CREATIVE_NAMES).refresh(); } } and registered like so in my mod loader constructor: public JustVerticalSlabsLoader() { MinecraftForge.EVENT_BUS.register(new ServerAboutToStartEventHandler()); MinecraftForge.EVENT_BUS.register(new RecipeUpdateEventHandler()); IEventBus bus = FMLJavaModLoadingContext.get().getModEventBus(); BLOCKS.register(bus); BLOCK_ENTITIES.register(bus); ITEMS.register(bus); RECIPES.register(bus); bus.register(new ModelRegistryEventHandler()); } But it seems nothing changed. I get no errors but still my slabs do not appear in the search tab after searching. However if I do the following (and I also unsubscribed the event): public void fillItemCategory(CreativeModeTab creativeModeTab, NonNullList<ItemStack> itemStacks) { if (this.allowdedIn(creativeModeTab) && VerticalSlabUtils.slabMap != null) { MutableSearchTree<ItemStack> creativeSearchTree = Minecraft.getInstance().getSearchTree(SearchRegistry.CREATIVE_NAMES); for(BlockState referringBlockState : VerticalSlabUtils.slabMap.values().stream().map(item -> Block.byItem(item).defaultBlockState()).toList()) { ItemStack verticalSlab = VerticalSlabUtils.getItemStackWithState(this, referringBlockState); itemStacks.add(verticalSlab); creativeSearchTree.add(verticalSlab); } creativeSearchTree.refresh(); } } It works without any error, but I guess it's not best practice (?)
  6. I guessed something like that was happening unfortunately. So now way to make my vertical slabs be in the search results? Can't the search tree modified after it has been initialized?
  7. Okay, I think I'm done with recipes for now. Later on I want to add waxing recipes and stone cutting recipes, but now that I have the slabMap I think it's not going to be difficult. Anyway, I was trying to make my vertical slabs to have a proper hover name and, while I managed to do that, I found out that my vertical slabs can't be searched in the search tab. If no string is search, they will appear at the end of the tab, otherwise with any string they do not appear. I'm not sure how to fix this, I had hoped having a proper hover name would make the research work but it looks like it's not the case. Code is here.
  8. I knew I was missing something stupid! ๐Ÿคฆโ€โ™‚๏ธ๐Ÿคฆโ€โ™‚๏ธ๐Ÿคฆโ€โ™‚๏ธ Thank you, you're a real life saver!
  9. Okay, I thought I was doing something wrong. In the end just a null check in fillItemCategory fixes it. I have another problem now: I can't understand why only my ReferringBlockRecipe is working. I put some debug points in the other recipes matches and they never get called, but I made them the same exact way as my ReferringBlockRecipe, registering their serializers and all. I really don't get what's happening. Most recent code here.
  10. I created my ImmutableMap in my event handler as follow: @SubscribeEvent(priority = EventPriority.LOWEST) public void onServerAboutToStartEvent(ServerAboutToStartEvent event) { Map<Item, Item> slabMap = new HashMap<Item, Item>(); ForgeRegistries.ITEMS.tags().getTag(ItemTags.SLABS).forEach(slab -> { event.getServer().getRecipeManager().getAllRecipesFor(RecipeType.CRAFTING).forEach(recipe -> { if (recipe.getResultItem().is(slab)) { recipe.getIngredients().stream().filter(ingredient -> !ingredient.test(slab.getDefaultInstance())).findFirst().ifPresent(ingredient -> { ItemStack block = ingredient.getItems()[0]; if (!block.is(ItemTags.SLABS)) { slabMap.put(slab, block.getItem()); } }); } }); }); JustVerticalSlabsLoader.slabMap = ImmutableMap.copyOf(slabMap); } This (very naive) logic works and if I print out the slabMap I get this: { crimson_slab=crimson_planks, waxed_cut_copper_slab=waxed_cut_copper, birch_slab=birch_planks, mossy_stone_brick_slab=mossy_stone_bricks, cut_red_sandstone_slab=cut_red_sandstone, polished_andesite_slab=polished_andesite, smooth_sandstone_slab=smooth_sandstone, polished_blackstone_brick_slab=polished_blackstone_bricks, warped_slab=warped_planks, red_nether_brick_slab=red_nether_bricks, prismarine_slab=prismarine, smooth_quartz_slab=smooth_quartz, polished_deepslate_slab=polished_deepslate, waxed_oxidized_cut_copper_slab=waxed_oxidized_cut_copper, spruce_slab=spruce_planks, stone_slab=stone, cobblestone_slab=cobblestone, oxidized_cut_copper_slab=oxidized_cut_copper, polished_blackstone_slab=polished_blackstone, stone_brick_slab=stone_bricks, waxed_exposed_cut_copper_slab=waxed_exposed_cut_copper, dark_prismarine_slab=dark_prismarine, polished_granite_slab=polished_granite, andesite_slab=andesite, prismarine_brick_slab=prismarine_bricks, granite_slab=granite, cobbled_deepslate_slab=cobbled_deepslate, oak_slab=oak_planks, waxed_weathered_cut_copper_slab=waxed_weathered_cut_copper, deepslate_brick_slab=deepslate_bricks, quartz_slab=chiseled_quartz_block, end_stone_brick_slab=end_stone_bricks, deepslate_tile_slab=deepslate_tiles, cut_sandstone_slab=cut_sandstone, acacia_slab=acacia_planks, sandstone_slab=sandstone, purpur_slab=purpur_block, exposed_cut_copper_slab=exposed_cut_copper, polished_diorite_slab=polished_diorite, weathered_cut_copper_slab=weathered_cut_copper, smooth_stone_slab=smooth_stone, brick_slab=bricks, cut_copper_slab=cut_copper, blackstone_slab=blackstone, diorite_slab=diorite, red_sandstone_slab=red_sandstone, dark_oak_slab=dark_oak_planks, nether_brick_slab=nether_bricks, mossy_cobblestone_slab=mossy_cobblestone, smooth_red_sandstone_slab=smooth_red_sandstone, jungle_slab=jungle_planks } Now, this makes the game load fine and the print happens when I load a game. However if I try to use the map in my VerticalSlabBlockItem#fillItemCategory I get a NullPointerException making the game crash on load. How do I fix this? I pushed my code here.
  11. Isn't there a way to do the opposite, that is knowing the result ItemStack and get its recipe? Because I think it's better to cycle through all items having the slabs tag rather than cycling through all items and checking if they can make a slab.
  12. But how do I set values in my map at first? Of course once I have the map I'll always use that, but at first I have to put values in that map. So I need a way to go from a slab block to its block. It's fine to assume that a slab will always have a crafting made of 3 blocks in a row, if this can help.
  13. For a BlockPos you can use NbtUtils#readBlockPos and NbtUtils#writeBlockPos. There's also BlockEntity#getPosFromTag and BlockEntity has a BlockPos field with getter and setter. For the world I'm not sure either with tags, however BlockEntity has a protected Level field with setter and getter, maybe it can be of use (?).
  14. I'm trying to do what we said, but a few points are unclear to me. How do I get a list of all blocks/items that have the slabs tag? I saw that in previous Forge versions it was possible to do something like BlockTags.SLABS.getValues(), but it doesn't seem to be possible anymore. Once I have a block/item that is a slab, how do I get the block/item it's made from? Is it okay to have a reference to my ImmutableMap in my loader class, setting its value only in the ServerAboutToStartEvent handler, and then access it anywhere around my code? In particular I need to access it in my recipes and in my VerticalSlabBlockItem#fillItemCategory. I pushed a (very simple) draft here.
  15. Hi! Have you found a solution for .getValues() in the end? I need too to get the list of all blocks matching a specific tag
  16. Oh I thought that assemble would be called only after matches and so it was safe to use data from matches in assemble. If no internal state should be used, can I safely save 1 instance of my recipe so that its serializer can return always the same rather than instantiating a new one? Here I'm supposing that normal slabs will always be crafted with 3 of their respective blocks in a row. I know it could not always be the case, but it's fine for most cases. I don't need to get from block to normal slab with every recipe, but I want to implement several recipes, in particular two that involve only normal slabs and vertical slabs. So, to get the BlockState I need for the vertical slab I'd need to go from normal slab to its block, vice versa when I need to craft a normal slab from a vertical slab. As said above, they are made of both. So if the ImmutableMap is a good idea, I think I know how to implement the recipes I need. Problem is: how and when do I create this ImmutableMap? For how I would loop through all slabs registered in the game, modded or not, but I'm not sure when it's best to do this and how to actually implement it ๐Ÿ˜…
  17. I changed to another recipe that seemed actually easier: from vertical slabs to referring block. I created my class ReferringBlockRecipe implementing CraftingRecipe. I know the matches logic is not correct, but it's good enough for some tests. First of all, is there a way to get the amount of empty slots or filled slots? If not I'd design the matches logic this way: loop through crafting rows, check all but last slots of each row testing whether there are two vertical slabs next to each other. Then if I found exactly 1 match, return true, otherwise return false. If there was a way to know how many empty slots there are, I would first check if there are 7 empty slots and if so search for the first (and only) match. Return true if found, return false if not. Anyway, apart from these details, I'm wondering if my Serializer implementation is fine, I just return a new instance of ReferringBlockRecipe in fromJson and fromNetwork, while I do nothing in toNetwork. It seems to be working with the few tests I did, but maybe I'm missing something under the hood. My code with recipes can be found here. I have a couple more questions: Once I have a BlockState of a block, and so its Item and ItemStack, how to I get its slab? Loop through all recipes that use that block as ingredient? If so, how? Same as the previous one, but in the opposite sense: how do I get a block from its slab BlockState (or Item or ItemStack)? About these two points, I've thought something: I need on game startup to get a list of all the blocks in game that can be crafted into normal slabs, to use this list in fillItemCategory. While doing this I could just save in a static HashMap the blockstates of each block and its slab. This way I would have a list of blocks and slabs that can be used in my recipes and an easy and fast way to get from block to slab and vice versa while matching and assembling my recipes. So with this I would only need to know how to go from block to slab (or from slab to block, since maybe it's easier to loop through all blocks with "slabs" tag rather than looping through all blocks and checking if each has a slab) and then when I'd need to go the opposite way I could just use the HashMap.
  18. Okay, now that rendering is done I'd need some help with craftings. Should I make a new thread? Let me know. Anyway, how do I register a custom recipe? Do I also need a custom recipe serializer? Or can I just use one of the default ones? Should I create 4 different custom recipe classes, one for each recipe, or should I create less grouping them together? Should my recipe classes extend CraftingRecipe? Or is it better to extend ShapedRecipe/ShapelessRecipe? I tried implementing the recipe that should allow to craft a normal slab into a vertical slab, but I don't know how to get the block used to craft the normal slab from the normal slab, block I need to get its blockstate and return the correct version of a vertical slab. I also don't know what's the use of getResultItem and canCraftInDimensions. I also have more questions but I'm trying not to flood here and to solve as many things I can by myself, so maybe I'll ask them another time. EDIT: I actually think I got what canCraftInDimensions does
  19. I managed to solve also the breaking process not being shown. But to be honest I'm not sure why it works ๐Ÿ˜† I noticed that when I would start breaking the block and VerticalSlabBakedModel#getQuads would get called modelData was empty, thus making getQuads return an empty list. I'm not sure why when breaking the block getQuads gets called with an empty modelData, however if in that case I return my jsonBakedModel.getQuads rather than an empty list it works just fine and renders the breaking process correctly, without changing the "background" sprite (to my surprise). I'm wondering however if I need to cache also them and, in that case, how would I do that, since the modelData is empty and so the referringBlockState is null. Maybe cache them using the state passed to getQuads? But maybe since they are BakedQuads from the JSON model that has already been baked and it's static caching them is not necessary?
  20. I'm glad to announce that I found a way to fix the stretched textures. I finally had more time to go line by line debugging starting from the baking of the JSON model, and I found the class FaceBakery that's responsible for creating the vertices of a BakedQuad. In particular in fillVertex there are two elements being set that involve the texture rendered and are set like so: arr[i + 4] = Float.floatToRawIntBits(sprite.getU((double)blockFaceUV.getU(i) * .999 + blockFaceUV.getU((i + 2) % 4) * .001)); arr[i + 4 + 1] = Float.floatToRawIntBits(sprite.getV((double)blockFaceUV.getV(i) * .999 + blockFaceUV.getV((i + 2) % 4) * .001)); and so I figured out that if I had access the my models blockFaceUVs when needed I could just update those specific vertices. Sadly, there is no way to do that. But luckily I realized that I didn't need those blockFaceUVs at all because while it's true that they hold data on which part of the sprite to take, I actually need the double parameter they compute to. So with a little bit of math to get the inverse formula I managed to retrieve the double parameter from the data in my original sprite, then I could update those specific vertices by applying the same formula as above but using the referring sprite and the double parameter I retrieved from my original sprite. As always I'll leave here my most updated code. Now I just need to understand why the breaking progress is not shown and fix it, then I think the rendering part will be finally done.
  21. But getSoundType is called in place deep nested in, so I would need to completely copy the code inside place and just change 1 line. Which I'm not a big fan of, but anyway I was already copying the whole code in updateCustomBlockEntityTag so I might as well do that. However I actually can't because in place there's a call to BlockItem#updateBlockStateFromTag which is private so I can't call it from my subclass. I would need to copy also that code in, but then I would also need to copy the code in BlockItem#updateState because it's private too, so it would make a bit of a mess in my code. P.S. I don't know if it was only me, but I couldn't connect to the form yesterday, this is way I replied so late
  22. Okay, so I just create this new method that takes an itemStack and returns a SoundType. But when will it get called? And also in BlockItem#place the "wrong" getSoundType would still be called. I am so sure I'm missing something obvious but I just can't get what it is ๐Ÿ˜…
  23. I would be interested in this, but I'm not sure what a variant is. I tried searching online but I found nothing useful. Also in BlockItem#place getSoundType is never called with the itemStack, what am I missing? I'm sorry if I'm missing something obvious or that should just be Java knowledge.
  24. So I think I found a solution. In BlockItem#updateCustomBlockEntityTag the entity tag is updated only if level.getServer() != null. This is a problem because when it gets called client side it doesn't update the entity tag and so my BlockEntity doesn't have a referringBlockState. Indeed in multiplayer others would actually hear the correct sound (BlockEntity is updated, sound can be retrieved, level is server side, level#playSound will play it for anyone but the player), it's only the player placing the block that would not hear the correct sound (BlockEntity is not updated, sound defaults to Stone, level is client side, level#playSound will play it for the player only). My solution is to override BlockItem#updateCustomBlockEntityTag (not the static one) and if BlockItem#updateCustomBlockEntityTag (the static one) returns false, then I check if it was because level#getServer() returned null and if so I do the same BlockItem#updateCustomBlockEntityTag (the static one) would do if level#getServer() did not return null. It works and doesn't seem to be creating any problems, however I'm wondering if will it actually create some kind of problem because I'm updating my BlockEntity also client side (and if it doesn't create problems, why does it update only server side by default?) is there no better way of doing this other than copy-pasting the logic from BlockItem#updateCustomBlockEntityTag (the static one). Ideally it would be nice to have all the logic that actually updates the BlockEntity in a separated static method and then having BlockItem#updateCustomBlockEntityTag (the static one) call it if level#getServer() != null (and return false otherwise). This way it would also be possible to forcefully updating the BlockEntity by calling the method wrapped in the check, which is exactly what I'd need to.
×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.