Jump to content

How to set texture for dynamically registered blocks from already existing blocks [1.18.2]


X-Lomir

Recommended Posts

15 hours ago, diesieben07 said:

Override place and call it :)

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

Link to comment
Share on other sites

  • Replies 92
  • Created
  • Last Reply

Top Posters In This Topic

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.

Link to comment
Share on other sites

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?

 

Link to comment
Share on other sites

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

Edited by X-Lomir
Link to comment
Share on other sites

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.

 

Edited by X-Lomir
Link to comment
Share on other sites

4 minutes ago, diesieben07 said:

Mostly fine except that your recipe class must not have internal state (in your case the firstIndex). Calling assemble must work without first calling matches, the two methods must operate independently of each other.

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?

9 minutes ago, diesieben07 said:

If you mean that you want to get a generic slab, then yes you'd have to hope that there exists a "standard" slab crafting recipe (and the slab doesn't require some modded machine). I don't quite understand why you need to do this though?

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.

17 minutes ago, diesieben07 said:

Are your recipes not made from the actual block (i.e. oak planks) instead of the slab? If so, you could make this Map, yes. But it would have to be immutable and thread safe (ImmutableMap is threadsafe), because in single player both server and client thread would use it.

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 😅

Link to comment
Share on other sites

1 hour ago, diesieben07 said:

Recipes can come from data packs, so you have to do this after data packs are loaded. ServerAboutToStartEvent should be all you need.

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.

Link to comment
Share on other sites

3 hours ago, diesieben07 said:

You again have to look into your map from slabs to blocks. There is no direct link.

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.

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

2 hours ago, diesieben07 said:

fillItemCategory might be called before the game has started a server, which means there are no recipes yet. You must deal with this fact.

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.

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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 (?)

Edited by X-Lomir
Updated a piece of code.
Link to comment
Share on other sites

1 hour ago, diesieben07 said:

Yeah, this means the search tree is refreshed very often, which is an expensive operation.

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.

Link to comment
Share on other sites

@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.

Link to comment
Share on other sites

19 minutes ago, diesieben07 said:

You don't even need to call refresh, adding is enough.

Actually I just tried removing that line and the slabs don't appear in the search tab after searching something anymore.

Link to comment
Share on other sites

I'm trying to add stone cutter recipes now and I have a couple of issues that I hope can be solved somehow.

  1. 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.
  2. 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.

Link to comment
Share on other sites

3 hours ago, diesieben07 said:

The easiest way to know this is that if it is created from your serializer's fromNetwork function, then it is on the client.

I did this:

@Override
public BlockToVerticalSlabStonecutterRecipe fromNetwork(ResourceLocation resourceLocation, FriendlyByteBuf friendlyByteBuf) {
	return new BlockToVerticalSlabStonecutterRecipe(true);
}

Where that true is the value for a private final boolean field of my BlockToVerticalSlabStonecutterRecipe, but it appears that the field is always false and my Serializer#fromNetwork is never getting called (I put some breakpoints and they never got activated). I'm probably missing out once again something obvious.

3 hours ago, diesieben07 said:

Just make two recipe JSONs?

Mmh... I will try to explain myself better: there are cases where a block can be stonecut into 2 different kind of slabs, so I need to have 2 different vertical slab recipes for that block too. However this, like the rest of this mod, must be dynamic, I can't go with a solution that will work when there are 2 slabs but not 3 or more, I'd need a way to dynamically add the stonecutting vertical slab recipes. For example let's take stone: it can be stonecut into stone slab and brick stone slab, so I need that also a vertical slab mimicking stone and a vertical slab mimicking stone bricks can be stonecut from stone. Let's suppose another mod adds a new stone variant, which we'll call stone tiles. Now the stonecutting recipes for stone have an extra slab, the stone tiles slab. Without any extra step also a vertical slab for stone tiles should be present in the stonecutting menu.

Link to comment
Share on other sites

2 hours ago, diesieben07 said:

You'll have to use the kludge that is EffectiveSide.get.

This works, thanks!

2 hours ago, diesieben07 said:

I don't think this is possible. There must always be a JSON file for every recipe that is being loaded. While you could use AddPackFindersEvent to create a dynamic datapack at runtime, it wouldn't allow you to inspect the already existing recipes - because recipes are loaded from datapacks, so while datapacks are being discovered there are no recipes yet.

