Resource Creating entity without spawning it

Discussion in 'Spigot Plugin Development' started by Duckelekuuk, Apr 17, 2018.

  1. Hi people who read this.

    I was looking on the forums on how to create an entity without spawning it but I couldn't find anything useful.
    So I decided to give it a go and I was successful so I wanted to share my code incase somebody else runs into the same problem I had.

    What could you use it for?

    I personally use it for ProtocolLib. It gives you the option of sending an entity but i saw the entity spawn every time just for a split second and in found it ruining the experience. I know the use for this is very limited but if I can just help out a few people then I did my job. You could also use this thread to learn how I tackled this problem.

    How did i tackle this problem?
    After not being able to find a solution on google (which is the first place you should always check) I started taking a look in the CraftBukkit code on the spigot hub.
    I knew it was there because of experience but I just had to find what class. Because the method was in the getWorld function it used the CraftWorld class. After that you should trace back the methods till you found the method that you are looking for. So looking in the spawn method I saw that it called
    createEntity which takes in as parameters, A location and a class that extends entity.
    Link to the method

    So now that we found where we need to be. We need to try simulating what the spawn method was doing but without spawing.
    What I did next was create a reflection method that would execute that method in CraftWorld.
    I use reflection so i am not depended on the server version and so I can use it in multiple plugins without having to worry about compatibility. Just ran the first test and it worked straight away. :)

    Code:
    Code (Java):
    public class EntityCreator {

        /**
         *
         * @param entityType The type of entity that you want to create
         * @param location The location where you want the entity.
         * @return Entity
         */

        public static Entity create(EntityType entityType, Location location) {
            try {
                // We get the craftworld class with nms so it can be used in multiple versions
                Class<?> craftWorldClass = getNMSClass("org.bukkit.craftbukkit.", "CraftWorld");

                // Cast the bukkit world to the craftworld
                Object craftWorldObject = craftWorldClass.cast(location.getWorld());

                // Create variable with the method that creates the entity
                // https://hub.spigotmc.org/stash/projects/SPIGOT/repos/craftbukkit/browse/src/main/java/org/bukkit/craftbukkit/CraftWorld.java#896
                Method createEntityMethod = craftWorldObject.getClass().getMethod("createEntity", Location.class, Class.class);

                // Attempt to invoke the method that creates the entity itself. This returns a net.minecraft.server entity
                Object entity = createEntityMethod.invoke(craftWorldObject, location, entityType.getEntityClass());

                // finally we run the getBukkitEntity method in the entity class to get a usable object
                return (Entity) entity.getClass().getMethod("getBukkitEntity").invoke(entity);
            } catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | IllegalAccessException exception) {
                exception.printStackTrace();
            }


            // If something went wrong we just return null
            return null;
        }

        /**
         *
         * @param prefix What comes before the version number
         * @param nmsClassString What comes after the version number
         * @return Class The class that you tried to access
         * @throws ClassNotFoundException throws an exception if the class it not found
         */

        private static Class<?> getNMSClass(String prefix, String nmsClassString) throws ClassNotFoundException {
            // Getting the version by splitting the package
           String version = Bukkit.getServer().getClass().getPackage().getName().replace(".", ",").split(",")[3] + ".";

            // Combining the prefix + version + nmsClassString for the full class path
            String name = prefix + version + nmsClassString;
            return Class.forName(name);
        }
    }
     

    I hope you found this thread useful and enjoyed reading it.
    Keep on coding ;)
     
    #1 Duckelekuuk, Apr 17, 2018
    Last edited: May 6, 2018
    • Useful Useful x 2
  2. Great Tutorial, Keep it up :p
     
  3. 2008Choco

    Junior Mod

  4. I'm exceedingly confused on what you mean by
    If you mean spawning the entities on the client side, this method demonstrably does not do that, in fact, you should probably really be using World#spawn(...) or World#spawnEntity(...) because they all overload this method, which in turn calls the method you are calling reflectively anyways.
     
  5. In org.bukkit.World you only have a method to spawn entities. This will create an entity and add it to the world. What EntityCreator does is that it takes out the last step of the spawn process which is actually spawning it by adding it to the world.
    This class will just create the entity so you can do with it whatever you want.

    What I saw is that people spawn an entity, save the instance and remove it. The problem I had is that it would still need to be somewhere in the world for just a split second.

    Yes you are right about that but, in combination with packets you can spawn an entity on the client side.
     
  6. Ah I see. Thank you for the clarification!
     

Share This Page