Jump to content
Search In
  • More options...
Find results that contain...
Find results in...

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


X-Lomir
 Share

Recommended Posts

Which maps are you talking about?

5 minutes ago, X-Lomir said:

Also I'm wondering why on the Forge docs it's written to use a separate class when creating a simple channel object. Wouldn't it be okay if I did it in my JustVerticalSlabsLoader?

Just a matter of code style. You can put it wherever you want, really.

Link to comment
Share on other sites

  • Replies 146
  • Created
  • Last Reply

Top Posters In This Topic

10 minutes ago, diesieben07 said:

Which maps are you talking about?

The volatile immutable slab maps used to go from block to slab and vice versa, the ones instantiated in the ServerAboutToStart event.

Link to comment
Share on other sites

Just now, diesieben07 said:

Why do you need them on the client?

Because I actually use them also to render. I had changed my code to make so that my vertical slabs refer the slab and not the block to fix some issues, however this caused some problems in rendering. So I needed the maps during rendering to go from slab to block whenever possible, and also I have a map holding all the translucent slabs.

Link to comment
Share on other sites

So the server will have them after ServerAboutToStart event fired, after on the client the RecipesUpdated event will fire and there I can send a request to the server to get the values for my maps. Correct?

Link to comment
Share on other sites

Okay, now it works without crashing and without major inconveniences. However I found two issues:

  1. Vertical slabs don't show up in the creative inventory, neither the dedicated tab nor the search tab.
  2. In the stonecutter menu the recipes are there but they are not visible, resulting in a shift of the visual recipes compared to the actual output recipe.

They most probably originate from the same problem, that is when I compute the maps I use ForgeRegistries.ITEMS.tags().getTag(ItemTags.SLABS), but this returns null. This is because in ForgeRegistryTagManager I can see the field tags being a map with size 0, client side.

I'm guessing it's because Minecraft registries, and the same Forge registries, are only server side. However I'm not sure what to do to get the Item list of slabs client side.

Link to comment
Share on other sites

However if I compute my maps during that event I won't have access to the recipe manager anymore, am I right?

Also, other than the recipe manager, I need my maps to be computed before I do

MutableSearchTree<ItemStack> creativeSearchTree = Minecraft.getInstance().getSearchTree(SearchRegistry.CREATIVE_NAMES);
for(BlockState referredSlabState : VerticalSlabUtils.slabStateMap.values()) {
  creativeSearchTree.add(VerticalSlabUtils.getVerticalSlabItem(referredSlabState, VerticalSlabUtils.isTranslucent(referredSlabState)));
}
creativeSearchTree.refresh();

during the RecipesUpdateEvent.

Link to comment
Share on other sites

  • 2 weeks later...

I'm actually working on a similar kind of mod, that adds vertical slabs. I made them so they can connect like stairs.

GitHub Branch

Now I have a "Connectable_Oak_Leaf_Wall" that get's registered in my ModBuildingBlocks Class.

The Forge Documentation on tinting is pretty minimalistic: https://mcforge.readthedocs.io/en/1.18.x/resources/client/models/tinting/#blockcoloritemcolor

So I call this in my Main:

unknown.png

Why does it not work? I'm obviously doing something very wrong since you wrote like 30 lines to change the color. But since your Half Slabs probably are getting created in a very different way I thought maybe you could help?

Link to comment
Share on other sites

11 minutes ago, Brainterminator said:

I'm actually working on a similar kind of mod, that adds vertical slabs. I made them so they can connect like stairs.

GitHub Branch

Now I have a "Connectable_Oak_Leaf_Wall" that get's registered in my ModBuildingBlocks Class.

The Forge Documentation on tinting is pretty minimalistic: https://mcforge.readthedocs.io/en/1.18.x/resources/client/models/tinting/#blockcoloritemcolor

So I call this in my Main:

unknown.png

Why does it not work? I'm obviously doing something very wrong since you wrote like 30 lines to change the color. But since your Half Slabs probably are getting created in a very different way I thought maybe you could help?

