[NMS] Player Entities and General NPCs

Discussion in 'Plugin Development' started by TheDummy, Jan 31, 2014.

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

    TheDummy

    Comphenix xTrollxDudex CaptainBern Garris0n Jogy34

    I've been searching for ways to spawn custom player (and mob entities) on the forums for about the past two weeks. In that time, I've become fairly familiar with Reflection, reading NMS, and packets. However, there are a few issues I can't seem to work out.

    I have managed to spawn entities using packets and even send animation packets and despawn them for specific players. However, I want to program in more functionality without moving to a prebuilt API (tried Citizens 2). The majority of the reason is to learn how to make a custom NPC API for myself.

    I came across a few topics where each one of the developers tagged above came across as grasping all of this really well. So hopefully, they can help.

    I need the ability for a walking animation (I tried messing with the PacketPlayOutRelMoveLook, but it gets complicated really fast for any movement greater than 4 blocks. From that reasoning, I started looking around for other methods such as replacing the PlayerConnection with an empty NetworkManager (it has a native move method and supports interactions/packet interception for entity interactions). It seems the best route for creating an NPC player (or mob) that can be programmed to do a lot of things.

    Unfortunately, I only understand the basic concept behind replacing the connection and a small bit of how to implement it correctly. To get the point now, I would like any code (pseudo or otherwise) or links to full code of pre-built APIs aside from Citizens that use the PlayerConnection replacement method.

    Here's a list of what I know so far for this method:
    • Extend EntityPlayer
    • ?
    • Use reflection to replace PlayerConnection and NetworkManager
    • ?
    • Spawn Entity using NMS server and everything magically works :)
    Thank you in advance for all help.
     
  2. TheDummy From what I understood (@xTrollxDudex said) that you will have to inject stuff to the client (I dont really understand that.... ) and its waaaaaaay too messy and stuff... I would suggest you to extend an entity (eg: EntitySkeleton) and then add to that entity some PathFinders or whatever you want... and then keep his ID (entity.getEntityId()) in some ArrayList, after that... when someone join - send them packet of NPC entity on that custom entity, its would make that entity looks exactly like player... with his motion and all :p
     
  3. Offline

    Garris0n

    I agree with this. I don't think adding players to the server is a very good idea, you'll probably end up breaking something.

    Also, in what sense is it more complicated over 4 blocks? Just use either Minecraft's pathfinders or the A* pathfinder found in the resources section.
     
  4. Someone_Like_You Not really, you don't have to inject any stuff inside any client to create npc's. It is true that you have to swap the NetworkManager and PlayerConnection with custom ones because the else the server will send packets to a null socket/null channel so it will throw npe's like crazy.

    TheDummy Spawning NPC's is fairly easy. Most people have trouble with the custom NetworkManager and PlayerConnection and a "NullChannel", while this is still easy to make.
    In EntityApi, we use some reflection to set/fix those.

    You will basically need to create a custom NetworkManager by just creating a new class and extending NetworkManager. Here is an example of what we use in EntityAPI. The FieldAccessor is a util we use, you don't need it you can just use Field instead.

    Then there's the NullChannel, this is a "netty thing", it's just a custom Channel so netty doesn't start to throw exceptions. Here you can see how I did it.

    And last but not least; The "NullPlayerConnection", this is to stop minecraft self from throwing exceptions, it's just ignores every method call, so when there's a packet send to the npc, it just ignores it. You can see the source here.

    And then to finish all this and make your NPC actually work, add this code to it's contructor:
    Code:java
    1. NetworkManager manager = new FixedNetworkManager();
    2. playerConnection = new NullPlayerConnection(server, manager, this);
    3. manager.a(playerConnection);


    Also, it looks like you're mixing up two diffrent ways of NPC spawning, when you're spawning NPC with the way I just provided, then you won't have to inject anything, if you are planning to use packets (notice: This is much harder to maintain because you have to resend the packets each time a client join/chunk loads/teleport etc...)

    If you are intrested in a way to spawn NPC's with packets then feel free to take a look at my WIP NPC-Lib (It's WIP, it's nowhere near finished but it should give you the basic idea.)
    Anyways, feel free to peak at the source: https://github.com/CaptainBern/NPC-Lib

    - Cap.

    Garris0n It's nearly impossible to use Minecrafts pathfinding for this because of 2 reasons: the Navigation can only be applied to entities which extend EntityInsentient and because if you do want to use it you'll have to recreate so many classes that it's just not worth it. As for the A* library, I've tried that one but it just looked all laggy.

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

    Garris0n

    CaptainBern What happens when the server doesn't get a keepalive from the fake entity?
     
  6. Garris0n Server doesn't care :p
    The fake entity won't even show up in the online player list, basically, only bukkit will care about it. (well that's what it looked like when I tested it)
     
    Garris0n likes this.
  7. Offline

    Garris0n

    Hm, I wonder if it's handled in one of the classes you're replacing. Either way it can't attempt to disconnect them, so I guess that's why it's alive.
     
    CaptainBern likes this.
  8. Garris0n Hmm, I'm not sure actually. If that would be the reason then it's probably because I'm overriding the Channel. I rather think it's because the minecraft server doesn't "know" about the fakeplayer. In EntityAPI we're using a hacky way to add entities to the world so maybe minecraft's internal have some kind of "list" with each player it has to check/ping etc. I'm not too sure about this, Comphenix probably knows more about this.
     
    Garris0n likes this.
  9. Offline

    TheDummy

    What would break if I added a player, in your opinion? The PacketPlayOutRelEntityMoveLook packet supports at maximum a distance of four blocks of relative movement. I've tried scheduling sync tasks to send packets every tick (and it works to a degree), but it feels like there should be a better method for moving the NPC with a packet.

    Speaking of null sockets, I saw one on a library somewhere, but you don't have it your guide below (which is very helpful). Do I need to make a custom null socket?

    The NPC-Lib is something I also came across. I believe you answered a player named @BladeBurner on his packet based human NPC library. Unless I'm missing it, I think you used a teleport packet (Packet 20 = PacketPlayOutEntity) as opposed to a relative move packet to send an NPC to a location. One of the reasons I'm trying to build my NPC lib is to have a smoother "walking" animation when I need to change location for an NPC.

    CaptainBern Comphenix The extending class method as it seems like it will create a more natural and interactive player entity opposed to sending display/animation packets to the client. Speaking of which, in another post you said in order to detect interaction with the custom entities, I need to intercept packets. Is it easier (or possible) with this method of creating custom entities without the use of a library like Protocolib?
     
  10. CaptainBern lol youre amazing.
    Garris0n youre amazing too :p forgot to tell that the idea of sending the packet on entityId was yours :)
     
    CaptainBern and Garris0n like this.
  11. Offline

    Garris0n

    Well, apparently less than I thought will break. It can cause issues with plugins that check if entities are players and do something with them, but other than that, it should be fine. As for the movements, the game considers anything more than four blocks a teleport, and thus uses a teleport packet for that. I don't really know what kind of better method you expect...the code is structured to run a game, not be manipulated by plugin developers :p
     
  12. TheDummy Yep indeed, I helped burnblader with his one.
    About the null socket, since minecraft switched over to netty I think a nullchannel will fullfill, I don't set the socket to null or so, never had a problem with it.

    About my lib, it's also packet based and will, in the future, contain a custom pathfinder algorithm and have a smooth walking animation. My library also has a PlayerInteractNPCEvent, which get's called when a player rights/left clicks an npc. My library isn't finished tho and I wouldn't suggect using it in a big plugin (or any plugin?). I need to do some fixing on the interaction-detection stuff. (which, in it's current state, could possibly damage servers)

    As for the intercepting of packets, yes, since 1.7 this is "super-easy", thanks to netty you can easily add your own handler to the player channel' pipeline and so detect incoming/outgoing packets. Comphenix has made a nice gist, which can be found here.

    Someone_Like_You Thank you sir :)
     
  13. Offline

    xTrollxDudex

    Glad to help (*cough cough* CaptainBern/Garris0n ;))
     
    CaptainBern likes this.
  14. Offline

    Garris0n

    Shh, you're blow the secret that we're all the same per-oops...
     
  15. Offline

    Jogy34

    I personally get around the other plugins checking for players by creating my own version of bukkit's Player entities by extending from CraftPlayer and replacing the bukkit entity with that. I then override any methods that I know cause problems from other plugins to do nothing but I create a similarly named method that does what the now useless method used to do in case I actually want to use that functionality (eg. making teleport() do nothing but a new method called tp() does what teleport() used to do). This unfortunately requires building with a slightly modified version of Craftbukkit to get around the duplicate health methods (doesn't require running the server with a modified Craftbukkit though). I also do some magic to be able to get around plugins finding my custom entities when using entity types. It would still work to find them using instanceof though.
     
    Garris0n likes this.
  16. Offline

    TheDummy

    CaptainBern Thank you for all your help. I will be trying to put all of this together in a few hours. Is there any documentation or source code for the Channel object? I see both you and Comphenix use the "pipeline()" method and then "addBefore()" method for packet interception.

    Also, Comphenix uses "onPacketInAsync()" and from the name does that mean I need to schedule tasks on the main thread if I need something done for the player? And should I be using "instanceof" to distinguish the packets?

    Sorry for so many questions, but I'm just trying to understand everything without copying and pasting code.

    Jogy34 Good idea. I was planning on doing something similar for certain aspects.
     
  17. TheDummy Yes indeed, this means you need to schedule on the mainthread (if you make any calls to the bukkitapi). As for the channel, feel free to copy the code example I gave you. And yes, instanceof should work. :)

    - Cap.
     
  18. Offline

    TheDummy

    CaptainBern Jogy34 Garris0n I feel very stupid right now, but how I get the player to spawn into the world. I remember seeing something on CraftWorld's spawn function, but I don't want to call that function unless I know for sure.

    Edit: Looking at some other posts, it looks like I need to cast the Bukkit world to a CraftWorld and use the "addEntity()" method. However, most of those posts also involve me "registering the entity." Is this the method I should be following? I have no clue how to register entities.
     
  19. Offline

    Jogy34

    Code:java
    1. nmsWorld.addEntity(yourCustomEntity);
     
  20. Offline

    TheDummy

    Jogy34
    Thank you for posting that. I stated in an edit about 1 min before your post that I also came across this function, but it had additional code for "registering" the NPC. In fact, I believe it may have been one of your posts where you register the entity. Does this function take care of that aspect or do I have to do that for this as well?

    I'm also running into issues actually creating an instance of the custom player entity. The constructor is as follows:
    Code:java
    1. public DerivedPlayer(MinecraftServer arg0,
    2. WorldServer arg1,
    3. GameProfile arg2,
    4. PlayerInteractManager arg3) {}


    CaptainBern I know how to create a GameProfile and cast the WorldServer object, but not the MinecraftServer or PlayerInteractManager object. I can't seem to find the forum post that had an example of this either (I've seen this constructor before).
     
  21. Offline

    Jogy34

    I made this a little bit ago. It should be able to help you out.
    http://forums.bukkit.org/threads/tutorial-1-7-creating-a-custom-entity.212849/

    Code:java
    1.  
    2. MinecraftServer.getServer() //get the MinecraftServer
    3. new PlayerInteractManager(((CraftWorld) loc.getWorld()).getHandle()) //create a PlayerInteractManager
    4.  


    My previous post was concerning registering as I saw your post before you added a few things.

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

    TheDummy

    Jogy34 I swear I was just looking at that thread. It's extremely helpful. From what I gather, the "registering" is just so it can be in the EntityTypes class right? So that the player can spawn from the custom entity ID? Amazing, I will definitely have to try that after I get this player NPC set up.

    Edit: Is that "MinecraftServer.getServer()" supposed to be used on the Bukkit server instance or ... ?

    Edit: Doh! I just looked at the NMS. It's a static function, so MinecraftServer.getServer() would return it without an instance of the object.

    CaptainBern

    Code:java
    1. // In NMS
    2. private Channel k;
    3. private SocketAddress l;
    4.  
    5. // Code used to reflect
    6. Field channelField = NetworkManager.class.getField( "k" );
    7. channelField.setAccessible( true );
    8. channelField.set( this , new NullChannel( null ) );
    9. channelField.setAccessible( false );
    10.  
    11. Field socketAddressField = NetworkManager.class.getField( "l" );
    12. socketAddressField.setAccessible( true );
    13. socketAddressField.set( this , null );
    14. socketAddressField.setAccessible( true );


    This is throwing a " FieldNotFoundException: k " when I construct the CustomPlayer entity. Any ideas as to why it would say this if the Channel field is named "k" ?

    Fixed it. Solution is:
    Code:java
    1. // Change getField to getDeclaredField
    2.  
    3. // Old
    4. NetworkManager.class.getField( "k" );
    5.  
    6. // New
    7. NetworkManager.class.getDeclaredField( "k" );


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

    xTrollxDudex

    TheDummy
    Use getDeclaredField

    Edit: Wow, that was fast.
     
  24. Offline

    TheDummy

    xTrollxDudex Thanks. I figured it out a few minutes before you posted through experimentation. Any idea why getDeclaredField() is able to get it, but getField() is not? (I'm going to go read the documentation and post an edit to this post if I figure it out.)

    Answer: getField() has access to public data members (including those of parent classes). getDeclaredField() has access to all data members (only of current class).
     
  25. Offline

    Jogy34

    Registering the entity tells the server about the entity and how to send the information about it to any clients.
     
  26. Offline

    TheDummy

    CaptainBern Garris0n xTrollxDudex Jogy34
    I want to thank you for all your help in getting the structure setup. I've managed to spawn the NPC and learn a little more NMS.
    ----------------------------
    I've started experimenting with the movement, but for large distances it teleports the NPC. I think I remember reading something about setting pathfinders? Does that apply to EntityPlayer instances? If so, how can I set those up (links to pre-made good tutorial threads are okay)?

    With packets, I could choose to send display packets to certain players. How do I set it so that only certain players can see the NPC? I also can't seem to find a "setCarriedItem" function in the Entity, EntityHuman, and EntityPlayer classes. Should I just broadcast a packet or is there actually one in there I'm not seeing?
     
  27. TheDummy for the item in hand: npc.getBukkitEntity().setItemInHand();
    For the packets etc, yes but you will need to capture the packets.

    The pathfinding stuff, the a* lib on here does the job quite well but needs some modification before it can work with your npc's.
     
    Garris0n likes this.
  28. Offline

    Jogy34

    I'm not sure about making only certain players see the NPC but for movement, what I do is I manually set the motX, motY, and motZ to make the entity move in the direction of a target block. Pathfinders don't work for player entities. And for the carried item, that's mapped to equipment index 0.
     
  29. Offline

    xTrollxDudex

    TheDummy
    If you move an entity more than 4 blocks, that's teleportation, no way to get around it. EntityPlayers can't have pathfinders, you will need to setup the algorithms yourself (but the last time I checked, it was possible to give pathfinders, not sure about now). You an send a PacketPlayOutDestroyEntity for the players that you don't want to see the NPC. EntityHuman (which is EntityPlayer's superclass, youvan direvtly access it), has "PlayerInventory inventory" at the top. PlayerInventory has an ItemStack[36] field named items, which you can set an index from a field named itemInHandIndex to your own ItemStack.

    Edit: Looks like I did way too much research and Cap came up with a better solution to set the item in hand :p
     
    Garris0n likes this.
  30. Offline

    Garris0n

    Tell me about it, here's what I did:
    1. Start going through the files trying to find out how to set the item.
    2. Find what you said.
    3. Realize that you can just use .getBukkitEntity().setItemInHand().
    4. Come back to respond and realize that CaptainBern already did.
    5. Cry.
     
    rbrick, CaptainBern and xTrollxDudex like this.
Thread Status:
Not open for further replies.

Share This Page