Ruins (Structure Spawning System)

Ruins (Structure Spawning System)

26M Downloads

[Infernal Mobs] Item gets deleted when damaging a Sticky Infernal

Closed this issue ยท 16 comments

commented

Versioning
Latests version of Infernal Mobs, minecraft 1.20.1.

What did you do
Install Forgotten Nunchakus mod and Just a lot more Enchantments, use the Lightning Nunchaku item, the item must be enchanted with Curse of Possession , hit a normal mob that is close to a sticky infernal mob. The item instead of being returned to the player, is just removed.

Expected behavior
Normally, when hitting a sticky infernal directly with this same weapon with the same enchantments, it works perfectly.

I have code to provide in hopes you may know why this bug happens since im the mod dev of those mods and i cant just understand why.

This is the class of the bugged item

public class LightningDragonBoneNunchaku extends BaseNunchakuItem {

	public LightningDragonBoneNunchaku(Tier tier, Properties properties, float attackDamage, float attackSpeed) {
		super(tier, properties, attackDamage, attackSpeed);
	}

	@Override
	public boolean hurtEnemy(ItemStack stack, LivingEntity target, LivingEntity source) {
		boolean modloaded = ModList.get().isLoaded("iceandfire");
		boolean flag = super.hurtEnemy(stack, target, source);
		if (source instanceof Player player) {
			// Since i dont know how to apply InF lightning effects i will just apply the
			// default minecraft one
			LightningBolt lightningBolt = EntityType.LIGHTNING_BOLT.create(player.level());
			if (lightningBolt != null) {
				lightningBolt.moveTo(target.getX(), target.getY(), target.getZ());
				lightningBolt.setVisualOnly(true); // This makes it not cause damage/fire.
				player.level().addFreshEntity(lightningBolt);
			}
			applyChainLightning(target, player, modloaded);
			target.knockback(1F, source.getX() - target.getX(), source.getZ() - target.getZ());
		}
		return flag;
	}

	
	public void applyChainLightning(LivingEntity target, Player player, boolean modloaded) {
	    // Maximum number of additional targets in the chain (main target + 4 additional = 5 total)
		
	    final int maxChainCount = 4;
	    // Maximum chain range (in blocks)
	    final double chainRange = 5.0D;
	    List<LivingEntity> chainTargets = new ArrayList<>();
	    chainTargets.add(target);
	    LivingEntity current = target;
	    for (int i = 0; i < maxChainCount; i++) {
	        // Define a search area centered at the current target
	        AABB searchBox = new AABB(
	            current.getX() - chainRange, current.getY() - chainRange, current.getZ() - chainRange,
	            current.getX() + chainRange, current.getY() + chainRange, current.getZ() + chainRange
	        );
	        List<LivingEntity> candidates = player.level().getEntitiesOfClass(LivingEntity.class,
	                searchBox,
	                e -> e != player && e.isAlive() && !chainTargets.contains(e));
	        if (candidates.isEmpty()) {
	            break;
	        }
	        LivingEntity nextTarget = candidates.get(player.getRandom().nextInt(candidates.size()));
	        chainTargets.add(nextTarget);
	        current = nextTarget;
	    }
	    float bonus = 0;
	    ResourceKey<DamageType> LIGHTNING_DAMAGE_KEY = ResourceKey.create(Registries.DAMAGE_TYPE, new ResourceLocation(NunchakusConfig.lightningDamage));
	    Holder.Reference<DamageType> customDamageType = player.level()
                .registryAccess()
                .registryOrThrow(Registries.DAMAGE_TYPE)
                .getHolderOrThrow(LIGHTNING_DAMAGE_KEY);
	    DamageSource lightningDamage = new DamageSource(customDamageType, player);
		int[] hpLosses = {5, 4, 3, 2, 1 };
		int[] slowDurations = {10, 8, 6, 4, 2 };
		// Loop over each extra target and apply the damage and effect
		LivingEntity last = target;
		for (int i = 0; i < chainTargets.size(); i++) {
			LivingEntity extra = chainTargets.get(i);
			if (modloaded) {
				if (extra instanceof EntityFireDragon || extra instanceof EntityIceDragon) {
					bonus = 4;
				} else {
					bonus = 0;
				}
			}
			extra.hurt(lightningDamage, hpLosses[i] + bonus);
			extra.addEffect(
					new MobEffectInstance(NunchakusConfig.lightningMobEffect, slowDurations[i], 4, false, false));
			last = extra;
		}
	}

}
commented

