Bukkit / Java : Problem with recursive method

51 views Asked by At

I'm trying to do a plugin where guns can shoot ammo, and I want that when the ammo hit a block (= block of the location of the ammo isn't air), this ammo destroys the block

The problem is that the following code (Ammo class) has the 'moveFrom()' method calling itself infinitely.

I get an error in the console : StackOverflowError, so I'm pretty sure the problem comes from the recursive method.

Any suggestion is welcomed !

package fr.laponie.ShooterPlugin;

import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.block.Block;
import org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.util.Vector;

import net.minecraft.server.v1_8_R3.EnumParticle;
import net.minecraft.server.v1_8_R3.PacketPlayOutWorldParticles;

public class Ammo {
    
    public Ammo(Player p,Weapon w) {
        this.p = p;
        this.w = w;
        this.loc = p.getLocation();
    }
    
    Player p;
    Weapon w;
    Location loc;
     
    public void moveFrom(Location init) {
        Location eyeLoc = p.getEyeLocation();
        Vector dir = eyeLoc.getDirection();
        
        dir = dir.add(new Vector(1,1,1));
        
        Vector distance = new Vector(0.4,0.4,0.4);
        
        distance = distance.add(dir);
        this.loc = distance.toLocation(p.getWorld());
        EnumParticle trail = EnumParticle.CRIT;
        
        
        Block target = this.loc.getBlock();
        if (target.getType() == Material.SAND) {
            p.sendMessage(ChatColor.DARK_AQUA + "" + ChatColor.BOLD + "HIT SAND");
            target.setType(Material.AIR);
            return;
        }
        
        Location particleLoc = this.loc;
        PacketPlayOutWorldParticles packParticle = new PacketPlayOutWorldParticles(trail, false, (float) particleLoc.getX(),(float) particleLoc.getY(),(float) particleLoc.getZ(),
                0,0,0,0,0, null);
        CraftPlayer cp = (CraftPlayer) p;
        cp.getHandle().playerConnection.sendPacket(packParticle); 
        moveFrom(loc);
    }
    
    public void moveTo(Location destination) {
        this.loc = destination;
    }
    
    public void setLocation(Location loc) {
        this.loc = loc;
    }
}

And here is the PlayerInteractEvent() :

@EventHandler
    public void onRightClickWeapon(PlayerInteractEvent e) {
        Player p = e.getPlayer();
        Action a = e.getAction();
        
        ItemStack current = p.getItemInHand();
        ItemStack rifle = new AssaultRifle().mat;
        
        int count = 0;
        
        Location eyeloc = p.getEyeLocation();
        
        Vector defaultVector = new Vector(1,1,1);
        Vector distance = new Vector(0.4,0.4,0.4);
        Vector dir = eyeloc.getDirection();
        
        if (a == Action.RIGHT_CLICK_AIR || a == Action.RIGHT_CLICK_BLOCK) {
            if (current.getType() == rifle.getType() && current.getItemMeta().getDisplayName().equalsIgnoreCase(rifle.getItemMeta().getDisplayName())) {
                
                Ammo ammo = new Ammo(p, this);
                ammo.setLocation(p.getLocation());
                while (ammo.loc.getBlock().getType() == Material.AIR) {
                    ammo.moveFrom(eyeloc);
                    
                }
            }
            
        }
        
     }

EDIT: And even if i try with a for-loop like here : it does nothing

@EventHandler
    public void onRightClickWeapon(PlayerInteractEvent e) {
        Player p = e.getPlayer();
        Action a = e.getAction();
        
        ItemStack current = p.getItemInHand();
        ItemStack rifle = new AssaultRifle().mat;
        
        int count = 0;
        
        Location eyeloc = p.getEyeLocation();
        
        Vector defaultVector = new Vector(1,1,1);
        Vector distance = new Vector(0.4,0.4,0.4);
        Vector dir = eyeloc.getDirection();
        
        if (a == Action.RIGHT_CLICK_AIR || a == Action.RIGHT_CLICK_BLOCK) {
            if (current.getType() == rifle.getType() && current.getItemMeta().getDisplayName().equalsIgnoreCase(rifle.getItemMeta().getDisplayName())) {
                
                Ammo ammo = new Ammo(p, this);
                ammo.setLocation(p.getLocation());
                while (ammo.loc.getBlock().getType() == Material.AIR) {
                    Location eyeLoc = p.getEyeLocation();
                    dir = eyeLoc.getDirection();
                    
                    dir = dir.add(new Vector(1,1,1));
                    
                    distance = new Vector(0.4,0.4,0.4);
                    
                    distance = distance.add(dir);
                    ammo.loc = distance.toLocation(p.getWorld());
                    EnumParticle trail = EnumParticle.CRIT;
                    
                    
                    Block target = ammo.loc.getBlock();
                    if (target.getType() == Material.SAND) {
                        p.sendMessage(ChatColor.DARK_AQUA + "" + ChatColor.BOLD + "HIT SAND");
                        target.setType(Material.AIR);
                        return;
                    }
                    
                    Location particleLoc = ammo.loc;
                    PacketPlayOutWorldParticles packParticle = new PacketPlayOutWorldParticles(trail, false, (float) particleLoc.getX(),(float) particleLoc.getY(),(float) particleLoc.getZ(),
                            0,0,0,0,0, null);
                    CraftPlayer cp = (CraftPlayer) p;
                    cp.getHandle().playerConnection.sendPacket(packParticle); 
                }
            }
            
        }
        
     }
1

There are 1 answers

2
lance-java On

Firstly, you should refactor your moveFrom(...) method to use a while loop instead of recursion. Recursion requires another stack frame for each recursive call and will use vastly more resources than a while loop.

Secondly it seems that moveFrom(...) will only exit when target.getType() == Material.SAND. I'm guessing you hit the max stack depth before you move to a sandy patch