Jump to content

[SOLVED] [1.15.2] Custom arrow entity not rendering


Blazer Nitrox

Recommended Posts

Entities, Renderers, and Registers, oh my!

 

So the gist of the issue right now is that I've created a custom arrow (ItemDart) that is just an ArrowItem modified to allow for setting the damage an arrows does and what item the arrow should give when picked up by the player (otherwise it would always return an arrow). This then creates a custom AbstractArrowEntity (EntityDart) which takes the reference item and returns it in getArrowStack. Then, to render that entity, I have a custom ArrowRenderer (DartRenderer) which overrides getEntityTexture and returns a ResourceLocation whose path is generated based on the item the EntityDart refers to.

 

Two problems. First, DartRenderer is being constructed as it should when registered, but isn't being called in order to render the EntityDart. Second, EntityDart no longer has a renderer, as it seems not even ArrowRenderer is being called for it.

 

As always, here are the relevant bits of code. Some of this is kind of messy simply because I'm relearning Forge (and its best practices) for the first time, so if something is blatantly wrong, please let me know.

ItemDart

Spoiler

package io.github.tntftw21.agab.item;

import io.github.tntftw21.agab.entity.EntityDart;
import net.minecraft.entity.LivingEntity;
import net.minecraft.item.ArrowItem;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.world.World;
import net.minecraftforge.fml.RegistryObject;

/**
 * A version of ArrowItem which allows for custom damage values as well as
 * having the resultant entity drop its respective dart (i.e. EntityStoneDart can drop a stone
 * instead of a wooden one)
 *
 * @author TNTftw21
 */
public class ItemDart extends ArrowItem {
    
    /**
     * The amount of damage this Dart does by default.
     */
    public final float damage;
    
    private RegistryObject<Item> ref;

    /**
     *
     * @param properties Default Item.Properties
     * @param damageIn Amount of damage this dart deals as base (will be multiplied by projectile velocity later!)
     */
    public ItemDart(Properties properties, float damageIn) {
        super(properties);
        this.damage = damageIn;
    }
    
    /**
     * Set an internal reference to the represented item.
     * This is needed specifically for ensuring that EntityDart drops the correct dart item.
     * @param refIn a RegistryObject for building the Item
     * @returns Reference to this object, for method chaining
     */
    public ItemDart setItemReference(RegistryObject<Item> refIn) {
        this.ref = refIn;
        return this;
    }
    
    /**
     * Create an ArrowEntity representing this Item.
     * @returns the relevant Entity (in this case EntityDart) for use by the firing tool.
     */
    @Override
    public EntityDart createArrow(World worldIn, ItemStack stack, LivingEntity shooter) {
        EntityDart arrowentity = new EntityDart(shooter, worldIn, ref.get());
        arrowentity.setDamage(this.damage);
        return arrowentity;
    }

    /**
     * @returns whether the Dart is infinite and, therefore, whether one should be expended upon firing.
     */
    // We override this method here because the version in ArrowItem *directly* compares against ArrowItem.class, rather than this more flexible check.
    @Override
    public boolean isInfinite(ItemStack stack, ItemStack bow, net.minecraft.entity.player.PlayerEntity player) {
        int enchant = net.minecraft.enchantment.EnchantmentHelper.getEnchantmentLevel(net.minecraft.enchantment.Enchantments.INFINITY, bow);
        return enchant <= 0 ? false : this instanceof ArrowItem;
    }

}

 