For extra context
This is the class that im hardly debugging to understand why its not working under this specific condition
After debugging, the logs say that the item in main hand is returned normally even when in game is just removed. Which makes total no sense. I checked other methods in hopes that it might be another reason of why this bugs happens. But this is the only place when the item is modified in any way. I even took the same similar code approach than another mod but its just not working.

@SubscribeEvent
	public static void onGemDropped(ItemTossEvent event) {
		Player entity = event.getPlayer();
		ItemStack itemstack = event.getEntity().getItem();
		if (EnchantmentHelper.getItemEnchantmentLevel(JlmeModEnchantments.CURSE_OF_POSSESSION.get(), itemstack) != 0 && !entity.level().isClientSide()) {
			if (entity.getMainHandItem().isEmpty()) {
				JlmeMod.LOGGER.info("Stack dropped is: " + itemstack);
				ItemStack _setstack = itemstack.copy();
				entity.setItemInHand(InteractionHand.MAIN_HAND, _setstack);
				JlmeMod.LOGGER.info("Stack in mainhand after calling method: " + entity.getMainHandItem());
				JlmeMod.LOGGER.info("Stack copy is: " + _setstack);
			} else {
				ItemStack _setstack = itemstack.copy();
				ItemHandlerHelper.giveItemToPlayer(entity, _setstack);
			}
			event.setCanceled(true);
		}
	}
commented

The problem is likely a bad interaction on the Infernal Mobs side - in MM_Sticky:

ItemEntity drop = p.drop(p.getInventory().removeItem(p.getInventory().selected, 1), false);

Infernal Mobs removes the item, then attempts to have it dropped. Now see the "onGemDropped" method you quoted:

event.setCanceled(true);

So Infernal Mobs takes the item from the player and attempts to have them drop it. Just a lot more Enchantments cancels the drop, which means the item just disappears.

To fix this, Infernal Mobs must handle the possibility of forge cancelling the drop, like this:

            if (time > nextAbilityUse) {
                nextAbilityUse = time + coolDown;
                ItemStack equippedStack = p.getInventory().removeItem(p.getInventory().selected, 1);
                ItemEntity drop = p.drop(equippedStack, false);
                if (drop != null) {
                    drop.setPickUpDelay(50);
                    mob.level().playSound(null, mob.blockPosition(), SoundEvents.SLIME_ATTACK, SoundSource.HOSTILE, 1.0F + mob.getRandom().nextFloat(), mob.getRandom().nextFloat() * 0.7F + 0.3F);
                } else {
                    // drop was cancelled by forge hook, have to restore the itemstack or it disappears
                    p.getInventory().add(equippedStack);
                }
            }
commented

oh good to know
for now i fixed it by adding a mixin on my side
hope to see an update to it so i can remove it

commented

sorry but after further testing the glitch seems to still be there, just in a weird way now
so
when attacking directly with another weapon, the item now gets duplicated because from my side im creating a new item, and from your side its also creating a new item
then
when this weapon is used and the conditions are met, the item is now no longer removed so it worked, nothing is dropped on the ground either, so its fixed on this situation but i dont get why it is not getting duplicated as before
sadly i guess i will have to stick with the mixin since i dont get why this specific damage type dealt this way just breaks this event.
Ty any way
I suggest you to remove the returning item unless you add a check to see if the item is in mainhand or not. Gonna stick with the mixin for now

commented

For my understanding: If you run the new version WITHOUT your mixin, there is duplication happening?

commented