I understand. Then I will just go making available only the main block recipe, for example stone into stone vertical slab, but no stone into stone brick vertical slab. Anyway it's just for stonecutting, so it's important to at least have the vertical slab of the block you're using. Then it's not that important to have also all the variants mostly because normal slab variants will be available and so you can just craft them into the desired vertical slab with a single extra step. I'll consider this a small price to pay for scalability.

I have now a few things left to do for this mod:

  • Add in-world recipes for waxing. I have already added crafting waxing recipes and they correctly depend on the waxing crafting recipes of normal slabs. I would like to do the same but with the in-world waxing recipe, that is right-clicking with an honeycomb to wax a vertical slab that mimics a block that has a slab that can be in-world waxed. I would go with listening to the right-click block event and do my logic there, but I saw on Forge docs that there is a way to reuse my recipe logic to create a in-world recipe. So if there's a way to do that I'd kindly ask some suggestions on how to do that, otherwise I will just subscribe to the event and that's it.
  • Integrate my recipes with Minecraft Vanilla recipes book. I don't know what to do for this to be honest.
  • Fix a problem I found out when placing a vertical slab that's supposed to emit light: when first placed the texture of the vertical slab looks like it's light up however the vertical slab is not emitting light at all, indeed the light level is 0 all around. When placing another block next to it the vertical slab starts to emit light correctly, same goes on with reloading the world. I saw that VerticalSlabBlock#getLightEmission is called several times when placing/updating the vertical slab, many times with 0 and many times with the correct light level. I don't know how to fix this. This can be tested changing the return value of VerticalSlabBlock#getLightEmission to 15 when referringBlockState != null.
  • Make so that jumpFactor and speedFactor come from the mimicked block. But this we said that can't be changed unless Forge adds hooks for them, however I was thinking that maybe there's a way to override Entity#getBlockJumpFactor and Entity#getBlockSpeedFactor so that if a vertical slab is involved they pass to my VerticalSlabBlock the level and position that allow to mimic the correct block?

P.S. would you like to be listed as co-author or in credits (your choice)? Because none of this would have been possible without you so I would like to give you the credit you deserve, of course only if you want ot.

Link to comment
Share on other sites

4 hours ago, diesieben07 said:

Look at HoneycombItem.getWaxed, it should let you wax arbitrary block states, if possible. To handle this on your block, use PlayerInteractEvent.RightClickBlock.

Perfect, done!

4 hours ago, diesieben07 said:

The light value really must depend on the BlockState, not the BlockEntity, otherwise you will have strange effects. So you probably need to make a property on your block that sets the light level.

I tried to do this and I failed...

I changed VerticalSlabBlock#getLightEmission to:

@Override
  public int getLightEmission(BlockState state, BlockGetter getter, BlockPos pos) {
    BlockState referringBlockState = VerticalSlabUtils.getReferringBlockState(getter, pos);
    if (referringBlockState != null) {
      int lightLevel = 15;
      // int lightLevel = referringBlockState.getLightEmission(getter, pos);
      state.setValue(LEVEL, Integer.valueOf(lightLevel));
      return lightLevel;
    }
    return super.getLightEmission(state, getter, pos);
  }

and I added the LEVEL property initialization like so:

public VerticalSlabBlock() {
    super(
      BlockBehaviour.Properties.of(Material.AIR)
      .isValidSpawn((state, getter, pos, entityType) -> false)
      .isRedstoneConductor((state, getter, pos) -> false)
      .isSuffocating((state, getter, pos) -> false)
      .lightLevel(LightBlock.LIGHT_EMISSION)
    );
    this.registerDefaultState(this.defaultBlockState().setValue(FACING, Direction.NORTH).setValue(SHAPE, StairsShape.STRAIGHT).setValue(WATERLOGGED, Boolean.valueOf(false)).setValue(LEVEL, Integer.valueOf(0)));
  }

and LEVEL is just BlockStateProperties#LEVEL.

However nothing changed, I have the same problem. Also I would like that the item too has the correct light emission, so that it works with shaders that add dynamic lighting.

5 hours ago, diesieben07 said:

This could be done fairly easily with a forge hook that allows you to access the position. Feel free to make a pull request :)

And I would really love to do that, if only I knew how 🤣 I will try to look up how other hooks are implemented to see if I can do it.

Do you have any insight about the recipe book?

I'm sorry for asking so many questions every time, but it's really my first mod this complex and I'm discovering many things as I go, and know I'm really grateful because I couldn't have done without your help. I added you in the mod credits and I can't wait to finally publish this mod!