That register method takes as first parameter a instance of BlockColor, you are passing it an int (FoliageColor#getDefaultColor returns an int).

A BlockColor instance is quite easy as BlockColor is just an interface with a single method:

public interface BlockColor {
   int getColor(BlockState p_92567_, @Nullable BlockAndTintGetter p_92568_, @Nullable BlockPos p_92569_, int p_92570_);
}

Since you basically want to copy the coloring foliage gets, you can do something similar to me:

@SubscribeEvent
public static void onColorHandlerEventBlock(ColorHandlerEvent.Block event) {
  event.getBlockColors().register(
    new BlockColor() {
      public int getColor(BlockState state, @Nullable BlockAndTintGetter getter, @Nullable BlockPos pos, int tintIndex) {
        return getter != null && pos != null ? event.getBlockColors().getColor(Blocks.OAK_LEAVES.defaultBlockState(), getter, pos, tintIndex) : -1;
      }
    },
    ModBuildingBlocks.CONNECTABLE_OAK_LEAF_WALL
  );
}

Where you basically create a new BlockColor instance that returns the same coloring for oak leaves.

Alternatively you can also create a separate class implementing BlockColor:

public class ConnectableOakLeafWallBlockColor implements BlockColor {
  public int getColor(BlockState state, @Nullable BlockAndTintGetter getter, @Nullable BlockPos pos, int tintIndex) {
    return getter != null && pos != null ? Minecraft.getInstance().getBlockColors().getColor(Blocks.OAK_LEAVES.defaultBlockState(), getter, pos, tintIndex) : -1;
  }
}

And just pass it to register:

@SubscribeEvent
public static void onColorHandlerEventBlock(ColorHandlerEvent.Block event) {
  event.getBlockColors().register(new ConnectableOakLeafWallBlockColor(), ModBuildingBlocks.CONNECTABLE_OAK_LEAF_WALL);
}

The choice is yours.

 

Also be careful because you need to avoid having this part of the code when your mod runs only server-side or it will just crash being unable to find BlockColor (which indeed exists only client-side). I fixed this problem by having a separate class handling my color registering annotated like so (be careful to make your methods static if you do the same):

@EventBusSubscriber(value = Dist.CLIENT, bus = Bus.MOD)
public class ColorHandlerEventHandler {

  @SubscribeEvent
  public static void onColorHandlerEventBlock(ColorHandlerEvent.Block event) {
    // do stuff
  }

  @SubscribeEvent
  public static void onColorHandlerEventItem(ColorHandlerEvent.Item event) {
    // do stuff
  }
}

I speak from experience 🤣

I hope I was of help, if you need to look more at my code the most updated branch is this one.

Link to comment
Share on other sites

Posted (edited)
2 hours ago, X-Lomir said:

That register method takes as first parameter a instance of BlockColor, you are passing it an int (FoliageColor#getDefaultColor returns an int).

A BlockColor instance is quite easy as BlockColor is just an interface with a single method:

public interface BlockColor {
   int getColor(BlockState p_92567_, @Nullable BlockAndTintGetter p_92568_, @Nullable BlockPos p_92569_, int p_92570_);
}

Since you basically want to copy the coloring foliage gets, you can do something similar to me:

@SubscribeEvent
public static void onColorHandlerEventBlock(ColorHandlerEvent.Block event) {
  event.getBlockColors().register(
    new BlockColor() {
      public int getColor(BlockState state, @Nullable BlockAndTintGetter getter, @Nullable BlockPos pos, int tintIndex) {
        return getter != null && pos != null ? event.getBlockColors().getColor(Blocks.OAK_LEAVES.defaultBlockState(), getter, pos, tintIndex) : -1;
      }
    },
    ModBuildingBlocks.CONNECTABLE_OAK_LEAF_WALL
  );
}

Where you basically create a new BlockColor instance that returns the same coloring for oak leaves.

Alternatively you can also create a separate class implementing BlockColor:

public class ConnectableOakLeafWallBlockColor implements BlockColor {
  public int getColor(BlockState state, @Nullable BlockAndTintGetter getter, @Nullable BlockPos pos, int tintIndex) {
    return getter != null && pos != null ? Minecraft.getInstance().getBlockColors().getColor(Blocks.OAK_LEAVES.defaultBlockState(), getter, pos, tintIndex) : -1;
  }
}

And just pass it to register:

@SubscribeEvent
public static void onColorHandlerEventBlock(ColorHandlerEvent.Block event) {
  event.getBlockColors().register(new ConnectableOakLeafWallBlockColor(), ModBuildingBlocks.CONNECTABLE_OAK_LEAF_WALL);
}

The choice is yours.

 

Also be careful because you need to avoid having this part of the code when your mod runs only server-side or it will just crash being unable to find BlockColor (which indeed exists only client-side). I fixed this problem by having a separate class handling my color registering annotated like so (be careful to make your methods static if you do the same):

@EventBusSubscriber(value = Dist.CLIENT, bus = Bus.MOD)
public class ColorHandlerEventHandler {

  @SubscribeEvent
  public static void onColorHandlerEventBlock(ColorHandlerEvent.Block event) {
    // do stuff
  }

  @SubscribeEvent
  public static void onColorHandlerEventItem(ColorHandlerEvent.Item event) {
    // do stuff
  }
}

I speak from experience 🤣

I hope I was of help, if you need to look more at my code the most updated branch is this one.

First of all, thank you so much for your quick and detailed reply!!!

A lot of things got a lot clearer to me but my blocks still are rendered in gray. I don't get why and when I wanted to look into your code I realized there are no tinted blocks in your mod.

At least I couldn't find any leaves slabs or grass slabs so I'm still a little lost but your answer was by far the best I got in the last 2 weeks I spent trying to find a solution to this problem

 

p.s.: I just realised I'm stupid and I forgot to call it in the Main Constructor ..... sigh

Edited by Brainterminator
Link to comment
Share on other sites

2 hours ago, Brainterminator said:

I don't get why and when I wanted to look into your code I realized there are no tinted blocks in your mod.

Yeah I know, this is because Minecraft Vanilla doesn't have slabs of such materials, so in Vanilla no vertical slabs will have a need for coloring. However if you try my mod in combination with another mod that adds horizontal slabs of grass or foliage or whatever other block that needs coloring you will see that the vertical slabs will appear and will be tinted correctly :)

I'm glad you could figure out the other problem anyway

Link to comment
Share on other sites

56 minutes ago, X-Lomir said:

Yeah I know, this is because Minecraft Vanilla doesn't have slabs of such materials, so in Vanilla no vertical slabs will have a need for coloring. However if you try my mod in combination with another mod that adds horizontal slabs of grass or foliage or whatever other block that needs coloring you will see that the vertical slabs will appear and will be tinted correctly :)

I'm glad you could figure out the other problem anyway

In case of the Server How did you workaround that? I heard you can use the DistExecutor but lambda expression still overwhelm me so... I'm having problems with that.

My Constructor looks like this:

public UtilityBlocks()
    {
        IEventBus eventBus = FMLJavaModLoadingContext.get().getModEventBus();

        ModItems.register(eventBus);
        ModBlocks.register(eventBus);
        ModBuildingBlocks.register(eventBus);

        eventBus.addListener(this::setup);
        eventBus.addListener(this::clientSetup);

        
        eventBus.addListener(this::registerBlockColors);
        eventBus.addListener(this::registerItemColors);

        // Register ourselves for server and other game events we are interested in
        MinecraftForge.EVENT_BUS.register(this);
    }

But as you already said this way it is not runnable on a server. Do you know an easy way to outsource that?

Link to comment
Share on other sites

13 minutes ago, Brainterminator said:

In case of the Server How did you workaround that? I heard you can use the DistExecutor but lambda expression still overwhelm me so... I'm having problems with that.

My Constructor looks like this:

public UtilityBlocks()
    {
        IEventBus eventBus = FMLJavaModLoadingContext.get().getModEventBus();

        ModItems.register(eventBus);
        ModBlocks.register(eventBus);
        ModBuildingBlocks.register(eventBus);

        eventBus.addListener(this::setup);
        eventBus.addListener(this::clientSetup);

        
        eventBus.addListener(this::registerBlockColors);
        eventBus.addListener(this::registerItemColors);

        // Register ourselves for server and other game events we are interested in
        MinecraftForge.EVENT_BUS.register(this);
    }

But as you already said this way it is not runnable on a server. Do you know an easy way to outsource that?

As I said in my first reply, what I did was to directly subscribe a class that would handle the color events:

@EventBusSubscriber(value = Dist.CLIENT, bus = Bus.MOD)
public class ColorHandlerEventHandler {

  @SubscribeEvent
  public static void onColorHandlerEventBlock(ColorHandlerEvent.Block event) {
    // do stuff
  }

  @SubscribeEvent
  public static void onColorHandlerEventItem(ColorHandlerEvent.Item event) {
    // do stuff
  }
}

The important parts here are the Annotation for the class and the fact that its methods are static. If you do this you should also remove the lines in your constructor where you add those methods as listeners on the bus.

So, to sum it up, remove the lines that register registerBlockColors and registerItemColors, move those two methods into a separate class and make them static, annotate the class with @EventBusSubscriber(value = Dist.CLIENT, bus = Bus.MOD) and you're good to go.

Link to comment
Share on other sites

4 minutes ago, X-Lomir said:

As I said in my first reply, what I did was to directly subscribe a class that would handle the color events:

@EventBusSubscriber(value = Dist.CLIENT, bus = Bus.MOD)
public class ColorHandlerEventHandler {

  @SubscribeEvent
  public static void onColorHandlerEventBlock(ColorHandlerEvent.Block event) {
    // do stuff
  }

  @SubscribeEvent
  public static void onColorHandlerEventItem(ColorHandlerEvent.Item event) {
    // do stuff
  }
}

The important parts here are the Annotation for the class and the fact that its methods are static. If you do this you should also remove the lines in your constructor where you add those methods as listeners on the bus.

So, to sum it up, remove the lines that register registerBlockColors and registerItemColors, move those two methods into a separate class and make them static, annotate the class with @EventBusSubscriber(value = Dist.CLIENT, bus = Bus.MOD) and you're good to go.

I tried replicating your steps with my mod:

package net.brain.utilityblocks.handler;

import net.brain.utilityblocks.block.ModBuildingBlocks;
import net.minecraft.client.renderer.BiomeColors;
import net.minecraft.world.level.FoliageColor;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus;
import net.minecraft.client.color.item.ItemColor;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.client.event.ColorHandlerEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.api.distmarker.Dist;

@EventBusSubscriber(value = Dist.CLIENT, bus = Bus.MOD)
public class ColorHandlerEventHandler {
    @SubscribeEvent
    public void registerBlockColors(ColorHandlerEvent.Block event){
        event.getBlockColors().register((p_92626_, p_92627_, p_92628_, p_92629_) -> {
            return p_92627_ != null && p_92628_ != null ? BiomeColors.getAverageFoliageColor(p_92627_, p_92628_) : FoliageColor.getDefaultColor();
        }, ModBuildingBlocks.CONNECTABLE_OAK_LEAF_WALL.get());
    }

    @SubscribeEvent
    public void registerItemColors(ColorHandlerEvent.Item event){
        event.getItemColors().register(new ItemColor() {
            @Override
            public int getColor(ItemStack pStack, int pTintIndex) {
                return FoliageColor.getDefaultColor();
            }
        }, ModBuildingBlocks.CONNECTABLE_OAK_LEAF_WALL.get());
    }
}

but somehow it doesn't work. When I declare the registerBlockColors function in my Main Class and call "eventBus.addListener(this::registerBlockColors);" from the Constructor it works. 

 

Link to comment
Share on other sites

1 minute ago, Brainterminator said:

I tried replicating your steps with my mod:

package net.brain.utilityblocks.handler;

import net.brain.utilityblocks.block.ModBuildingBlocks;
import net.minecraft.client.renderer.BiomeColors;
import net.minecraft.world.level.FoliageColor;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus;
import net.minecraft.client.color.item.ItemColor;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.client.event.ColorHandlerEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.api.distmarker.Dist;

@EventBusSubscriber(value = Dist.CLIENT, bus = Bus.MOD)
public class ColorHandlerEventHandler {
    @SubscribeEvent
    public void registerBlockColors(ColorHandlerEvent.Block event){
        event.getBlockColors().register((p_92626_, p_92627_, p_92628_, p_92629_) -> {
            return p_92627_ != null && p_92628_ != null ? BiomeColors.getAverageFoliageColor(p_92627_, p_92628_) : FoliageColor.getDefaultColor();
        }, ModBuildingBlocks.CONNECTABLE_OAK_LEAF_WALL.get());
    }

    @SubscribeEvent
    public void registerItemColors(ColorHandlerEvent.Item event){
        event.getItemColors().register(new ItemColor() {
            @Override
            public int getColor(ItemStack pStack, int pTintIndex) {
                return FoliageColor.getDefaultColor();
            }
        }, ModBuildingBlocks.CONNECTABLE_OAK_LEAF_WALL.get());
    }
}

but somehow it doesn't work. When I declare the registerBlockColors function in my Main Class and call "eventBus.addListener(this::registerBlockColors);" from the Constructor it works. 

 

I told you, the methods need to be static. This is because with the annotation you're subscribing the class, not an instance of it.

Quote from @diesieben07: "@EventBusSubscriber registers the class to the event bus, as such your event handler methods must be static."

Quotes from me: "be careful to make your methods static if you do the same", "The important parts here are the Annotation for the class and the fact that its methods are static", "move those two methods into a separate class and make them static".

Link to comment
Share on other sites

Posted (edited)
30 minutes ago, X-Lomir said:

I told you, the methods need to be static. This is because with the annotation you're subscribing the class, not an instance of it.

Quote from @diesieben07: "@EventBusSubscriber registers the class to the event bus, as such your event handler methods must be static."

Quotes from me: "be careful to make your methods static if you do the same", "The important parts here are the Annotation for the class and the fact that its methods are static", "move those two methods into a separate class and make them static".

First of all thank you for all your help I have been stuck with this for a while! 
Okay so the @EventBusSubscriber makes it so I don't have to call the method, Forge automatically does?

 

EDIT: THANK YOU SO MUCH NOW EVERYTHING WORKS FINE!
YOU'RE A GOD DAMN LEGEND

Edited by Brainterminator
Fixed it!
Link to comment
Share on other sites

11 minutes ago, Brainterminator said:

First of all thank you for all your help I have been stuck with this for a while! 
Okay so the @EventBusSubscriber makes it so I don't have to call the method, Forge automatically does?

Yep. Instead of telling Forge a specific method needs to be called upon a certain event (which is what you were doing in the constructor) you're telling Forge to register that class (not instance, so only the static fields) to the event bus. Then, by adding @SubscribeEvent and the type of the event as parameter to the methods Forge will know that it has to call them upon the specified event.

By the way if you think about it you're never calling any method you register to the event listeners (this::methodName passed the method as parameter, doesn't call it), you're instead passing them as parameter so that Forge knows what to call and when. The key difference is that in the constructor you're registering the methods to the event bus by calling IEventBus#addListener, instead with the separate class you're using annotations to do it.

I am glad I was of help and I hope this explanation was helpful too.

Link to comment
Share on other sites

28 minutes ago, X-Lomir said:

Yep. Instead of telling Forge a specific method needs to be called upon a certain event (which is what you were doing in the constructor) you're telling Forge to register that class (not instance, so only the static fields) to the event bus. Then, by adding @SubscribeEvent and the type of the event as parameter to the methods Forge will know that it has to call them upon the specified event.

By the way if you think about it you're never calling any method you register to the event listeners (this::methodName passed the method as parameter, doesn't call it), you're instead passing them as parameter so that Forge knows what to call and when. The key difference is that in the constructor you're registering the methods to the event bus by calling IEventBus#addListener, instead with the separate class you're using annotations to do it.

I am glad I was of help and I hope this explanation was helpful too.

It definitely was! Thank you so much again! It was also really helpful to get it explained for once! I was also on a discord and when I asked them about it they were like "just use lambda", " if you don't even understand this don't even bother making a mc mod"

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

 Share




×
×
  • Create New...

Important Information

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