Jump to content

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


Recommended Posts

Posted (edited)
10 minutes ago, diesieben07 said:

I don't know. Perfectly mimicking other blocks becomes more and more impossible the further you go. Using the bare getLightEmissinon is probably your best bet.

What if I were to wrap

referringBlockState.getLightEmission(level, pos)

with a try & catch, catching any Exception that may arise, and if I get an exception I call the bare getLightEmission instead?

Something like this basically:

try {
	blockstate = blockstate.setValue(LEVEL, referringBlockState.getLightEmission(level, pos));
} catch (Exception e) {
	blockstate = blockstate.setValue(LEVEL, referringBlockState.getLightEmission());
}

To have my mod to its best to get as accurate as possible.

Edited by X-Lomir
Added snippet
  • Replies 92
  • Created
  • Last Reply

Top Posters In This Topic

Posted
2 minutes ago, diesieben07 said:

But you can try it, but at least log the exception as a warning once per block state, if it happens. Swallowing exceptions completely is never a good idea.

Sure thing, I need to add some logs here and there throughout my code, I will start from here.

Posted (edited)

I was testing my mod with other mods and it works fine, I have a few notes on some points I'd like to improve it in the future, but for now it's just fine.

Apart from one thing: texture overlays.

I was testing my mod with SlabGen and I noticed the vertical slab grass block had 2 issues with its texture:

  1. Grass color was grayscale
  2. Side overlays were not showing

For point 1 I found out that grass color depends on the biome and biome blend setting, while the sprite is in grayscale. The same thing goes on for foliage. I would like to get the correct tint index, but I don't know how to do that. It seemed to me that grass/foliage color is based on the biome and position the block is being rendered, however I can't find a way to get both info in my baked model. I'm not even sure getting them in the baked model is correct, since I would then cache the color.

It would be nice to find a way to take advantage of whatever Minecraft does to set the correct color simply by adding vertical slabs to blocks that need grass/foliage color and then get the biome color if the referring block is instanceof GrassBlock or LeavesBlock, otherwise get the default sprite color.

For point 2 I remembered we had said that in my baked model I'd settle with taking the first referring sprite for the given direction. So I changed my getQuads code to take into account all BakedQuads for the given direction:

@Override
public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction side, @Nonnull Random rand, @Nonnull IModelData modelData) {
  BlockState referringBlockState = modelData.getData(VerticalSlabUtils.REFERRING_BLOCK_STATE);
  if (referringBlockState != null) {
    VerticalSlabModelKey verticalSlabModelKey = new VerticalSlabModelKey(side, referringBlockState);
    if (!bakedQuadsCache.containsKey(verticalSlabModelKey)) {
      ArrayList<BakedQuad> bakedQuads = new ArrayList<BakedQuad>();
      for (BakedQuad jsonBakedQuad : jsonBakedModel.getQuads(state, side, rand, modelData)) {
        Direction orientation = jsonBakedQuad.getDirection();
        for (BakedQuad referringBakedQuad : getReferringBakedQuads(referringBlockState, orientation, rand, modelData)) {
          bakedQuads.add(getNewBakedQuad(jsonBakedQuad, referringBakedQuad.getSprite(), orientation));
          JustVerticalSlabsLoader.LOGGER.warn(side + " " + referringBakedQuad.getSprite().toString()); // Added only for tests. To be removed.
        }
      }
      bakedQuadsCache.put(verticalSlabModelKey, bakedQuads);
    }
    return bakedQuadsCache.get(verticalSlabModelKey);
  }
  return jsonBakedModel.getQuads(state, side, rand, modelData);
}

private List<BakedQuad> getReferringBakedQuads(BlockState referringBlockState, Direction side, Random rand, IModelData modelData) {
  BakedModel referringBakedModel = getReferringBakedModel(referringBlockState);
  IModelData referringModelData = getReferringModelData(referringBlockState, modelData);
  List<BakedQuad> referringBakedQuads = referringBakedModel.getQuads(referringBlockState, side, rand, referringModelData);
  for (BakedQuad referringBakedQuad : referringBakedModel.getQuads(referringBlockState, null, rand, referringModelData)) {
    if (referringBakedQuad.getDirection() == side) {
      referringBakedQuads.add(referringBakedQuad);
    }
  }
  if (referringBakedQuads.size() == 0) {
    JustVerticalSlabsLoader.LOGGER.warn("Referred Block has no texture for " + side + " face. No texture will be generated for that face.");
  }
  return referringBakedQuads;
}