Link to comment
Share on other sites

2 hours ago, diesieben07 said:

That's not what I meant. Do not override getLightEmission and especially do not modify the level in there. You are calling getReferringBlockState there, which will access your BlockEntity. Instead you need to set the correct level when the block is placed (from your BlockItem). Then when creating your BlockBehaviour.Properties use the BlockBehaviour.Properties#lightLevel to teach the game how to go from BlockState to light level.

Okay, I updated my code as follows and now it works! I also removed my override of getLightEmission.

@Override
public BlockState getStateForPlacement(BlockPlaceContext placeContext) {
  BlockPos pos = placeContext.getClickedPos();
  Level level = placeContext.getLevel();
  BlockState blockstate = this.defaultBlockState().setValue(FACING, placeContext.getHorizontalDirection()).setValue(WATERLOGGED, Boolean.valueOf(level.getFluidState(pos).getType() == Fluids.WATER));
  BlockState referringBlockState = VerticalSlabUtils.getReferringBlockState(placeContext.getItemInHand());
  if (referringBlockState != null) {
    blockstate = blockstate.setValue(LEVEL, Integer.valueOf(referringBlockState.getLightEmission(level, pos)));
  }
  return blockstate.setValue(SHAPE, getStairsShape(blockstate, level, pos));
}
Link to comment
Share on other sites

4 minutes ago, diesieben07 said:

Why the unnecessary boxing?

Honestly I have no idea, I saw around in Minecraft code that whenever BlockState#setValue was used to set some primitive property the value was always wrapped like that, so I assumed there was a reason for that.

9 minutes ago, diesieben07 said:

Also, this can crash the game, as you are calling methods on the referringBlockState with a position it is not actually placed at.

Then what should I do? Should I call the deprecated BlockStateBase#getLightEmission?

