Jump to content

Arrows not always hitting players


theishiopian

Recommended Posts

Im attempting to make a mod that reduces the damage of potion arrows to 0, with good results so far. However, I'm running into a peculiar bug. Whenever a tipped arrow is shot at a player, theres about a 50-50 chance of the arrow bouncing off the player. The chance increases to 100% with dispensers, and seems not to occur with strays. Messing with the entity hurt resistance timer thing didn't really seem to make a difference. Non player entities can be hit normally.

 

Code:

package com.theishiopian.usefulpotionarrows;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Field;

import net.minecraft.entity.projectile.EntityArrow;
import net.minecraft.entity.projectile.EntityTippedArrow;
import net.minecraft.potion.PotionType;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.world.World;
import net.minecraftforge.event.entity.ProjectileImpactEvent;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.relauncher.ReflectionHelper;

public class DamageNullifier 
{
	//note to self: find these names in C:\Users\Andrew\.gradle\caches\minecraft\de\oceanlabs\mcp\mcp_snapshot\20171003\1.12.2\srgs
	Field potion = ReflectionHelper.findField(EntityTippedArrow.class, "potion", "field_184560_g");
	
	
	@SubscribeEvent
	public void Nullifier(ProjectileImpactEvent.Arrow event) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException
	{
		EntityArrow arrow = event.getArrow();
		World world = arrow.getEntityWorld();
		
		if(!world.isRemote && arrow instanceof EntityTippedArrow)
		{
			potion.setAccessible(true);
			PotionType plocal = (PotionType) potion.get(arrow);
			
			if(!(plocal.getRegistryName().toString().equals("minecraft:empty")))
			{
				arrow.setDamage(0.0);
				if(event.getRayTraceResult().typeOfHit == RayTraceResult.Type.ENTITY)
					event.getRayTraceResult().entityHit.hurtResistantTime = 0;
			}
		}
	}
	
}

What could be causing this?

 

Link to comment
Share on other sites

3 hours ago, theishiopian said:

potion.setAccessible(true);

This is a very expensive operation that only needs to be done once! Put it in a static initialiser right below setting the field. That field reference should also be a constant

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

If your working on obfuscated files you’re doing something wrong

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

On 11/27/2018 at 7:20 AM, theishiopian said:

the files in the editor are obfuscated

 

Use ReflectionHelper with both the deobf and SRG names

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

Forge 1.12.2-14.23.5.2779 deprecated ReflectionHelper and changed the methods in ObfuscationReflectionHelper to only require an SRG name rather than both an SRG and MCP name. This is what @quadraxis was talking about.

  • Like 2

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

Link to comment
Share on other sites

Ok, I tried implementing the recommended changes. However, the game now crashes at startup when I try to access the field with an UnableToAccessFieldException. Im trying to get the field at startup in the postInit phase by calling reflectionSetup(). Did I misunderstand the phrase "static initialiser? Or can I not use static fields for this?"

 

Code:

 

package com.theishiopian.usefulpotionarrows;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

import net.minecraft.entity.projectile.EntityArrow;
import net.minecraft.entity.projectile.EntityTippedArrow;
import net.minecraft.potion.PotionType;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.world.World;
import net.minecraftforge.event.entity.ProjectileImpactEvent;
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;

public class DamageNullifier 
{
	private static EntityTippedArrow arrow = null;
	
	static Field potion;
	
	public static void reflectionSetup()
	{
		potion = ObfuscationReflectionHelper.getPrivateValue(EntityTippedArrow.class, arrow, "potion", "field_184560_g");
		
		potion.setAccessible(true);
	}
	
	
	
	@SubscribeEvent
	public void Nullifier(ProjectileImpactEvent.Arrow event) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException
	{
		arrow = (EntityTippedArrow) event.getArrow();
		World world = arrow.getEntityWorld();
		
		if(!world.isRemote && arrow instanceof EntityTippedArrow)
		{
			
			System.out.println(potion.get(arrow).toString());

//			if(!())
//			{
//				arrow.setDamage(0.0);
//				if(event.getRayTraceResult().typeOfHit == RayTraceResult.Type.ENTITY)
//					event.getRayTraceResult().entityHit.hurtResistantTime = 0;
//			}
		}
	}
	
}

 

 

 

 

 

 

Edited by theishiopian
initilizer
Link to comment
Share on other sites

Ok, after some further experimentation, I managed to figure out what a static initilizer is. Even after implementing static init properly, it still throws the same error, just now in a different place.

 

Code:

package com.theishiopian.usefulpotionarrows;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

import net.minecraft.entity.projectile.EntityArrow;
import net.minecraft.entity.projectile.EntityTippedArrow;
import net.minecraft.potion.PotionType;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.world.World;
import net.minecraftforge.event.entity.ProjectileImpactEvent;
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;

public class DamageNullifier 
{
	private static EntityTippedArrow arrow;
	
	static Field potion;
	
	static
	{
		potion = ObfuscationReflectionHelper.getPrivateValue(EntityTippedArrow.class, arrow, "potion", "field_184560_g");
		
		potion.setAccessible(true);
	}
	
	
	
	@SubscribeEvent
	public void Nullifier(ProjectileImpactEvent.Arrow event) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException
	{
		arrow = (EntityTippedArrow) event.getArrow();
		World world = arrow.getEntityWorld();
		
		if(!world.isRemote && arrow instanceof EntityTippedArrow)
		{
			
			System.out.println(potion.get(arrow).toString());

//			if(!())
//			{
//				arrow.setDamage(0.0);
//				if(event.getRayTraceResult().typeOfHit == RayTraceResult.Type.ENTITY)
//					event.getRayTraceResult().entityHit.hurtResistantTime = 0;
//			}
		}
	}
	
}

 

