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


  • Posts

  • Joined

  • Last visited

Posts posted by Anrza

  1. 19 hours ago, Luis_ST said:

    you need to create your own FoliagePlacer, then use BiomeLoadingEvent to loop through the Features, check if the Feature is an TreeFeature then replace the original FoliagePlacer with Reflection or use an AT

    Thanks for your reply! I have done it using data generation now. It works well for world generation, and I have realised that I don't care about saplings, so it works well.

    For anyone looking to do something similar, I include files to show what I did. They are just a bit hacky and incomplete now, but they can probably easily be generalised.


    import static net.anju.larus.data.ResourceLocationUtil.prefix;
    import static net.anju.larus.data.ResourceLocationUtil.slash;
    public abstract class LarusFeatureProvider<T> implements DataProvider {
        private static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
        protected final Set<FeatureEntry> featureEntries = new HashSet<>();
        private final DataGenerator generator;
        protected LarusFeatureProvider(DataGenerator generator, Registry<T> registry) {
            this.generator = generator;
            for (Map.Entry<ResourceKey<T>, T> entry : registry.entrySet()) {
                ResourceKey<T> key = entry.getKey();
                T feature = entry.getValue();
                JsonElement jsonElement = getJson(feature);
                if (jsonElement != null) {
                    FeatureEntry featureEntry = new FeatureEntry(prefix(key.location(), key.getRegistryName()), jsonElement);
        private void addFeatures() {
            for (FeatureEntry featureEntry : featureEntries) {
        protected abstract void modifyFeatureEntry(FeatureEntry featureEntry);
        public void run(HashCache cache) {
            writeFiles(cache, this.generator.getOutputFolder(), featureEntries);
        // Actually write out the tables in the output folder
        private static void writeFiles(HashCache cache, Path outputFolder, Set<FeatureEntry> featureEntries) {
            for (FeatureEntry featureEntry : featureEntries) {
                if (featureEntry.isModified()) {
                    ResourceLocation key = featureEntry.resourceLocation;
                    JsonElement jsonElement = featureEntry.jsonElement;
                    Path path = getPath(outputFolder, key);
                    try {
                        DataProvider.save(GSON, cache, jsonElement, path);
                    } catch (IOException e) {
                        LarusMod.LOGGER.error("Couldn't write data {}", key, e);
        public String getName() {
            return "Larus features";
        public JsonElement getJson(T feature) {
            if (feature instanceof ConfiguredFeature<?, ?> configuredFeature) {
                Optional<JsonElement> optional = ConfiguredFeature.DIRECT_CODEC.encodeStart(JsonOps.INSTANCE, configuredFeature).result();
                return optional.orElse(null);
            return null;
        public static Path getPath(Path outputFolder, ResourceLocation key) {
            return outputFolder.resolve("data/" + slash(key) + ".json");
        protected static class FeatureEntry {
            boolean modified = false;
            private final ResourceLocation resourceLocation;
            private final JsonElement jsonElement;
            private FeatureEntry(ResourceLocation resourceLocation, JsonElement jsonElement) {
                this.resourceLocation = resourceLocation;
                this.jsonElement = jsonElement;
            public JsonElement getJsonElement() {
                return jsonElement;
            protected void markModified() {
                this.modified = true;
            private boolean isModified() {
                return this.modified;
    public class LarusLeavesFeatureProvider extends LarusFeatureProvider<ConfiguredFeature<?, ?>> {
        public LarusLeavesFeatureProvider(DataGenerator generator) {
            super(generator, BuiltinRegistries.CONFIGURED_FEATURE);
        protected void modifyFeatureEntry(FeatureEntry featureEntry) {
            JsonElement jsonElement = featureEntry.getJsonElement();
            if (jsonElement.isJsonObject()) {
                JsonObject jsonObject = jsonElement.getAsJsonObject();
                if (jsonObject.has("config")) {
                    JsonObject config = jsonObject.get("config").getAsJsonObject();
                    if (config.has("foliage_provider")) {
                        JsonObject mcFoliage = config.get("foliage_provider").getAsJsonObject();
                        JsonObject larusFoliage = larusFoliage(mcFoliage);
                        if (larusFoliage != null) {
                            config.add("foliage_provider", larusFoliage);
        protected JsonObject larusFoliage(JsonObject mcFoliage) {
            JsonElement type = mcFoliage.get("type");
            if (type.getAsString().equals("minecraft:simple_state_provider")) {
                JsonObject state = mcFoliage.get("state").getAsJsonObject();
                // Change leaves
                String mcLeaves = state.get("Name").getAsString();
                LarusLeavesBlock larusLeavesBlock = larusLeavesBlock(mcLeaves);
                if (larusLeavesBlock == null || larusLeavesBlock.getRegistryName() == null)
                    return null;
                String larusLeaves = larusLeavesBlock.getRegistryName().toString();
                state.add("Name", new JsonPrimitive(larusLeaves));
                // Transform properties
                JsonObject mcProperties = state.get("Properties").getAsJsonObject();
                transformProperties(mcProperties, larusLeavesBlock);
            } else {
                LarusMod.LOGGER.warn("Unexpected type for MC foliage " + mcFoliage);
            return mcFoliage;
        protected void transformProperties(JsonObject properties, LarusLeavesBlock larusLeavesBlock) {
            if (larusLeavesBlock instanceof LarusDeciduousLeavesBlock)
                properties.add("leafy", new JsonPrimitive("green"));
        protected LarusLeavesBlock larusLeavesBlock(String mcLeaves) {
            switch (mcLeaves) {
                case "minecraft:oak_leaves" -> {
                    return LarusBlocks.OAK_LEAVES.get();
                case "minecraft:birch_leaves" -> {
                    return LarusBlocks.BIRCH_LEAVES.get();
                case "minecraft:acacia_leaves" -> {
                    return LarusBlocks.ACACIA_LEAVES.get();
                case "minecraft:jungle_leaves" -> {
                    return LarusBlocks.JUNGLE_LEAVES.get();
                case "minecraft:dark_oak_leaves" -> {
                    return LarusBlocks.DARK_OAK_LEAVES.get();
                case "minecraft:spruce_leaves" -> {
                    return LarusBlocks.SPRUCE_LEAVES.get();
            return null;


  2. Hello!

    I want to replace the leaves on trees that generate in the world with other blocks. One way I've found to do this is to manually create files in data/minecraft/worldgen/configured_feature that correspond to vanilla's features and replacing the block in "foliage_provider" with the one I want. Doing it manually works well, but it's not convenient to do at a large scale, and obviously has other drawbacks.

    I'd ideally use the data generator system, like I do for recipes and loot tables. Unfortunately, there is no built in support for specifically world generation, like there is for recipes and loot tables. I could create the json files if I managed to convert the features to JsonElements, but I don't know how I would do that.

    To summarise, I have one objective:

    • Replacing leaves on trees placed in the world by
      • world generation and
      • saplings.

    But I would gladly receive help on how to generate json files of ConfiguredFeatures such as Features.OAK.

    Thanks in advance!

  3. Hello! I want to add tilted ears to some animals. Under ideal circumstances, I'd just modelPart.zRot = t. However, the rotation around the z axis is applied before the other rotations, which makes a big mess of everything.

    As far as I can tell, transformation of the matrix M represented by the PoseStack to the result R depending on the rotation matrices X, Y, Z and the translation matrix P is applied as such: R=XYZPM. My solution prior to 1.17 was to extend ModelRenderer and apply the transformations in the order ZXYPM - solving the problem very simply. But since ModelPart - the 1.17 counterpart to the old ModelRenderer - is final, I see no such simple solution.

    I see two possible approaches:

    1. Denote T=XYZP. Apply T^-1(ZXYP) to the PoseStack before I ModelPart#render. The ModelPart will then apply T, resulting in my desired ZXYP  transformation. No problem in theory, but quite convoluted in practice.
    2. Consider Z(z)X(x)Y(y). Do some linear algebra and trigonometry to find out (s,t,u) such that X(s)Y(t)Z(u)=Z(z)X(x)Y(y), then ModelPart#setRotation(s,t,u). I haven't done the math on this, so I'm not sure how feasible this is.

    Anyone got a simpler solution? Or should I rather do a feature request to make ModelPart not final?

  4. Hello! I am looking to have a global time variable that ticks up with the normal gameTick but skips forward when you use the time command or skip the night by sleeping, in order to simulate the time that has passed. Quite confident with the logical part of it, but I have a few questions regarding how to save/sync and properly update every tick:

    1. What is the proper way to run code every tick? I've looked at listening for TickEvent or ClientTickEvent and ServerTickEvent. Would it be sensible to use them?
    2. How do I save global data on the server? The documentation is somewhat useful, but it's outdated and I haven't found a way to save global data.
    3. How do I conveniently send time data from server to client? Should I use my own packets? Can I include the numbers when sending initial world data to the client?
    4. Can I count on World#getGameTime and World#getDayTime to stay in sync between server and client? If so, I should be able to just execute the same code every tick on both client and server to keep them in sync.
    5. Can I count on World#getGameTime and World#getDayTime to be equal across different worlds on the server? From my testing, they seem to be the same regardless of which world you call the method from.

    Thanks in advance for any answers to my questions!

  5. 35 minutes ago, Draco18s said:

    Flowing water is not a water source block. As such, it has no source, and evaporates.

    Thanks for the information, however, it doesn't help me with my problem. What do you mean by "evaporates"?


    To be clear: the issue I have is not that it disappears gradually, as flowing water usually behaves in MC. As I said, "it does not flow". It simply disappears after about half a second.


    Flowing water can exist in the world without a source block. If you want to see what I mean, create a 1x2x1 space in the world and place water in the upper of those blocks, that is then a source block, while the block below becomes flowing water. Then remove the source block, and the flowing water block below will not disappear instantly, but gradually.


    I'm looking to create such a block, that

    - is not a "source block"

    - does not disappear instantly, but gradually

  6. Hey!


    I'm trying to place flowing water in the world. I do this:

            world.setBlockState(pos, Fluids.FLOWING_WATER.getDefaultState().getBlockState());

    I can place it, and it appears. Using World#getFluidState and World#getBlockState indicates that it has worked.

    It seems to happen on both the client and the server side.


    However, it does not flow and disappears almost instantly after being placed.

    Interestingly, it behaves as expected if I place two blocks next to each other rapidly.


    Can anyone help me?

  7. Hello!

    I'm rendering a model with a TileEntityRenderer. The rendering works fine apart from the texture, which looks like all textures in MC pasted together. I've tried to use TextureManager#bindTexture(ResourceLocation), but it doesn't affect the rendering at all.


    package net.anju.larus.renderer;
    import com.mojang.blaze3d.matrix.MatrixStack;
    import com.mojang.blaze3d.vertex.IVertexBuilder;
    import net.anju.larus.LarusMod;
    import net.anju.larus.block.CrucibleBlock;
    import net.anju.larus.block.LarusBlocks;
    import net.anju.larus.tileentity.CrucibleTileEntity;
    import net.minecraft.block.BlockState;
    import net.minecraft.client.Minecraft;
    import net.minecraft.client.renderer.BlockRendererDispatcher;
    import net.minecraft.client.renderer.IRenderTypeBuffer;
    import net.minecraft.client.renderer.RenderType;
    import net.minecraft.client.renderer.model.ModelRenderer;
    import net.minecraft.client.renderer.tileentity.TileEntityRenderer;
    import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher;
    import net.minecraft.util.ResourceLocation;
    import net.minecraft.util.math.vector.Vector3f;
    import net.minecraft.world.World;
    import net.minecraftforge.api.distmarker.Dist;
    import net.minecraftforge.api.distmarker.OnlyIn;
    import javax.annotation.ParametersAreNonnullByDefault;
    public class CrucibleTER extends TileEntityRenderer<CrucibleTileEntity> {
        private final CrucibleModel crucible;
        private final ResourceLocation resourceLocation = new ResourceLocation(LarusMod.MODID, "textures/block/crucible/crucible.png");
        public CrucibleTER(TileEntityRendererDispatcher rendererDispatcherIn) {
            this.crucible = new CrucibleModel();
        public void render(CrucibleTileEntity tileEntityIn, float partialTicks, MatrixStack matrixStackIn, IRenderTypeBuffer bufferIn, int combinedLightIn, int combinedOverlayIn) {
            World world = tileEntityIn.getWorld();
            boolean flag = world != null;
            BlockState blockState = flag ? tileEntityIn.getBlockState() : LarusBlocks.CRUCIBLE.get().getDefaultState();
            if (blockState.get(CrucibleBlock.TILTING)) {
                float rotation = blockState.get(CrucibleBlock.FACING).getHorizontalAngle();
                boolean warm = blockState.get(CrucibleBlock.WARM);
                matrixStackIn.translate(0.5D, 0.5D, 0.5D);
                matrixStackIn.rotate(Vector3f.XP.rotationDegrees(50F * tileEntityIn.getCrucibleAngle(partialTicks)));
                matrixStackIn.translate(-0.5D, -0.5D, -0.5D);
                IVertexBuilder iVertexBuilder = bufferIn.getBuffer(RenderType.getSolid());
                /*float angle = tileEntityIn.getCrucibleAngle(partialTicks);
                angle = 1.0F - angle;
                angle = 1.0F - angle * angle * angle;
                angle = -(angle * ((float) Math.PI / 2F));*/
                crucible.render(matrixStackIn, iVertexBuilder, combinedLightIn, combinedOverlayIn, tileEntityIn.getContentLevel());
                matrixStackIn.pop(); //Anrza: you must #push() before you pop, or it will crash
        private class CrucibleModel {
            private final ModelRenderer bottom;
            private final ModelRenderer front;
            private final ModelRenderer back;
            private final ModelRenderer right;
            private final ModelRenderer left;
            private final ModelRenderer level_1;
            private final ModelRenderer level_2;
            private final ModelRenderer level_3;
            public CrucibleModel() {
                int twi = 48;
                int thi = 16;
                this.bottom = new ModelRenderer(twi, thi, 0, 0);
                this.bottom.addBox(2, 3, 2, 12, 1, 12, 0);
                this.front = new ModelRenderer(twi, thi, 0, 0);
                this.front.addBox(2, 4, 12, 12, 12, 2);
                this.back = new ModelRenderer(twi, thi, 0, 0);
                this.back.addBox(2, 4, 2, 12, 12, 2);
                this.left = new ModelRenderer(twi, thi, 0, 0);
                this.left.addBox(2, 4, 4, 2, 12, 8);
                this.right = new ModelRenderer(twi, thi, 0, 0);
                this.right.addBox(12, 4, 4, 2, 12, 8);
                this.level_1 = new ModelRenderer(twi, thi, 0, 0);
                this.level_1.addBox(4, 4, 4, 8, 2, 8);
                this.level_2 = new ModelRenderer(twi, thi, 0, 0);
                this.level_2.addBox(4, 4, 4, 8, 6, 8);
                this.level_3 = new ModelRenderer(twi, thi, 0, 0);
                this.level_3.addBox(4, 4, 4, 8, 11, 8);
            public void render(MatrixStack matrixStackIn, IVertexBuilder bufferIn, int packedLightIn, int packedOverlayIn, int level) {
                this.bottom.render(matrixStackIn, bufferIn, packedLightIn, packedOverlayIn);
                this.front.render(matrixStackIn, bufferIn, packedLightIn, packedOverlayIn);
                this.back.render(matrixStackIn, bufferIn, packedLightIn, packedOverlayIn);
                this.left.render(matrixStackIn, bufferIn, packedLightIn, packedOverlayIn);
                this.right.render(matrixStackIn, bufferIn, packedLightIn, packedOverlayIn);
                switch (level) {
                    case 1: {
                        level_1.render(matrixStackIn, bufferIn, packedLightIn, packedOverlayIn);
                    case 2: {
                        level_2.render(matrixStackIn, bufferIn, packedLightIn, packedOverlayIn);
                    case 3: {
                        level_3.render(matrixStackIn, bufferIn, packedLightIn, packedOverlayIn);


  8. Using a normal TER is a bit clumsy for my purposes. I've found TileEntityRendererAnimation that is supposed to work with the Forge model system and animations. It seems to depend on the block's IModelData having the Properties.AnimationProperty


            IBakedModel model = blockRenderer.getBlockModelShapes().getModel(state);
            IModelData data = model.getModelData(world, pos, state, ModelDataManager.getModelData(te.getWorld(), pos));
            if (data.hasProperty(Properties.AnimationProperty))
              //do animations

    but I haven't been able to find a way to add the AnimationProperty.


    I found an old post describing how to do it a few updates back, but some of the method it depends on no longer exist.

  • Create New...

Important Information

By using this site, you agree to our Privacy Policy.