// Updates vertices as I had explained in a previous post.
private BakedQuad getNewBakedQuad(BakedQuad jsonBakedQuad, TextureAtlasSprite referringSprite, Direction orientation) {
    return new BakedQuad(updateVertices(jsonBakedQuad.getVertices(), jsonBakedQuad.getSprite(), referringSprite), jsonBakedQuad.getTintIndex(), orientation, referringSprite, jsonBakedQuad.isShade());
  }

And it still worked fine for all Vanilla blocks and more, however with grass now I get the overlay on the side, which is in grayscale (see point 1), but the background is black:

https://imgur.com/a/BLoHmQx

However the sprites, and so the vertices, are correct:

[11:59:19] [Worker-Main-34/WARN]: down TextureAtlasSprite{name='minecraft:block/dirt', frameCount=1, x=192, y=416, height=16, width=16, u0=0.1875, u1=0.203125, v0=0.40625, v1=0.421875}
[11:59:19] [Worker-Main-34/WARN]: up TextureAtlasSprite{name='minecraft:block/grass_block_top', frameCount=1, x=512, y=272, height=16, width=16, u0=0.5, u1=0.515625, v0=0.265625, v1=0.28125}
[11:59:19] [Worker-Main-34/WARN]: south TextureAtlasSprite{name='minecraft:block/grass_block_side', frameCount=1, x=512, y=224, height=16, width=16, u0=0.5, u1=0.515625, v0=0.21875, v1=0.234375}
[11:59:19] [Worker-Main-34/WARN]: south TextureAtlasSprite{name='minecraft:block/grass_block_side_overlay', frameCount=1, x=512, y=240, height=16, width=16, u0=0.5, u1=0.515625, v0=0.234375, v1=0.25}
[11:59:19] [Worker-Main-34/WARN]: west TextureAtlasSprite{name='minecraft:block/grass_block_side', frameCount=1, x=512, y=224, height=16, width=16, u0=0.5, u1=0.515625, v0=0.21875, v1=0.234375}
[11:59:19] [Worker-Main-34/WARN]: west TextureAtlasSprite{name='minecraft:block/grass_block_side_overlay', frameCount=1, x=512, y=240, height=16, width=16, u0=0.5, u1=0.515625, v0=0.234375, v1=0.25}
[11:59:19] [Worker-Main-34/WARN]: east TextureAtlasSprite{name='minecraft:block/grass_block_side', frameCount=1, x=512, y=224, height=16, width=16, u0=0.5, u1=0.515625, v0=0.21875, v1=0.234375}
[11:59:19] [Worker-Main-34/WARN]: east TextureAtlasSprite{name='minecraft:block/grass_block_side_overlay', frameCount=1, x=512, y=240, height=16, width=16, u0=0.5, u1=0.515625, v0=0.234375, v1=0.25}
[11:59:19] [Worker-Main-34/WARN]: null TextureAtlasSprite{name='minecraft:block/grass_block_side', frameCount=1, x=512, y=224, height=16, width=16, u0=0.5, u1=0.515625, v0=0.21875, v1=0.234375}
[11:59:19] [Worker-Main-34/WARN]: null TextureAtlasSprite{name='minecraft:block/grass_block_side_overlay', frameCount=1, x=512, y=240, height=16, width=16, u0=0.5, u1=0.515625, v0=0.234375, v1=0.25}

My guess is that the wrong RenderType is being used for my vertical slabs and I should use CUTOUT rather than SOLID. But I'm not sure how to do that and if it's correct.

EDIT: This is what I get when I print out MinecraftForgeClient.getRenderType().toString() in my baked model:

RenderType[solid:CompositeState[[texture[Optional[minecraft:textures/atlas/blocks.png](blur=false, mipmap=true)], shader[Optional[net.minecraft.client.renderer.RenderStateShard$$Lambda$4781/0x0000000800bcc238@3d572b5f]], no_transparency, depth_test[<=], cull[true], lightmap[true], overlay[false], no_layering, main_target, default_texturing, write_mask_state[writeColor=true, writeDepth=true], line_width[1.0]], outlineProperty=affects_outline]]

So yes it is SOLID and maybe CUTOUT is not needed but just a way to set that overlay property to true? Also I think a similar problem will arise when vertical slabs will need to mimic blocks that have transparency, but I couldn't find any mod that adds transparent slabs to test it out.

Edited by X-Lomir
Added extra info.
Posted (edited)

I managed to solve the overlay texture problem by doing this:

public class FMLClientSetupEventHandler {
  /**
   * Sets the correct {@link RenderType} for {@link crystalspider.justverticalslabs.blocks.VerticalSlabBlock VerticalSlabBlocks}.
   * 
   * @param event
   */
  @SubscribeEvent
  public void setup(FMLClientSetupEvent event) {
    event.enqueueWork(() -> {
      // TODO: Check if it works also for translucent blocks.
      ItemBlockRenderTypes.setRenderLayer(JustVerticalSlabsLoader.VERTICAL_SLAB_BLOCK.get(), RenderType.cutoutMipped());
    });
  }
}

I also tried to solve the color problem by doing this:

public class ColorHandlerEventHandler {
  @SubscribeEvent
  public void onColorHandlerEventBlock(ColorHandlerEvent.Block event) {
    event.getBlockColors().register(new BlockColor() {
      public int getColor(BlockState state, @Nullable BlockAndTintGetter getter, @Nullable BlockPos pos, int tintIndex) {
        if (getter != null && pos != null) {
          BlockState referredSlabState = VerticalSlabUtils.getReferredSlabState(getter, pos);
          if (referredSlabState != null) {
            Block slab = referredSlabState.getBlock();
            if (slab instanceof GrassBlock || slab instanceof LeavesBlock) {
              return event.getBlockColors().getColor(Block.byItem(VerticalSlabUtils.slabMap.get(slab.asItem())).defaultBlockState(), getter, pos, tintIndex);
            }
          }
        }
        return -1;
      }
    }, JustVerticalSlabsLoader.VERTICAL_SLAB_BLOCK.get());
  }

  @SubscribeEvent
  public void onColorHandlerEventItem(ColorHandlerEvent.Item event) {
    event.getItemColors().register(new ItemColor() {
      public int getColor(ItemStack itemStack, int tintIndex) {
        BlockState referredSlabState = VerticalSlabUtils.getReferredSlabState(itemStack);
        if (referredSlabState != null) {
          Block slab = referredSlabState.getBlock();
          if (slab instanceof GrassBlock || slab instanceof LeavesBlock) {
            return event.getItemColors().getColor(VerticalSlabUtils.slabMap.get(slab.asItem()).getDefaultInstance(), tintIndex);
          }
        }
        return -1;
      }
    }, JustVerticalSlabsLoader.VERTICAL_SLAB_ITEM.get());
  }
}

And I tried registering this class first with

FMLJavaModLoadingContext.get().getModEventBus().register(new ColorHandlerEventHandler());

and after with 

MinecraftForge.EVENT_BUS.register(new ColorHandlerEventHandler());

however either way I get this (and no errors):

https://imgur.com/a/M9sfwVw

EDIT: I forgot to add that I moved the code in this new branch.

Edited by X-Lomir
Added extra info.
Posted

In the end it was just me being stupid. What you said I had already did by using the tint index of the referred sprite rather than of my model JSON.

The real problem was that I was checking instanceof on the slab, but I needed to check on the block.

I changed my code to this and it works:

public class ColorHandlerEventHandler {
  @SubscribeEvent
  public void onColorHandlerEventBlock(ColorHandlerEvent.Block event) {
    event.getBlockColors().register(new BlockColor() {
      public int getColor(BlockState state, @Nullable BlockAndTintGetter getter, @Nullable BlockPos pos, int tintIndex) {
        if (getter != null && pos != null) {
          BlockState referredSlabState = VerticalSlabUtils.getReferredSlabState(getter, pos);
          if (referredSlabState != null) {
            // Edited here
            Block block = Block.byItem(VerticalSlabUtils.slabMap.get(referredSlabState.getBlock().asItem()));
            if (block instanceof GrassBlock || block instanceof LeavesBlock) {
              return event.getBlockColors().getColor(block.defaultBlockState(), getter, pos, tintIndex);
            }
          }
        }
        return -1;
      }
    }, JustVerticalSlabsLoader.VERTICAL_SLAB_BLOCK.get());
  }

  @SubscribeEvent
  public void onColorHandlerEventItem(ColorHandlerEvent.Item event) {
    event.getItemColors().register(new ItemColor() {
      public int getColor(ItemStack itemStack, int tintIndex) {
        BlockState referredSlabState = VerticalSlabUtils.getReferredSlabState(itemStack);
        if (referredSlabState != null) {
          // Edited here
          Block block = Block.byItem(VerticalSlabUtils.slabMap.get(referredSlabState.getBlock().asItem()));
          if (block instanceof GrassBlock || block instanceof LeavesBlock) {
            return event.getItemColors().getColor(block.asItem().getDefaultInstance(), tintIndex);
          }
        }
        return -1;
      }
    }, JustVerticalSlabsLoader.VERTICAL_SLAB_ITEM.get());
  }
}

The correct bus seems to be FMLJavaModLoadingContext.get().getModEventBus().

Posted
38 minutes ago, diesieben07 said:

Why do you even have that instanceof check? You can just call BlockColors / ItemColors#getColor directly for all blocks. Not just grass and leaves can be colored.

Yeah you're right. I thought I would break colors of normal blocks if I did that, I don't know why.

Posted

I noticed that some mods can add slabs for blocks that are not full height, like soulsand and dirt path. That caused a couple of problems with my vertical slabs, however I managed to make so that vertical slabs can also be not full height and their textures adapt too.

There is one problem that arose which is that vertical slabs not full height still make other blocks cull their faces: https://imgur.com/a/ZBUpszZ

I don't know why this is happening nor how to solve this. You can see the textures and the shape of the vertical slab being correct, however it's as if it's considered as a full height block.

Code is here.

Posted

Okay, thanks, it works.

I'm testing my mod with transparent slabs, like glass, and, as I expected given I set the render layer as cutout mipped, transparent blocks don't work well at all.

Is there a way to set the render layer depending on the block entity? Or what else could I do to make so that only vertical slabs referring transparent blocks are rendered in translucent render layer?

Posted
2 hours ago, diesieben07 said:

No, RenderType can only be set on the Block level (not even BlockState). You'll have to make multiple blocks, one for each RenderType you want to support.

However then you still need a hardcoded map that stores which Block maps to which RenderType, because this information is normally not available server side.

Okay, I figured out as much. I guess I will make another kind of block and use it for certain blocks on a best-effort base, like checking if the ID of the mimicked block contains "glass", "leaf", "leaves", or something like that. Maybe add a config option to add another keyword to use to render a specific kind of block with the translucent layer.

Posted (edited)

I added a class for transparent vertical slabs. Everything works fine so far except one thing that is transparent vertical slabs still cull their faces and the faces of their neighboring blocks.

Is there some method or property I can override in my new class or do I necessarily have to duplicate my JSON models and remove the cullfaces?

EDIT: I found getOcclusionShape and it looks like it's working.

Edited by X-Lomir
Added found solution.
Posted

It was pointed out to me a bug my mod has when using shaders other than Minecraft defaults, for example with Optifine internal shaders and BSL Shaders.

The bug consists in all textures, apart from the oak planks, for my vertical slabs to be completely messed up. I guess this has to do with how I'm forcefully writing in the vertexes of my model.

Is there a way to test and debug my mod with Optifine and, optionally, an external shader pack?

Do you have any suggestions or insights about what may be causing the problem and how to fix it? I'm also trying to ask for some help on Minecraft Shaders Discord, I'll update here if I find a solution.

Posted

I managed to solve the issue. The fix was actually quite simple, and I'm also not exactly sure why it works, but it does and I'm satisfied with it 🤣