Stacktrace of exception:

https://pastebin.com/pKUwxBSr

Link to comment
Share on other sites

Your trying to get the instance field of a null instance. Get the field in a static initialiser (ReflectionHelper.findField), and then call Field.get(instanceOfArrow) in your normal code

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

Ah, that makes much more sense. Ill try that out in a few minutes, thank you so much!

EDIT: wait, isnt that what Im already doing? Field.get() is being called in the event handler.

EDIT2: I backtracked a bit, going back to my original, working code from the first post, except with a  static initilizer to get the field and no call of setAccessible. Now it works, but I need to run a few more tests to make sure the original problem is gone.

Edited by theishiopian
further information.
Link to comment
Share on other sites

Ok, so the reflection changes don't solve the original problem. Tipped arrows from players still only hit other players 50% of the time, and tipped arrows from dispensers still cant hit players at all.

 

Code now looks like this:

 

public class DamageNullifier 
{
	static Field potion;
	
	static
	{
		potion = ReflectionHelper.findField(EntityTippedArrow.class, "potion", "field_184560_g");
	}
	
	
	
	@SubscribeEvent
	public void Nullifier(ProjectileImpactEvent.Arrow event) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException
	{
		EntityArrow arrow = event.getArrow();
		World world = arrow.getEntityWorld();
		
		if(!world.isRemote && arrow instanceof EntityTippedArrow)
		{
			PotionType plocal = (PotionType) potion.get(arrow);
			
			if(!(plocal.getRegistryName().toString().equals("minecraft:empty")))
			{
				arrow.setDamage(0.0);
				if(event.getRayTraceResult().typeOfHit == RayTraceResult.Type.ENTITY)
					event.getRayTraceResult().entityHit.hurtResistantTime = 0;
			}
		}
	}
	
}

 

Edited by theishiopian
Forgot a word
Link to comment
Share on other sites

33 minutes ago, theishiopian said:

Ok, so the reflection changes don't solve the original problem. Tipped arrows from players still only hit other players 50% of the time, and tipped arrows from dispensers still cant hit players at all.

So is the problem that the entity isn't getting the damage animation? That has to do with the damage value and how taking damage is handled on Entities.

VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING

I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect.

Forge and vanilla BlockState generator.

Link to comment
Share on other sites

1 minute ago, theishiopian said:

So  maybe set the damage to 0.1 then?

Take a full look at the damage handling by setting a breakpoint in the EntityArrow class that handles the collision and then damage handling(its in the update method).

VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING

I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect.

Forge and vanilla BlockState generator.

Link to comment
Share on other sites

52 minutes ago, theishiopian said:

ok, ill do that. just to refresh my memory, how to i jump to the next breakpoint?

There is a button somewhere on your IDE.

VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING

I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect.

Forge and vanilla BlockState generator.

Link to comment
Share on other sites

This happens because if the damage is 0 then EntityLivingBase#attackEntityFrom will return false which will cause the arrow to "bounce". You can have the exact same effect if you shoot the player in creative mode.

You might be able to circumvent the issue by using a LivingHurtEvent(or LivingDamageEvent) in combination with ProjectileImpactEvent. Basically in the impact event do your arrow check and if it is supposed to have no damage set some kind of flag to true(somewhere in your code that is) and also store the entity reference of the entity being damaged(that's important)(you may store something to identify the entity by, like it's UUID instead of a direct reference). Next in the Hurt/Damage event check for the flag - if it's true check whether the entity being damaged is the one you've stored the reference to(it should be but there are edge cases where it may not be). If it isn't set the flag to false, clear the reference and return. If it is you have a jackpot - set the damage to 0, flag to false, clear reference and be done. Alternatively don't clear the reference, but store it as a UUID to not leak like half of the game.

I've just tested this idea and it works flawlessly. Here is the code I've used.

private static boolean checkImpact = false;
    private static UUID hurtID = null;

    @SubscribeEvent
    public static void onArrowDamage(ProjectileImpactEvent event)
    {
        checkImpact = false;
        if (event.getEntity() instanceof EntityArrow && event.getRayTraceResult() != null && event.getRayTraceResult().typeOfHit == RayTraceResult.Type.ENTITY)
        {
            checkImpact = true;
            hurtID = event.getRayTraceResult().entityHit.getUniqueID();
        }
    }

    @SubscribeEvent
    public static void onHurt(LivingHurtEvent event)
    {
        if (checkImpact)
        {
            checkImpact = false;
            UUID id = event.getEntityLiving().getUniqueID();
            if (id.equals(hurtID))
            {
                event.setAmount(0);
            }

            hurtID = null;
        }
    }

Now admitedly you shouldn't have to store the entity reference, since the code flow should take you directly to the LivingHurtEvent from the ProjectileImpactEvent(in this case) but you never know with all the forge re-implementations around like them spigots and stuff. Since I can't guarantee the code flow within those implementations it's better to be sure.

  • Like 1
Link to comment
Share on other sites

I haven't been able to do any testing due to being really sick, so this is super appreciated! It looks like your code just nullifies all projectile damage, but it should be easy enough to merge my reflection code with your code to narrow it down to just potion arrows. Thanks for the suggestion, Ill be sure to list you as a contributor in the mcinfo file if this works!

Link to comment
Share on other sites

5 minutes ago, theishiopian said:

So, after some preliminary experimentation, I've discovered a small bug in your code, namely the event handlers being static means they don't register properly. Changing them to non static methods seems to fix it.

He was registering them in a different way than you were. If you register an instance of the class they can't be static, but if you register the class itself or use the @EventBusSubscriber on the top of the class then the methods must be static.

VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING

I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect.

Forge and vanilla BlockState generator.

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.



×
×
  • Create New...

Important Information

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