Yes.

commented

Ah! I see it. The problem is that i return the dropped item now, but #onGemDropped also returns a copy.

What Infernal Mobs should do is try to toss the item first (without removing it from the player), and if that is aborted from outside, just do nothing. If the toss succeeds, Infernal Mobs should remove the item from the player.

commented

Ah, crap, that will still duplicate. Infernal Mobs no longer removes the original but #onGemDropped will add a copy.

commented

Final code:

                ItemStack equippedStack = p.getMainHandItem();
                if (ItemStack.EMPTY != equippedStack) {
                    p.getInventory().removeFromSelected(false);
                    ItemEntity drop = p.drop(equippedStack, false);
                    if (drop != null) {
                        // drop may be cancelled by forge event hook, but if it was, restoring the lost item
                        // is the responsibility of that outside party
                        drop.setPickUpDelay(50);
                        mob.level().playSound(null, mob.blockPosition(), SoundEvents.SLIME_ATTACK, SoundSource.HOSTILE, 1.0F + mob.getRandom().nextFloat(), mob.getRandom().nextFloat() * 0.7F + 0.3F);
                    }
                }
commented

I dont actually know if overwriting the 1.20.1.8 version on curseforge works. Will check tomorrow.

commented

np
its good to know you are actively taking time to fix this little issue thou
many thanks
I will keep testing on my side too if it gets fixed
Gonna wait for the next update

commented

I checked, curseforge does not allow for the released version to be replaced. So i just "released" a new one.

commented

the item is now getting instantly deleted
even normal items XD dude i think this event is just broken
I think you have better chances to posting the event yourself rather than using that method that for some reason does this???
wtf

@Nullable
   public static ItemEntity onPlayerTossEvent(@NotNull Player player, @NotNull ItemStack item, boolean includeName)
   {
       player.captureDrops(Lists.newArrayList());
       ItemEntity ret = player.drop(item, false, includeName);
       player.captureDrops(null);

       if (ret == null)
           return null;

       ItemTossEvent event = new ItemTossEvent(ret, player);
       if (MinecraftForge.EVENT_BUS.post(event))
           return null;

       if (!player.level().isClientSide)
           player.getCommandSenderWorld().addFreshEntity(event.getEntity());
       return event.getEntity();
   }

maybe instead of calling for p.drop you can try this, which is calling the event directly without so you can handle the removal of the item on your side but still fire the event for other mods to handle

ItemTossEvent event = new ItemTossEvent(ret, player);
        if (MinecraftForge.EVENT_BUS.post(event))
            return null;
commented
            ItemStack equippedStack = p.getMainHandItem();
                if (ItemStack.EMPTY != equippedStack) {
                    //Since the event is meant to only avoid the creation of an ItemEntity and not to avoid the removal of the ItemStack
                    //This kinda of makes sense
                    entity.setItemInHand(InteractionHand.MAIN_HAND, ItemStack.EMPTY);
                    ItemTossEvent event = new ItemTossEvent(new ItemEntity(p.level(), p.getX(), p.getY(), p.getZ(), equippedStack), player);
                    if (!MinecraftForge.EVENT_BUS.post(event)) {
                        // drop may be cancelled by forge event hook, but if it was, restoring the lost item
                        // is the responsibility of that outside party
                        drop.setPickUpDelay(50);
                        mob.level().playSound(null, mob.blockPosition(), SoundEvents.SLIME_ATTACK, SoundSource.HOSTILE, 1.0F + mob.getRandom().nextFloat(), mob.getRandom().nextFloat() * 0.7F + 0.3F);
                    }
                }

Maybe something like this might work
I tried on my custom environment but not when other mods are present, but it works without any kind of duplication.

commented

ok i tested this locally. it removes the item properly and will do nothing when the toss is cancelled which should fix the original problem. if this doesnt fix it, use your mixin and be happy with that :)

commented

Looks like it finally works as intended
gonna try in extra conditions just to be sure but now its working properly even with the item triggers its effects
Many thanks man