I think it works because the BLOCK VertexFormat has, in order, a position element, a color element and the 2 UVs elements. A position elements uses 3 vertex elements (x, y and z), a color element takes 1 vertex element, U and V elements take 1 vertex element each. So the index for U is 3 + 1 + vertexIndex, where vertexIndex is the index of the current vertex, which increases every iteration by the size each vertex takes in the vertices array. Similarly, the index for V is 3 + 1 + vertexIndex + 1, accounting for the previous U vertex element.

However I'm not sure this is the correct explanation, but I'll leave here my intuition and the fixed method to update vertices:

private int[] updateVertices(int[] vertices, int[] referredVertices, TextureAtlasSprite oldSprite, TextureAtlasSprite newSprite, boolean faceUp) {
  int[] updatedVertices = vertices.clone();
  for (int vertexIndex = 0; vertexIndex < DefaultVertexFormat.BLOCK.getVertexSize(); vertexIndex += DefaultVertexFormat.BLOCK.getIntegerSize()) {
    float y = Float.intBitsToFloat(referredVertices[vertexIndex + 1]);
    // Lower only top face since RenderType CutoutMipped will remove extra transparent texture bits that go out of the shape. 
    if (faceUp && y > 0 && y < 0.5) {
      updatedVertices[vertexIndex + 1] = Float.floatToRawIntBits(y + 0.5F);
    }
    updatedVertices[vertexIndex + 4] = changeUVertexSprite(oldSprite, newSprite, updatedVertices[vertexIndex + 4]);
    updatedVertices[vertexIndex + 5] = changeVVertexSprite(oldSprite, newSprite, updatedVertices[vertexIndex + 5]);
  }
  return updatedVertices;
}

 

Posted

I'm wondering... Couldn't I put a property into my BlockState that will hold the referredBlockState?

Basically in VerticalSlabBlock#getStateForPlacement I could read the NBT tag from the itemstack and save it in my BlockState, the same way I do for light. This way I could get rid of BEs altogether and make better use of BlockState chaching system. Also it would allow my blocks to be pushed by pistons and to mimic properties I currently cannot mimic because their getter only takes a BlockState as input.

The only problem I can see would be how to handle VerticalSlabBakedModel#getQuads because of that thing with the breaking progress animation.

Posted

I'm trying to put my mod on a server and I get this error:

-- Head --
Thread: main
Stacktrace:
        at cpw.mods.cl.ModuleClassLoader.loadClass(ModuleClassLoader.java:138) ~[securejarhandler-1.0.3.jar:?] {}
-- MOD justverticalslabs --
Details:
        Caused by 0: java.lang.reflect.InvocationTargetException
                at jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[?:?] {}
                at jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77) ~[?:?] {}
                at jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[?:?] {}
                at java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499) ~[?:?] {}
                at java.lang.reflect.Constructor.newInstance(Constructor.java:480) ~[?:?] {}
                at net.minecraftforge.fml.javafmlmod.FMLModContainer.constructMod(FMLModContainer.java:67) ~[javafmllanguage-1.18.2-40.1.20.jar%23126!/:?] {}
                at net.minecraftforge.fml.ModContainer.lambda$buildTransitionHandler$4(ModContainer.java:106) ~[fmlcore-1.18.2-40.1.20.jar%23125!/:?] {}
                at java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1804) ~[?:?] {}
                at java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1796) ~[?:?] {}
                at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373) ~[?:?] {}
                at java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1182) ~[?:?] {}
                at java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1655) ~[?:?] {re:computing_frames}
                at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1622) ~[?:?] {re:computing_frames}
                at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165) ~[?:?] {}

        Caused by 1: java.lang.NoClassDefFoundError: net/minecraft/client/color/item/ItemColor
                at crystalspider.justverticalslabs.JustVerticalSlabsLoader.<init>(JustVerticalSlabsLoader.java:159) ~[justverticalslabs-1.18.2-3.0.0.0.jar%2390!/:1.18.2-3.0.0.0] {re:classloading}
                at jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[?:?] {}
                at jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77) ~[?:?] {}
                at jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[?:?] {}
                at java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499) ~[?:?] {}
                at java.lang.reflect.Constructor.newInstance(Constructor.java:480) ~[?:?] {}
                at net.minecraftforge.fml.javafmlmod.FMLModContainer.constructMod(FMLModContainer.java:67) ~[javafmllanguage-1.18.2-40.1.20.jar%23126!/:?] {}
                at net.minecraftforge.fml.ModContainer.lambda$buildTransitionHandler$4(ModContainer.java:106) ~[fmlcore-1.18.2-40.1.20.jar%23125!/:?] {}
                at java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1804) ~[?:?] {}
                at java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1796) ~[?:?] {}
                at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373) ~[?:?] {}
                at java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1182) ~[?:?] {}
                at java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1655) ~[?:?] {re:computing_frames}
                at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1622) ~[?:?] {re:computing_frames}
                at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165) ~[?:?] {}

        Mod File: justverticalslabs-1.18.2-3.0.0.0.jar
        Failure message: Just Vertical Slabs (justverticalslabs) has failed to load correctly
                java.lang.reflect.InvocationTargetException: null
        Mod Version: 1.18.2-3.0.0.0
        Mod Issue URL: https://github.com/Nyphet/just-vertical-slabs/issues
        Exception message: java.lang.ClassNotFoundException: net.minecraft.client.color.item.ItemColor
