Player Entities again.

Discussion in 'Plugin Development' started by xigsag, Dec 23, 2013.

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

    xigsag

    #No dependencies please.
    I've seen a few libraries such as RemoteEntities and another one which I've forgotten the name of, and they all seem so complicated. I'm looking for:

    #1. A way to spawn human entities and remove specified human entities, and let them look at players, and

    #2. how do you handle right clicking said player entity? For example, I'm right clicking the Blacksmith, and I want to check if the entity I clicked, is in fact the blacksmith, which I would then open up the blacksmith's shop inventory. Do I simply check for the entity's name?

    I would love for some code libraries instead of plugin dependancies. RemoteEntities has too much code for me to follow from, so I would prefer having a library, that handles at least #1,
    Or if its simple enough, someone to guide me through how.

    Much appreciated in advance. Merry Christmas.
     
  2. Offline

    xlrion

    for #1
    I believe that might be a bit too much to handle without an API. Spawning a fake player would require editing packets, which would require NMS reflections. Any solution you could come up with would likely be much more complicated than using, for example, ProtocolLib.

    For what your trying to accomplish maybe you could use a Villager and name it "Blacksmith"?

    #2
    You could use a listener for EntityInteractEvent. Which would let you see when the player interacts with the Villager (Or Player, If you come up with a creative way to do that), cancel that villager inventory from opening, then open your larger shop inventory. As for looking at layers, I think you would have to listen for PlayerMoveEvent perhaps, check if the shop keeper entity is nearby then, if so, adjust the yaw and pitch of it's head.
     
  3. Offline

    xigsag

    xlrion I'd prefer human entities, but I guess using villagers would work. Just confirming, but do you use a high level slow to make it so that the npc doesn't move?
     
  4. Offline

    TryB4

    xigsag
    players would be able to push them.
     
  5. Offline

    xigsag

    TryB4 So is there a good way to make them unmovable?
     
  6. Offline

    Garris0n

    Override the methods that make them pushable.
     
  7. Offline

    xigsag

    Garris0n And what would those obfuscated methods be? If you don't mind me being lazy and asking..
     
  8. Offline

    TryB4

    xigsag
    HashMap<Entity, Location>
    put them in there with the location you want them to be frozen at.
    And make a scheduler which runs every 30 seconds or so, if their x/y/z aren't equal to the one of the location you set in the hashmap teleport them.
     
  9. Offline

    Garris0n

    I don't know. If you're doing this you should know how to find them.
     
  10. Offline

    xlrion

    xigsag You could use a high level slowness effect, combined with tryb4's suggested method. Every so often reset the entity back to its original position.
     
  11. Offline

    Cirno

    Override move(double x, double y, double z) and make it empty. (I think it's double)
     
  12. Offline

    Jogy34

    What you could do is make a custom entity that extends EntityPlayer. This requires a lot of NMS and can easily get really complicated. Since it looks like you might be looking for a fairly easy, fast, and straight forward method of doing this I'll just point you to my code that I use for my TARDIS plugin which uses human NPC's for parts of it. If you do want to do something similar I can try to answer any questions you may have about doing something like this.

    PACKAGE LINK

    TARDIS Protocall - Base NPC
    TARDISPlayer/ITARDISPlayer - Craftbukkit Entity implementation (not actually needed)
    Merchant Protocall - Basically opens up villager trading screens for players when right clicked
    Tree Protocall/Weaponry Protocall - Two different types of Merchant Protocalls
    Disk Protocall - Programmable NPC (can talk, walk, look around, etc... based on in game scripts)

    Once again, doing something like this can get really complicated really quickly.
     
  13. Offline

    xTrollxDudex

    I think PacketPlayInUseEntity applies for number 2
     
  14. Offline

    Cirno

    It would probably start getting really complicated at this point; would PlayerInteractEntityEvent still be called because it notices an entity? If it does, it would likely throw a nurupo as soon as you try to perform any operations on the entity itself (because it's client-side), or even worse, crash the server. Or it can be for the better and not be called at all.
     
  15. Offline

    xTrollxDudex

    Wait what... I thought I was talking about PacketPlayInUseEntity, it goes from C->S so the server actually revieves information about the entity from the client.
     
  16. Offline

    xigsag

    Jogy34 Thanks! Yeah, I'm not too familiar with NMS, so I think I am going to need some of your help with this..

    So far, following the most basic of all the code in your source, I've managed to implement them without any errors. Though when I run the code to spawn the entity at the sender's position, no errors come up, neither does the entity.
    Am I doing something wrong? Here's the code.

    Command:
    Code:java
    1. if (label.equalsIgnoreCase("test")) {
    2. net.minecraft.server.v1_7_R1.World cs = ((CraftWorld) player.getWorld()).getHandle();
    3. Npc npc = new Npc(cs);
    4. npc.setPosition(137, 66, 27);
    5. cs.addEntity(npc);
    6. }


    The Npc class, taken and trimmed immensely from your Silurian.class :
    Code:java
    1. import net.minecraft.server.v1_7_R1.EntityPlayer;
    2.  
    3. import org.bukkit.ChatColor;
    4. import org.bukkit.Color;
    5. import org.bukkit.Material;
    6. import org.bukkit.craftbukkit.v1_7_R1.inventory.CraftItemStack;
    7. import org.bukkit.inventory.ItemStack;
    8. import org.bukkit.inventory.meta.LeatherArmorMeta;
    9.  
    10. import net.minecraft.server.v1_7_R1.EntityVillager;
    11. import net.minecraft.server.v1_7_R1.World;
    12.  
    13. public class Npc extends EntityVillager {
    14. public Npc(World w) {
    15. super(w);
    16. super.setCustomName("Npc");
    17. }
    18.  
    19. @Override
    20. public String getName() {
    21. return "Npc";
    22. }
    23. }


    Jogy34 Weirdly enough, I managed to get the PlayerEntity to spawn, but not the Villager entity. Either way, I am still very curious as to why the villager isn't spawning :l

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: Jun 5, 2016
  17. Offline

    Jogy34


    First off, I'm really surprised that you got the player entity to spawn at all. What's wrong is that you aren't actually actually adding your custom entity to the server so your server can't tell the client what it is. To do this you have to use some reflection to get into the EntityTypes NMS class to add the entity to a few maps. This comes from my Load class:
    Code:java
    1.  
    2. protected static Field mapStringToClassField, mapClassToStringField, mapClassToIdField, mapStringToIdField;
    3. //Creating static variables for the map fields (some variable in some class) so that I don't have to keep retrieving them
    4.  
    5. // Setting up all of the fields in the static initializer
    6. static
    7. {
    8. try
    9. {
    10. mapStringToClassField = net.minecraft.server.v1_7_R1.EntityTypes.class.getDeclaredField("c"); //Setting this field to the variable named 'c'
    11. mapClassToStringField = net.minecraft.server.v1_7_R1.EntityTypes.class.getDeclaredField("d");
    12. mapClassToIdField = net.minecraft.server.v1_7_R1.EntityTypes.class.getDeclaredField("f");
    13. mapStringToIdField = net.minecraft.server.v1_7_R1.EntityTypes.class.getDeclaredField("g");
    14.  
    15. mapStringToClassField.setAccessible(true); //Allowing access to the fields, basically changes private to public
    16. mapClassToStringField.setAccessible(true);
    17. mapClassToIdField.setAccessible(true);
    18. mapStringToIdField.setAccessible(true);
    19. }
    20. catch (Exception e)
    21. {
    22. e.printStackTrace(); //an exception is called if one of the fields doesn't exist
    23. }
    24. }
    25.  
    26. //Adding a custom entity to the game
    27. @SuppressWarnings({ "rawtypes", "unchecked" })
    28. protected static void addCustomEntity(Class entityClass, String name, int id)
    29. {
    30. if (mapStringToClassField == null || mapStringToIdField == null || mapClassToStringField == null || mapClassToIdField == null)
    31. {
    32. return; //In case something went wrong when retrieving the fields
    33. }
    34. else
    35. {
    36. try
    37. {
    38. Map mapStringToClass = (Map) mapStringToClassField.get(null); //Retrieving the static variable, null would be replaced by an actual object instance if it wasn't static
    39. Map mapStringToId = (Map) mapStringToIdField.get(null);
    40. Map mapClasstoString = (Map) mapClassToStringField.get(null);
    41. Map mapClassToId = (Map) mapClassToIdField.get(null);
    42.  
    43. mapStringToClass.put(name, entityClass); //Adding the name matched to the entity class
    44. mapStringToId.put(name, Integer.valueOf(id)); //Adding the name matched to the network id
    45. mapClasstoString.put(entityClass, name); //adding the class matched to the name
    46. mapClassToId.put(entityClass, Integer.valueOf(id)); //Adding the class matched to the network id
    47.  
    48. mapStringToClassField.set(null, mapStringToClass); //Setting the map back into the field in just in case it doesn't update it. There's a good chance this isn't needed
    49. mapStringToIdField.set(null, mapStringToId);
    50. mapClassToStringField.set(null, mapClasstoString);
    51. mapClassToIdField.set(null, mapClassToId);
    52. }
    53. catch (Exception e)
    54. {
    55. e.printStackTrace(); //A lot of things can go wrong here
    56. }
    57. }
    58. }
    59.  


    The network id tells your client what the entity looks like. For instance, if you look in my load class you'll see that I'm setting the Silurian network id to that of a Skeleton. When I spawn the Silurian entity, it will appear to be a Skeleton but it will act like a villager so that if you right click it, the villager's trading menu will pop up. You can do some really interesting things with this, if you look at my MetalStingRay class you'll see that it extends from EntityWither however I set the network id to a squid and make these spawn on one of my custom planets so when you're on that planet you get chased by a bunch of flying squids that are throwing wither skulls at you.

    Not all network id's will work with all mobs, a few problems that I remember is that a pig won't work with a zombie or pig zombie network id and probably a few others. I have found no problems so far with using a squid or a bat with any network id's though.

    Anyways, to find the network id just google "Minecraft [Insert Mob Name Here]" and one of the first links will be the minecraft wiki then under the picture of the mob on the right side there will be a section that says "Network ID" and then a number.
     
  18. Offline

    xigsag

    Jogy34 Ah, that's helpful. By the way, could you explain to me how the saving of entities spawned using this source of yours works? I see some configs in there, do you use the built in config interface to store them? Also, how does that work, as in what do you store?
     
  19. Offline

    Jogy34


    I save the TARDISProtocall entities in config files because those require more information than just an NMS World. So if I were to just leave them where they are, then when the server is shut down or reloaded, they would turn into Player Entity shells that you would have to shut down the server to get rid of.

    First off, I'll explain my config system. I use a lot of custom configs, for any given server that uses my TARDIS plugin you can expect there to probably be at least 15+ different config files. I've developed a system to allow myself the creation of pretty much an infinite amount of custom config files (well as many as the computer the server is running on can handle). It's actually fairly simple, I create a custom config file, and then store it into a map to be able to be retrieved, saved, created, or reloaded by an id which also servers as the file name. I created a library a while back that used this same idea except was generalized to use any plugin. You can find the config portion of that HERE (that library also contains NMS Tile entity retrieval that is most likely out of date). You could also try to find the more specific code for configs in my TARDISMain class but you would have to do a lot of searching and matching to get everything. For the library, you would pretty much just take away the JavaPlugin references and take out the default config retrieval in the reloadConfig() method (I don't use default config files)

    As to what I store, for the actual entity information I only store things for the TARDISProtocalls. For the merchant ones, I make a list for each type (tree and weaponry) and then every time one is created, I add it to the list. For each section in one of the lists I store it in the format:
    • [WorldName]:[X(to 2 decimal places]:[Y(to 2 decimal places)]:[Z(to 2 decimal places)]:[NAME]
    The X, Y, and Z, I use as the coordinates to spawn them in the World when the server starts up (or is reloaded). And the Name is the name of the NPC. This acts just like the name of a player where the NPC's skin will change depending on what it is. So if it was named Jogy34, it would have my skin but if the name was changed to xigsag, it would have your skin.
    For the Disk protocall, I don't store any information about the actual Entity. What happens with these guys is that someone writes a script (the scripting language is unique to this plugin) in a book then goes over to a Disk Reader (set from within the plugin in someone's TARDIS) and sets the code for one of the protocalls and then hit's play or 'burns' it onto a record and puts it into the disk reader. The protocall spawns and goes through the script then despawns. Because of this, there isn't any reason to save the location, name, or anything of the entity itself. Instead, What I do is I assign each new protocall that is created to a UUID (Universally Unique Identifier [Java already handles this for me]). I then create a new config file using the 'toString()' of the UUID as the file id but embed it into a different folder. To put it into a folder, I send in the config id as "FolderName/FileName" and that will automatically create a folder for me. Anyways, in the Disk protocall's config files, I save the name of the program, the inventory of the protocall, and the code. The code then contains the starting location, the starting yaw (where the protocall is looking), the name of the actual protocall, and all of the script steps split up identical to the pages of the books so if one page contains 8 steps, all of those steps appear as one item in the list of steps as a single string.
     
  20. Offline

    xigsag

    Jogy34 Alright, so let me get this right. Whenever the server shuts down, in the onDisable() method, you get every EntityPlayer from TARDISProtocall's getAll() method, which you then add to a String[] to store in the config in the format:
    Code:java
    1. [WorldName]:[X(to 2 decimal places]:[Y(to 2 decimal places)]:[Z(to 2 decimal places)]:[NAME]


    , which you can then loop and retrieve each string on start up, in the onEnable() method, split them up and spawn them.. how?
     
  21. Offline

    Jogy34

    I save them by calling the static 'saveAll()' method (HERE) which puts a bunch of Strings, in that format, into a list which is then put into a custom config file under what type of Protocall it is.

    To spawn them when I call the loadProtocalls() method in my load class (HERE) by splitting the Strings that I get from the list at the ':'s then getting the world from the name and the location from the X, Y, and Z values converted to doubles and then the name and create a new Protocall.
     
    xigsag likes this.
  22. Offline

    xigsag

    Jogy34 Hehe, now I see. I tried doing exactly that without method-ising these blocks of code, and just inserting them straight into the onEnable and onDisable methods. Gave me errors, but I guess I wasn't doing something your saveAll method did. Thanks! I'll post a question here to hopefully be answered, if I do have anymore, for public referenece. Thanks again! :)
     
  23. Offline

    xigsag

    Just as a side note, I feel like a fucking idiot right now. (That wasn't the side note.)

    I had a plugin created which had a minor function that simply cancels creature spawn events, because
    I was doing my testing on a flat-land world, and I didn't want mobs to spawn everywhere. That cancelled the spawning of my EntityVillager, and I didn't even notice it. Anyway, its working now.

    Also, how do I give it a potion effect? I don't get it.
     
  24. Offline

    xTrollxDudex

    Jogy34
    Sorry to break the news, but it's spelled 'Protocol' not 'Protocall' ;)
    xigsag
    Since your Npc class extends EntityVillager, and EntityLiving (which provides an addEffect(...) method) is it's superclass, you can essentially do:
    PHP:
    Npc npc /* Your stuff here */;

    int id // http://minecraft.gamepedia.com/Status_effect under List effects, left column
    int duration // duration, most likely in ticks
    int amplify // the 'power', like Speed 3 or Strength 2
    MobEffect effect = new MobEffect(iddurationamplify);

    npc.addEffect(effect);
    Make sure to read the comments, they contain important information.
     
  25. Offline

    xigsag

    xTrollxDudex Ah, so that's how you use the addEffect Method. I was thinking MobEffect would act like Sound or Effect.
     
  26. Offline

    TheTinySpider

    I would personally go full NMS way, I played around with this for a while and it seems do-able.
    I found a source for the new netty connections, with this you can hack into a player connection and edit the packets, this is basicly the same as protocolib, just a little less reliable.

    The way I would do it is as follow:

    - Spawn an custom entity with a empty H() field (movement that is)
    - Edit the packets send to the client to make your entity look like a player (this way it doesnt matter how many with the same name are online)
    - Then, you can just check for interaction with your custom entity and no tricky stuff.

    If you do the custom entity thing the right way you can just add this:
    to your plugin and you don't have to use any config savings or anything, the server will handle entity saving for you.
    If you're interested in this way of doing it, I can post some of the code required for it on here.
     
  27. Offline

    xigsag

    TheTinySpider This sounds interesting. Sure, please do post some code here! :) Any help would be appreciated, since I have not fully solved this thing yet.
     
  28. Offline

    TheTinySpider

    Okay first off, the custom protocol (Thanks Comphenix!):
    https://gist.github.com/aadnk/8016437

    Then initalize the protocol to something like this:

    Code:java
    1. new TinyProtocol(<plugin>) {
    2. @Override
    3. public Object onPacketOutAsync(Player sender, Object packet) {
    4. if (packet instanceof PacketPlayOutSpawnEntityLiving) {
    5. try {
    6. PacketPlayOutNamedEntitySpawn packet1 = new PacketPlayOutNamedEntitySpawn();
    7. Field field1 = packet1.getClass().getDeclaredFields()[0];
    8. field1.setAccessible(true);
    9.  
    10. // Or field "a"
    11. Entity entity = ((CraftWorld) sender.getWorld()).getHandle().getEntity(field1.getInt(packet1));
    12.  
    13. if (entity != null && entity instanceof EntityLiving) {
    14. packet = new PacketPlayOutNamedEntitySpawn();
    15.  
    16. // Something
    17. }
    18.  
    19. } catch (Exception e) {
    20. // Don't change the packet
    21. System.out.print("Error!");
    22. System.out.print(e.getLocalizedMessage());
    23. }
    24. }
    25. return super.onPacketOutAsync(sender, packet);
    26. }
    27. };



    Then the entity, you can use ID's or whatever system you prefer:
    Code:java
    1. public class CustomMob extends EntityCreature implements IAnimal {
    2. public String name = "Custom Player";
    3. public int id = 1;
    4.  
    5. public CustomMob(World world) {
    6. super(world);
    7. }
    8.  
    9. @Override
    10. public boolean bk() {
    11. // Disable default AI
    12. return true;
    13. }
    14.  
    15. @Override
    16. public String getName() {
    17. return name;
    18. }
    19. }


    In the TinyProtocol you can check the if the entity instanceof your CustomMob, if so, edit the packet.
    And you can do the same instanceof check in InteractListener.

    Then tell the server you are using this mob, else the client would crash due to unknown mob:
    Code:java
    1. // 120 is a villager
    2. addEntity(CustomMob.class, "Custom Mob", 120);

    Code:java
    1. @SuppressWarnings("unchecked")
    2. public void addEntity(Class<?> mob, String name, int id) {
    3. try {
    4. Field cField = EntityTypes.class.getDeclaredField("c");
    5. Field dField = EntityTypes.class.getDeclaredField("d");
    6. Field fField = EntityTypes.class.getDeclaredField("f");
    7. Field gField = EntityTypes.class.getDeclaredField("g");
    8.  
    9. cField.setAccessible(true);
    10. dField.setAccessible(true);
    11. fField.setAccessible(true);
    12. gField.setAccessible(true);
    13.  
    14. HashMap<String, Class<?>> c = (HashMap<String, Class<?>>)cField.get(null);
    15. HashMap<Class<?>, String> d = (HashMap<Class<?>, String>)dField.get(null);
    16. HashMap<Class<?>, Integer> f = (HashMap<Class<?>, Integer>)fField.get(null);
    17. HashMap<String, Integer> g = (HashMap<String, Integer>)gField.get(null);
    18.  
    19. c.put(name, mob);
    20. d.put(mob, name);
    21. f.put(mob, id);
    22. g.put(name, id);
    23.  
    24. } catch (Exception e) {
    25. e.printStackTrace();
    26. System.out.print("Error while adding \"" + name + "\" entity!");
    27. }
    28. }


    And finally spawn your player/mob:
    Code:java
    1. public static CustomMob spawnMob(Location location, String name) {
    2. // NMS world
    3. World world = ((CraftWorld) location.getWorld()).getHandle();
    4.  
    5. // Creating and setting mob & location
    6. CustomMob customMob = new CustomMob(world);
    7. customMob.setLocation(location.getX(), location.getY(), location.getZ(), location.getPitch(), location.getYaw());
    8.  
    9. // Adding it
    10. world.addEntity(customMob);
    11.  
    12. return customMob;
    13. }


    And you're done! Of course this is unfinished code and stuff, but the mob should work, only thing needed is the protocol working. I'm not exacly sure, but I hope you'll figure it out!
    Here is some help too: PacketPlayOutNamedEntitySpawn

    I hope this helped you alot! If you have any questions please tahg me!
     
    Waffletastic likes this.
  29. Offline

    Jogy34

    You're the first person to ever say that (which really surprises me). I purposely spelled it incorrectly as the entities don't fit the definition of a protocol but I couldn't think of a better name.

    It doesn't matter if you have multiple player entities with the same name. This happens all the time in my plugin and there are no problems with it.

    It also doesn't look like you would be able to set the name of the player through your method or that it would even make the entity look like a player.
     
  30. Offline

    TheTinySpider

    Jogy34 Yes you can, all the way at the top there is a piece of protocol that will do that for you. But yea, you need to look into making fake GameProfiles, but the protocol code is just a pseudo.
     
Thread Status:
Not open for further replies.

Share This Page