EntityDart (I'll do JavaDocs on this once I get it working lol):

Spoiler

package io.github.tntftw21.agab.entity;

import io.github.tntftw21.agab.AGAB;
import io.github.tntftw21.agab.init.ModEntityTypes;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.projectile.AbstractArrowEntity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.world.World;

public class EntityDart extends AbstractArrowEntity {

    private final Item referenceItem;

    @SuppressWarnings("unchecked")
    public EntityDart(EntityType<?> type, World world) {
        super((EntityType<? extends AbstractArrowEntity>) type, world);
        this.referenceItem = null;
    }
    
    public EntityDart(LivingEntity shooter, World world, Item referenceItemIn) {
        super(ModEntityTypes.DART.get(), shooter, world);
        this.referenceItem = referenceItemIn;
    }

    @Override
    public ItemStack getArrowStack() {
        AGAB.LOGGER.debug("Getting Arrow Stack");
        return new ItemStack(this.referenceItem);
    }

}

 

DartRenderer:

Spoiler

package io.github.tntftw21.agab.client.render;

import io.github.tntftw21.agab.AGAB;
import io.github.tntftw21.agab.entity.EntityDart;
import net.minecraft.client.renderer.entity.ArrowRenderer;
import net.minecraft.client.renderer.entity.EntityRendererManager;
import net.minecraft.item.Item;
import net.minecraft.util.ResourceLocation;

/**
 * Custom ArrowRenderer for EntityDart. Designed to be usable with all Dart types.
 * 
 * @author TNTftw21
 */
public class DartRenderer extends ArrowRenderer<EntityDart> {

	public DartRenderer(EntityRendererManager renderManagerIn) {
		super(renderManagerIn);
	}

	@Override
	public ResourceLocation getEntityTexture(EntityDart entity) {
		Item refItem = entity.getArrowStack().getItem();
		return new ResourceLocation(AGAB.MODID, "textures/entity/projectile/" + refItem.getRegistryName().getPath());
	}

}

 

This next few I'm included because the problem might be something to do with how I'm registering everything. To be frank, for the life of me I couldn't find any explanation as to how Entity/Renderer registration is done, so I lifted some code of the Web (Thanks, Cadiboo!) and modified it until it """worked""".

ModItems:

Spoiler

package io.github.tntftw21.agab.init;

import io.github.tntftw21.agab.AGAB;
import io.github.tntftw21.agab.item.ItemAtlatl;
import io.github.tntftw21.agab.item.ItemDart;
import net.minecraft.item.Item;
import net.minecraftforge.fml.RegistryObject;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries;

public class ModItems {
	
	public static final DeferredRegister<Item> ITEMS = new DeferredRegister<>(ForgeRegistries.ITEMS, AGAB.MODID);

	//public static final RegistryObject<Item> EXAMPLE_ITEM = ITEMS.register("example_item", () -> new Item(new Item.Properties().group(ModItemGroups.MOD_ITEM_GROUP)));
	public static final RegistryObject<Item> WOODEN_ATLATL = ITEMS.register("wooden_atlatl", () -> new ItemAtlatl(new Item.Properties().group(ModItemGroups.AGAB_WEAPONS).maxDamage(288), 1.33F));
	public static final RegistryObject<Item> ENHANCED_ATLATL = ITEMS.register("enhanced_atlatl", () -> new ItemAtlatl(new Item.Properties().group(ModItemGroups.AGAB_WEAPONS).maxDamage(288), 1.75F));
	
	public static final RegistryObject<Item> WOODEN_DART = ITEMS.register("wooden_dart", () -> new ItemDart(new Item.Properties().group(ModItemGroups.AGAB_WEAPONS), 1.5F));
	public static final RegistryObject<Item> STONE_DART = ITEMS.register("stone_dart", () -> new ItemDart(new Item.Properties().group(ModItemGroups.AGAB_WEAPONS), 1.75F));
	
}

 

 

ClientModEventSubscriber:

Spoiler

package io.github.tntftw21.agab.client;

import io.github.tntftw21.agab.AGAB;
import io.github.tntftw21.agab.client.render.DartRenderer;
import io.github.tntftw21.agab.init.ModEntityTypes;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.client.registry.RenderingRegistry;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;

@EventBusSubscriber(modid = AGAB.MODID, bus=EventBusSubscriber.Bus.MOD, value=Dist.CLIENT)
public class ClientModEventSubscriber {

	@SubscribeEvent
	public static void onClientSetup(final FMLClientSetupEvent event) {
		RenderingRegistry.registerEntityRenderingHandler(ModEntityTypes.DART.get(), DartRenderer::new);
	}

}

 

 

ModEntityTypes:

Spoiler

package io.github.tntftw21.agab.init;

import io.github.tntftw21.agab.AGAB;
import io.github.tntftw21.agab.entity.EntityDart;
import net.minecraft.entity.EntityClassification;
import net.minecraft.entity.EntityType;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fml.RegistryObject;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries;

public class ModEntityTypes {

	public static final DeferredRegister<EntityType<?>> ENTITY_TYPES = new DeferredRegister<>(ForgeRegistries.ENTITIES, AGAB.MODID);
	
	public static final String DART_NAME = "dart";
	public static final RegistryObject<EntityType<EntityDart>> DART = ENTITY_TYPES.register("dart",
			() -> EntityType.Builder.<EntityDart>create(EntityDart::new, EntityClassification.MISC).size(0.5F, 0.5F).build(new ResourceLocation(AGAB.MODID, DART_NAME).toString()));

}

 

 

I'm not including them, but I am initializing the DeferredRegistries in my main mod file's constructor. Also, I'm calling ItemDart#setItemReference in my main EventSubscriber class's item register event, as according to my testing the deferred register fires before the registry event does. That being said, I have no idea if that a safe assumption to make in the wild, so I intend to test that a big more unless someone can shoot it down right here.

 

As far as I know, everything should be working properly. At the very least, Minecraft seems to know that EntityDart is using a custom renderer, but it's not properly attaching it. I haven't seen any related errors in the logs, however. I'd include debug.log, but Forge insists on generating a 3M dump of every single class it loads and I've yet to find a way to tell it to not.

Edited by Blazer Nitrox
Edit title to match question status
Link to comment
Share on other sites

17 minutes ago, Blazer Nitrox said:

I have no idea if that a safe assumption to make in the wild, so I intend to test that a big more unless someone can shoot it down right here.

You can add a priority to your @SubscribeEvent annotation to ensure you run before/after other listeners.

 

One thing you may have forgotten is that Items are Singletons. I.E. there is only one (the one you register) in the game.

 

Other than that I don’t see any glaring issues, you’ve debugged and noticed that neither your renderer or the vanilla arrow renderer is being called for your entity?

Edited by Cadiboo
  • Like 1

About Me

Spoiler

My Discord - Cadiboo#8887

My WebsiteCadiboo.github.io

My ModsCadiboo.github.io/projects

My TutorialsCadiboo.github.io/tutorials

Versions below 1.14.4 are no longer supported on this forum. Use the latest version to receive support.

When asking support remember to include all relevant log files (logs are found in .minecraft/logs/), code if applicable and screenshots if possible.

Only download mods from trusted sites like CurseForge (minecraft.curseforge.com). A list of bad sites can be found here, with more information available at stopmodreposts.org

Edit your own signature at www.minecraftforge.net/forum/settings/signature/ (Make sure to check its compatibility with the Dark Theme)

Link to comment
Share on other sites

Oh :P Am big dumb. I see what you mean about singletons now, although that doesn't seem to be the cause of my current issue - it's never getting far enough to have undefined behavior. I know for a fact that my DartRenderer isn't being called from debug calls, and stepping through shows that indeed neither is ArrowRenderer. Near as I can figure, I somehow convinced Minecraft that I don't have any renderer registered for the entity, although I don't see anything in the logs that would indicate that.

 

Actually, looking back on it, I'm still treating the items as though they're singletons... where were you seeing behavior that indicates otherwise?

Edited by Blazer Nitrox
Link to comment
Share on other sites

7 hours ago, Blazer Nitrox said:

Actually, looking back on it, I'm still treating the items as though they're singletons... where were you seeing behavior that indicates otherwise?

I was in a hurry when I wrote that and I noticed your setting/getting item refs and thought it could be an issue. I’m not sure what the issue is, can you please post your code on GitHub?

  • Like 1

About Me

Spoiler

My Discord - Cadiboo#8887

My WebsiteCadiboo.github.io

My ModsCadiboo.github.io/projects

My TutorialsCadiboo.github.io/tutorials

Versions below 1.14.4 are no longer supported on this forum. Use the latest version to receive support.

When asking support remember to include all relevant log files (logs are found in .minecraft/logs/), code if applicable and screenshots if possible.

Only download mods from trusted sites like CurseForge (minecraft.curseforge.com). A list of bad sites can be found here, with more information available at stopmodreposts.org

Edit your own signature at www.minecraftforge.net/forum/settings/signature/ (Make sure to check its compatibility with the Dark Theme)

Link to comment
Share on other sites

8 hours ago, Cadiboo said:

I was in a hurry when I wrote that and I noticed your setting/getting item refs and thought it could be an issue. I’m not sure what the issue is, can you please post your code on GitHub?

 

Sure thing :) I think you noticed the same thing I did, but near as I can tell the itemRef should be fine, since I'm instantiating ItemDart multiple times, once for each item instance.