Link to comment
Share on other sites

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.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.




  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • Hello I cannot resize my minecraft anymore when I try it does only crash. I don't know what is happening with this game I'm using Forge 1.20.1-47.2.17 and here's the link of the latest log https://paste.ee/p/y00ed Edit: I decided to create a new world and it crashed too so I got this other link https://paste.ee/p/RZdaD  
    • Wait guys, I still don't don't understand... I shall send my code -- Define the function to decrease hunger local function decreaseHunger(player)     local currentHunger = player.getHunger()          -- Check if hunger is not already zero     if currentHunger > 0 then         player.setHunger(currentHunger - 1)     end end -- Register event to decrease hunger over time script.registerEvent(EntityPlayer, function(player)     while true do         -- Decrease hunger every few seconds         decreaseHunger(player)         script.sleep(1000) -- Sleep for 1 second     end end)     Any help appriecated. It's been a while since I learnt Javascript or whatever Minecraft is made with... ... ... ... Nicknotname Hungerinpeaceful xx
    • good days  i get this error: java.lang.ClassCastException: class net.minecraft.client.player.LocalPlayer cannot be cast to class net.minecraftforge.common.extensions.IForgeServerPlayer (net.minecraft.client.player.LocalPlayer is in module minecraft@1.20.4 of loader 'TRANSFORMER' @3e8b3b79; net.minecraftforge.common.extensions.IForgeServerPlayer is in module forge@49.0.26 of loader 'TRANSFORMER' @3e8b3b79)     at mercmod.blocks.classes.panel_whit_entity_inside.use(panel_whit_entity_inside.java:576) ~[main/:?] {re:classloading}     at net.minecraft.world.level.block.state.BlockBehaviour$BlockStateBase.use(BlockBehaviour.java:826) ~[forge-1.20.4-49.0.26_mapped_official_1.20.4-recomp.jar:?] {re:classloading}     at net.minecraft.client.multiplayer.MultiPlayerGameMode.performUseItemOn(MultiPlayerGameMode.java:324) ~[forge-1.20.4-49.0.26_mapped_official_1.20.4-recomp.jar:?] {re:classloading,pl:runtimedistcleaner:A}     at net.minecraft.client.multiplayer.MultiPlayerGameMode.lambda$useItemOn$4(MultiPlayerGameMode.java:292) ~[forge-1.20.4-49.0.26_mapped_official_1.20.4-recomp.jar:?] {re:classloading,pl:runtimedistcleaner:A}     at net.minecraft.client.multiplayer.MultiPlayerGameMode.startPrediction(MultiPlayerGameMode.java:251) ~[forge-1.20.4-49.0.26_mapped_official_1.20.4-recomp.jar:?] {re:classloading,pl:runtimedistcleaner:A}     at net.minecraft.client.multiplayer.MultiPlayerGameMode.useItemOn(MultiPlayerGameMode.java:291) ~[forge-1.20.4-49.0.26_mapped_official_1.20.4-recomp.jar:?] {re:classloading,pl:runtimedistcleaner:A}     at net.minecraft.client.Minecraft.startUseItem(Minecraft.java:1799) ~[forge-1.20.4-49.0.26_mapped_official_1.20.4-recomp.jar:?] {re:classloading,pl:accesstransformer:B,pl:runtimedistcleaner:A}     at net.minecraft.client.Minecraft.handleKeybinds(Minecraft.java:2083) ~[forge-1.20.4-49.0.26_mapped_official_1.20.4-recomp.jar:?] {re:classloading,pl:accesstransformer:B,pl:runtimedistcleaner:A}     at net.minecraft.client.Minecraft.tick(Minecraft.java:1902) ~[forge-1.20.4-49.0.26_mapped_official_1.20.4-recomp.jar:?] {re:classloading,pl:accesstransformer:B,pl:runtimedistcleaner:A}     at net.minecraft.client.Minecraft.runTick(Minecraft.java:1216) ~[forge-1.20.4-49.0.26_mapped_official_1.20.4-recomp.jar:?] {re:classloading,pl:accesstransformer:B,pl:runtimedistcleaner:A}     at net.minecraft.client.Minecraft.run(Minecraft.java:801) ~[forge-1.20.4-49.0.26_mapped_official_1.20.4-recomp.jar:?] {re:classloading,pl:accesstransformer:B,pl:runtimedistcleaner:A}     at net.minecraft.client.main.Main.main(Main.java:234) ~[forge-1.20.4-49.0.26_mapped_official_1.20.4-recomp.jar:?] {re:classloading,pl:runtimedistcleaner:A} ------------------------------------------------------------------------------------------------------------------   context:  im updating the briefcase from mi mod an i need to make a gui whit this image that gonna be launched from a block and from a item soo im using kapenjoe tutorials  ---------------------------------------------------------------------------------------- The problem is that tutorial is for 1.20.1 and im using 1.20.4 and i really dont get how to call and open the gui from the block nor from the item i made this based on post i made short ago everything seems find but when right click the block to open the gui it crash the game  @Override public InteractionResult use(BlockState blkstate, Level warudo, BlockPos pos, Player pe, InteractionHand hand, BlockHitResult hitresult) {     if(warudo.isClientSide()){         BlockEntity blkentity = warudo.getBlockEntity(pos);         if(blkentity instanceof Panel_BlockEntity){             //   ↓↓↓  open the menu from the block              IForgeServerPlayer ifpe = (IForgeServerPlayer)pe; //<--- this is wrong  "class net.minecraft.client.player.LocalPlayer cannot be cast to class net.minecraftforge.common.extensions.IForgeServerPlayer"             ifpe.openMenu( (Panel_BlockEntity) blkentity, pos ); //<-- i need an example of how open a gui in 1.20.4         }     } return InteractionResult.sidedSuccess(warudo.isClientSide()); } Theres nothing highlighted in red in the code like everything where right an possible but dont works  ####################################################################################################### i need to see an example, just the piece of code for the use() method of the block whit the block entity for 1.20.4  and the same  but for the item     @Override     public @NotNull InteractionResultHolder<ItemStack> use(@NotNull Level warudo, @NotNull Player pe, @NotNull InteractionHand interactionHand)     {         if (!warudo.isClientSide())         {             ItemStack heldItem = pe.getItemInHand(interactionHand);                          if (heldItem.getCapability(ForgeCapabilities.ITEM_HANDLER).isPresent())             {                 //   ↓↓↓  open the menu from the item                 NetworkHooks.openScreen((ServerPlayer) pe, this);             }         }         return super.use(warudo, pe, interactionHand);     }   thanks for your attention           
    • It depends on the data you want to save, but generally speaking you should use some custom Player Capabilities, attach to the Player and use them to store/retrieve any kind of data you want. You can find how to make them work for 1.20.x in the documentation here: https://docs.minecraftforge.net/en/latest/datastorage/capabilities/
  • Topics

×
×
  • Create New...

Important Information

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