Tutorial Moving PacketPlayOutParticles With Entities

Discussion in 'Resources' started by benjfb1, Jul 14, 2015.

Thread Status:
Not open for further replies.
  1. Offline

    benjfb1

    Hey People! I know this is a little simple but it came to mind. I saw a few people asking for ways to do things like putting particles with shooting arrows and PacketPlayOutWorldParticles on Entities so I made this! This may be easy but a few people asked, this is for the people that don't know how, I will explain Every single step i make to make it clear and so everyone gets the idea.
    Firstly we will create a new class:
    [​IMG]

    Next we'll create our first function so we can use our functions in other classes:
    Code:
    public class Particle{ // Our Class
    
        Plugin plugin; // Creating a new Plugin Object
    
        public Particles(Plugin plugin){  // Creating a method that passes through a Plugin Object
      
            this.plugin = plugin; // Setting 'Plugin' to the Main class, to use instead of 'this.'
               
        }
    }
    
    Now we will create our first function to make the particles, making them move as the entity moves. This CAN be done for move then one entity at once calling the same function rather then writing this out again for each entity. Inside this method we'll create a Schedule Sync Repeating Task so it constantly gets the entities location.

    We will pass through a Plugin Object for the scheduleSyncReapeatingTask, A Player which would be sent the packets or particles. Sending it to all players is possible, we will look into that further into the tutorial. A Entity will be passed through the method also, this being the entity the particles will follow and move with along with a EnumParticle; the type of particle you would like it to be. The rest, integers, The size the particle spreads out, the speed of the particles and the amount of particles.

    First we have to create a public ArrayList to hold the entities that will have particle trials, we'll put this at the top of our class:
    Code:
    public ArrayList<Entity> arraylist = new ArrayList<Entity>();
    
    It'll work like this, we'll also add a statement that'll add the passed entity to the arraylist:
    Code:
        public void EntityParticles(Plugin plugin, Player player, Entity entity, EnumParticle enumparticle,
    int ParticleSize, int ParticleSpeed, int ParticleAmount){
    //Creating a Method with the required Objects.
    
    arraylist.add(entity); //Adding in passed entity to arraylist
      
            Bukkit.getScheduler().scheduleSyncRepeatingTask(plugin, new Runnable(){
    //Creating a constant repeating task ^^ We'll pass though the plugin object we made earlier
          
                @Override
                public void run() {
    //The Code will be here
    }
    }, 0L, 0L); //This is the delay that will happen each time this runnable is repeated.
    
    
    Now, Inside the runnable (public void run()) we will find the entities x, y and z Pos from the entity object that we received and cast it as a float as that is the required parameter type in the PacketPlayerOutWorldParticles method as well as checking if the entity is still in the arraylist, just incase it was removed to stop the particles by another function that we'll make later.
    We'll also create the particle effect from what we have done so far and send the packet to the player we are going to pass through:

    Code:
                @Override
                public void run() {
              
         if(arraylist.contains(entity)){ //Checking if the arraylist contains the entity to make sure it wasn't removed
              
                    float x = (float) entity.getLocation().getX();//getting the entities X location and casting it as a float
                    float y = (float) entity.getLocation().getY();//getting the entities Y location and casting it as a float
                    float z = (float) entity.getLocation().getZ();//getting the entities Z location and casting it as a float
    
                        PacketPlayOutWorldParticles packet = new PacketPlayOutWorldParticles(
    enumparticle,true, x, y, z, ParticleSize, ParticleSize, ParticleSize, ParticleSize, ParticleAmount);
    //creating the particle with the information we got at the pos of the entity
                  
                        ((CraftPlayer)player).getHandle().playerConnection.sendPacket(packet);
    //Sending the player that was passed through the packet, casting it as CraftPlayer due to Player not having //the getHandle() Method to send packets.
    
    
                        }
    }
    
    Now we have completed our EntityParticles function, we can move to the next method we need, is the RemoveEntityParticle which will remove the particles when they have been created when called It's simple. We just have to check that the arraylist contains the passed through entity and then remove it from the arraylist like this:
    Code:
        public void RemoveEntityParticles(Entity entity){
      
            if(arraylist.contains(entity)){
          
                arraylist.remove(entity);
          
            }
      
        }
    
    Now we have finished this class, we can start to implement it into our code, lets use a on player interact as a example, so it calls the method when the player right clicks:

    First, however, we'll create our Particles class object like this:

    Code:
            public void onEnable(){
                particles = newParticles(this);//Creating a pointer for particles class
                Bukkit.getServer().getPluginManager().registerEvents(this, this);//registering the PlayerInteractEvent
            }
    
            Particles particles;      //creating the object for particles class
      
    
    Then we can create the EventHandler finally, for the last step:

    Code:
            @EventHandler
            public void PlayerInteract(PlayerInteractEvent e){ //creating a PlayerInteractEvent
                Player player = (Player) e.getPlayer(); //Casting player to the player who interact
      
            if(e.getAction() == Action.LEFT_CLICK_AIR || e.getAction() == Action.LEFT_CLICK_BLOCK){
    //checking if the player right click air or block ^^
    
                       particle.EntityParticles(this, player, player, EnumParticle.VILLAGER_ANGRY, 1, 10, 10);
    //calling the EntityParticles Method. Showing the packets to the player, making the particles follow the //player, setting the particleType, setting the particle size, speed and amount
    
          Bukkit.getScheduler().scheduleSyncDelayedTask(this, new Runnable(){
    //Creating a delayed task ^^
          
                @Override
                public void run() {
    particle.RemoveEntityParticles(player); //removing the particle effects from player after 5 seconds
    }
    }, 5 * 20L); //This is the delay that will happen before running the code in the runnable
    
    //20L is a second roughly - 5 *  a second = 5 second :P
    
                    }
            }
    
    That's how we create particles for entities, now I will show the way you could do it with projectiles:

    We do the same thing as before, just with a onShoot Event and a OnHitEvent. This will give the arrow particles on shoot and stop the particles on land. You could use this for example with a magic wand checking if the item in the players hand is a stick, but we will use if they right click with any block:
    Code:
          @EventHandler
            public void OnShoot(EntityShootBowEvent e){ //creating a onShoot Event
               if(e.getEntity() instanceof Player){ //checking if the entity who shot bow is a Player
                Player player = (Player) e.getEntity(); //casting a player to to the entity who shot bow
          
                particle.EntityParticles(this, player, e.getProjectile, EnumParticle.VILLAGER_ANGRY, 1, 10, 10);
    //creating particles that follows bow as it moves ^^
               }
            }
    
            @EventHandler
            public void OnHit(ProjectileHitEvent e){ //creating a OnHit event
          
           if(particle.arraylist.contains(e.getEntity())){  //checking if a arraylist contains the projectile
              
                                         particle.RemoveEntityParticles(e.getEntity());
    //removing the projectile from the array, stopping particles for that Entity^^
              
                }
          
            }
    
    That's my tutorial over! i hope it helped those who wanted to know how to do something like this, the full classes code will be in these pastebins:
    Particles class

    Leave a like and a comment if it helped and/or want help from me.
    Thanks! ~Benjfb1
     
    Last edited: Jul 14, 2015
  2. Offline

    Totom3

    I have multiple suggestions for you.
    1. Write code that passes to the compiler. The code you have here will not, because for some reason, the field "arraylist" suddenly became "ent" in the "RemoveEntityParticles" method.
    2. Use a private final (Hash)Set instead of a public ArrayList. Add a public method that returns an immutable view of the collection using Collections.unmodifiableSet().
    3. If you want to remove an element from the collection, there's no need to check if said element is part of the collection before removing it. In other words, the "if contains then remove" should become "remove". There is no risks calling the remove method if the element is not part of the collection.
    4. Respect the Java naming conventions: method names should start with a lower case letter.
    5. The "plugin" field should be private and final.
    6. The constructor for the Particles class accepts null values. You shouldn't let those, as it is very likely to cause NPEs in the future.
    7. To avoid confusion, the repeat delay of the particles task shouldn't be 0, and I really don't think setting it to 1 is a great idea either. I recommend 4 (5 times per second), which is way enough to make it look fluid.
    8. This is going to kill the server at some point. Let me explain: you are creating a new repeating task per entity, per player, per method call, and NEVER cancel the tasks. If there are 5 players online, and each player creates a particle trail each minute (and want all players to be able to see it), you got there 25 new tasks created per minute and running each x amount of ticks (currently 1). After 10 minutes, you got 250 tasks running in the background, even if the particles aren't rendered anymore.
    9. If the add particles method is called a second time for the same entity, then the particles for the said entity will be displayed twice. If you call it a third time, there will be 3 times more particles. Why? Same as above. A new task is created for the entity, and each task checks whether or not the entity is in the collection. The three tasks will see that the entity is in the collection, and will therefore send the particles.
     
    Konato_K likes this.
  3. Offline

    xTrollxDudex

    NONONONONONONONONONONONONONONONOOONONONONONONONONONONONONO

    There are people out there that are stupid enough to forget to remove entities. There are also people that simply forget to call the remove method. Either way, strong referencing entities is a lurking bug that will come back and bite you in the back if you are not careful.
     
Thread Status:
Not open for further replies.

Share This Page