Here's that GitHub repo. I appreciate the help!

Edited by Blazer Nitrox
Link to comment
Share on other sites

So, uh... I think I discovered the problem.

 

In net.minecraft.client.renderer.WorldRenderer#func_228428_a_:

Breakpoint on line 944 shows that this line is never reach where `entity` is an EntityDart, even if one exists in the world. Problem is, the line immediately before it (943):

for(Entity entity : this.world.getAllEntities()) {

 

So... somehow... my EntityDart doesn't exist in the client-side world.

Interesting.

And AFAIK I'm not overriding anything that would be taking care of that - it should be doing it on its own.

 

-----EDIT #2-----

Well, this rabbit hole just goes deeper and deeper. I stumbled across FMLPlayMessages.SpawnEntity, and realize "oh, hey, I can just use this!" And then I realized, "shouldn't this already be called?"

Lo and behold, it's not. I have yet to figure out what should be sending the SpawnEntity packet, but it isn't doing its job - it never trips any of the breakpoints I put in it.

In the mean time, I'm going to attempt to build my own implementation and see if that accomplishes anything at all.

 

-----EDIT #3-----

Well, I finally got something to render. I ended up implementing my own packet specific to my entity and had ItemDart send it when it constructed the entity. It's super dirty (the best I could figure for who to send the packet to was just anybody tracking the chunk the dart was in), but it works... which begs the question of why FMLPlayMessages.SpawnEntity wasn't doing its job. I dunno, but at least I got something to show up on my gorram screen.

Edited by Blazer Nitrox
Link to comment
Share on other sites

Ah. Remove that hack and override getSpawnPacket or whatever it’s called in your entity and return NetworkHooks.getSpawnPacket(this) (I think)

  • Thanks 1

About Me

Spoiler

My Discord - Cadiboo#8887

My WebsiteCadiboo.github.io

My ModsCadiboo.github.io/projects

My TutorialsCadiboo.github.io/tutorials

Versions below 1.14.4 are no longer supported on this forum. Use the latest version to receive support.

When asking support remember to include all relevant log files (logs are found in .minecraft/logs/), code if applicable and screenshots if possible.

Only download mods from trusted sites like CurseForge (minecraft.curseforge.com). A list of bad sites can be found here, with more information available at stopmodreposts.org

Edit your own signature at www.minecraftforge.net/forum/settings/signature/ (Make sure to check its compatibility with the Dark Theme)

Link to comment
Share on other sites

8 hours ago, Cadiboo said:

Ah. Remove that hack and override getSpawnPacket or whatever it’s called in your entity and return NetworkHooks.getSpawnPacket(this) (I think)

Yep, that fixed it. It's NetworkHooks.getEntitySpawningPacket(this), btw.

 

Thanks a bunch for your help. Hopefully something can be done about the official documentation once the API's settled down a bit more.

 

Link to comment
Share on other sites

2 hours ago, Blazer Nitrox said:

Thanks a bunch for your help. Hopefully something can be done about the official documentation once the API's settled down a bit more.

It’s a community project, anyone can contribute. Unfortunately they seem to be pretty slow in accepting PRs, same as in the main Forge repo.

About Me

Spoiler

My Discord - Cadiboo#8887

My WebsiteCadiboo.github.io

My ModsCadiboo.github.io/projects

My TutorialsCadiboo.github.io/tutorials

Versions below 1.14.4 are no longer supported on this forum. Use the latest version to receive support.

When asking support remember to include all relevant log files (logs are found in .minecraft/logs/), code if applicable and screenshots if possible.

Only download mods from trusted sites like CurseForge (minecraft.curseforge.com). A list of bad sites can be found here, with more information available at stopmodreposts.org

Edit your own signature at www.minecraftforge.net/forum/settings/signature/ (Make sure to check its compatibility with the Dark Theme)

Link to comment
Share on other sites

  • 2 months later...

Ah yes XD
Here's my renderer, the line "refitem.getRegistryName().getPath()" return "air" not the name of  the file..

But if i replace this by my file name, it works.

Where is set the "path" attribute ?

public class RenderTrainingArrow extends ArrowRenderer<EntityTrainingArrow>
{

	public RenderTrainingArrow(EntityRendererManager renderManagerIn) {
		super(renderManagerIn);
		// TODO Auto-generated constructor stub
	}
	@Override
	public ResourceLocation getEntityTexture(EntityTrainingArrow entity) 
	{	Item refItem = entity.getArrowStack().getItem();
		return new ResourceLocation(AlduinMod.MOD_ID + ":textures/entity/projectiles/"+ refItem.getRegistryName().getPath());
	}

}
Edited by Delpig
Link to comment
Share on other sites

I change my entity like that

this.referenceItem = itemInit.TRAINING_ARROW.get();

And the renderer like that

return new ResourceLocation(AlduinMod.MOD_ID + ":textures/entity/projectiles/"+ refItem.getRegistryName().getPath()+ ".png");

And it works fine !

Thank you very much :D

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.

Announcements



  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • They were already updated, and just to double check I even did a cleanup and fresh update from that same page. I'm quite sure drivers are not the problem here. 
    • i tried downloading the drivers but it says no AMD graphics hardware has been detected    
    • Update your AMD/ATI drivers - get the drivers from their website - do not update via system  
    • As the title says i keep on crashing on forge 1.20.1 even without any mods downloaded, i have the latest drivers (nvidia) and vanilla minecraft works perfectly fine for me logs: https://pastebin.com/5UR01yG9
    • Hello everyone, I'm making this post to seek help for my modded block, It's a special block called FrozenBlock supposed to take the place of an old block, then after a set amount of ticks, it's supposed to revert its Block State, Entity, data... to the old block like this :  The problem I have is that the system breaks when handling multi blocks (I tried some fix but none of them worked) :  The bug I have identified is that the function "setOldBlockFields" in the item's "setFrozenBlock" function gets called once for the 1st block of multiblock getting frozen (as it should), but gets called a second time BEFORE creating the first FrozenBlock with the data of the 1st block, hence giving the same data to the two FrozenBlock :   Old Block Fields set BlockState : Block{minecraft:black_bed}[facing=east,occupied=false,part=head] BlockEntity : net.minecraft.world.level.block.entity.BedBlockEntity@73681674 BlockEntityData : id:"minecraft:bed",x:3,y:-60,z:-6} Old Block Fields set BlockState : Block{minecraft:black_bed}[facing=east,occupied=false,part=foot] BlockEntity : net.minecraft.world.level.block.entity.BedBlockEntity@6d1aa3da BlockEntityData : {id:"minecraft:bed",x:2,y:-60,z:-6} Frozen Block Entity set BlockState : Block{minecraft:black_bed}[facing=east,occupied=false,part=foot] BlockPos{x=3, y=-60, z=-6} BlockEntity : net.minecraft.world.level.block.entity.BedBlockEntity@6d1aa3da BlockEntityData : {id:"minecraft:bed",x:2,y:-60,z:-6} Frozen Block Entity set BlockState : Block{minecraft:black_bed}[facing=east,occupied=false,part=foot] BlockPos{x=2, y=-60, z=-6} BlockEntity : net.minecraft.world.level.block.entity.BedBlockEntity@6d1aa3da BlockEntityData : {id:"minecraft:bed",x:2,y:-60,z:-6} here is the code inside my custom "freeze" item :    @Override     public @NotNull InteractionResult useOn(@NotNull UseOnContext pContext) {         if (!pContext.getLevel().isClientSide() && pContext.getHand() == InteractionHand.MAIN_HAND) {             BlockPos blockPos = pContext.getClickedPos();             BlockPos secondBlockPos = getMultiblockPos(blockPos, pContext.getLevel().getBlockState(blockPos));             if (secondBlockPos != null) {                 createFrozenBlock(pContext, secondBlockPos);             }             createFrozenBlock(pContext, blockPos);             return InteractionResult.SUCCESS;         }         return super.useOn(pContext);     }     public static void createFrozenBlock(UseOnContext pContext, BlockPos blockPos) {         BlockState oldState = pContext.getLevel().getBlockState(blockPos);         BlockEntity oldBlockEntity = oldState.hasBlockEntity() ? pContext.getLevel().getBlockEntity(blockPos) : null;         CompoundTag oldBlockEntityData = oldState.hasBlockEntity() ? oldBlockEntity.serializeNBT() : null;         if (oldBlockEntity != null) {             pContext.getLevel().removeBlockEntity(blockPos);         }         BlockState FrozenBlock = setFrozenBlock(oldState, oldBlockEntity, oldBlockEntityData);         pContext.getLevel().setBlockAndUpdate(blockPos, FrozenBlock);     }     public static BlockState setFrozenBlock(BlockState blockState, @Nullable BlockEntity blockEntity, @Nullable CompoundTag blockEntityData) {         BlockState FrozenBlock = BlockRegister.FROZEN_BLOCK.get().defaultBlockState();         ((FrozenBlock) FrozenBlock.getBlock()).setOldBlockFields(blockState, blockEntity, blockEntityData);         return FrozenBlock;     }  
  • Topics

×
×
  • Create New...

Important Information

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