Stacktrace:
        at cpw.mods.cl.ModuleClassLoader.loadClass(ModuleClassLoader.java:138) ~[securejarhandler-1.0.3.jar:?] {}
        at java.lang.ClassLoader.loadClass(ClassLoader.java:520) ~[?:?] {}
        at crystalspider.justverticalslabs.JustVerticalSlabsLoader.<init>(JustVerticalSlabsLoader.java:159) ~[justverticalslabs-1.18.2-3.0.0.0.jar%2390!/:1.18.2-3.0.0.0] {re:classloading}
        at jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[?:?] {}
        at jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77) ~[?:?] {}
        at jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[?:?] {}
        at java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499) ~[?:?] {}
        at java.lang.reflect.Constructor.newInstance(Constructor.java:480) ~[?:?] {}
        at net.minecraftforge.fml.javafmlmod.FMLModContainer.constructMod(FMLModContainer.java:67) ~[javafmllanguage-1.18.2-40.1.20.jar%23126!/:?] {}
        at net.minecraftforge.fml.ModContainer.lambda$buildTransitionHandler$4(ModContainer.java:106) ~[fmlcore-1.18.2-40.1.20.jar%23125!/:?] {}
        at java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1804) ~[?:?] {}
        at java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1796) ~[?:?] {}
        at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373) ~[?:?] {}
        at java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1182) ~[?:?] {}
        at java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1655) ~[?:?] {re:computing_frames}
        at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1622) ~[?:?] {re:computing_frames}
        at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165) ~[?:?] {}

How do I fix this? Should I put a @OnlyIn(Dist.CLIENT) in my color event handler class? Or add a check for the side in the methods that register BlockColors and ItemColors? Or something else entirely?

Posted
38 minutes ago, diesieben07 said:

Do not use @OnlyIn.

You should use a separate @EventBusSubscriber with the Dist.CLIENT argument for a client-only event handler and do all your client only event handling there.

I did this:

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

And removed the manual subscribing in my mod loader class.

However now in single player my grass vertical slabs are back to being greyscale.

Posted

Here the code of the handler class.

It is not updated with the annotation, however all I did in local was to add the annotation as shown before and remove the line to register that class in my mod loader class.

Before this change it would crash when server side only, but would color correctly in single player.

After this change it's not coloring correctly in single player. I didn't check if it fixed the crash server side.

Posted

Thanks, it works now!

I found another problem unfortunately: when the version of the mod that's on the client tries to access the volatile immutable maps the game crashes. The issue is that those maps are never instantiated on the client.

How do I fix this? Do I have to register a packet and every time I want to access those maps I must send and receive a packet? Or is there a smarter way?

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?

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

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

Posted

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?

Posted

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.

Posted

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.

  • 2 weeks later...
Posted

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?

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

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.

Announcements




×
×
  • Create New...

Important Information

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