Solved [Difficult] All about NPC

Discussion in 'Plugin Development' started by Marcoral, Sep 12, 2016.

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

    Marcoral

    Hello. I am making highly advanced RPG server and currently I got stuck with NPCs. I have already read some threads about it, but it still not solute my problem. What I want to do is to make something citizens-like, but I wish I can customize these NPCs also with packets (for example I want NPCs which can give player a quest to have glowing effect. Of course different players can accept different quests, so I want to sollute it with packets. In addition I wish I was able to customise NPCs equipment depending to various players. It would be nicely too if some of NPC would be invisible to some of players etc.). Currently I still don't know much about NPCs. So could you give me a little compendium how to spawn "server-side" NPCs, how to prevent it for being hurt and, finally, how to customise it for every client? Thank you a lot :)
     
    okqi likes this.
  2. Offline

    Zombie_Striker

    Are you looking to get frustrated and confused, cause this is how you get frustrated and confused.

    Before I tell you what you need to do, you need to know a couple things:
    To make your NPC, do the following:
    1. Create a generic NPC object. This will contain the NPCs location, Skin UUID, and any other information all npcs will have.
    2. Create specific NPCs that extend the Generic NPC. This will be the NPCs with Dialog, effects, animations, ect.
    3. Whenever an NPC does something (move, attack, play effect), send the appropriate packets.
    All the rest (invisibility, clothing, "glowing effects") can be done once you set up the NPC.

    If you have any trouble or questions, post a reply and remember to tahg me. (click tahg User towards the bottom of this post.)
     
  3. Offline

    Marcoral

    @Zombie_Striker
    OK, I got my first NPCs spawned.

    here's the code:

    Code:
    private EntityPlayer npc;
    npc = new EntityPlayer(nmsServer, nmsWorld, new GameProfile(UUID.fromString("randomUUIDhere"), "myNicknameHere"), new PlayerInteractManager(nmsWorld));
    
    onEnable void:
    Code:
    MinecraftServer nmsServer = ((CraftServer) Bukkit.getServer()).getServer();
    WorldServer nmsWorld = ((CraftWorld) Bukkit.getWorlds().get(0)).getHandle();
    and in PlayerJoinEvent:
    Code:
    npc.setLocation(e.getPlayer().getLocation().getX(), e.getPlayer().getLocation().getY(), e.getPlayer().getLocation().getZ(), 0, 0);
    PlayerConnection connection = ((CraftPlayer) e.getPlayer()).getHandle().playerConnection;
    connection.sendPacket(new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.ADD_PLAYER, npc));
    connection.sendPacket(new PacketPlayOutNamedEntitySpawn(npc));
    But now I have some questions for you:
    Thus far I thought that packets are only something like "graphic overlay" to client and doesn't affect in any way to server's data (for example: I have diamond helmet equipped and server send packet that helmet is leather. So client displays leather helmet, but in fact it will behave like diamond one, right?
    Everywhere I found info NPCs are spawned with packets, so, following my way of thinking, there exists only client-side. How the server would know then in which NPC player is interacting? I would be very greatful if you can give me simple snippet of code, link to good tutorial solluting my doubts or simply pointing my mistakes of thinking. Thank you!


    @Edit:
    I looked onto Citizens code and it looks like it uses PlayerInteractEntityEvent to detect interact with NPCs, but for me it doesn't work. I tried to do it also with packets (using ProtocolLib) and event PacketType.Play.Client.USE_ENTITY is working, but I just want to know why PlayerInteractEntityEvent doesn't work for me. :(
     
    Last edited: Sep 13, 2016
  4. Offline

    Zombie_Striker

    @Marcoral
    You have to use protocol lib in order to detect NPC interactions. If the entity does not exist (like your NPC), then the event will not be called. You will need to use protocol lib in order to listen for the Use_Entity packet, check if the ID of the entity is equal to the NPC, and if so, do what you want.
     
  5. Yeah, If I'm completely honest. I need some help with NPCs/EntityPlayers/CustomEntitys myself. Watching this thread.
     
  6. Offline

    I Al Istannen

    @Marcoral
    The entity doesn't exist.
    The client thinks it does, as the server send a packet saying "Hey, there is a new player spawned. Here is his data".

    Now the client thinks:
    "Cool, I better display the new Player" and does so. Interaction with him is handled on the client side and then send to the server:

    "Hey server, I right clicked (used) the player here, his entity id is xxx".

    The entity id is unique for every entity as long as the server is not restarted. It just counts upwards. It is used to communciate WHICH entity has moved, died, performed an action, was hit, etc. Otherwise you would have to identify the entity in a different way (like UUID), but that is unnecessary data and quite new.

    Now the server hears the client say that but doesn't know any entity with that id (as it is only spawned at the client side) and therefore just throws the packet away. Which means it won't fire the PlayerInteractEntityEvent, as there is no entity. What would you expect the getEntity method to return? There is nothing it can.

    To still hear the client say "Hey, I used that entity there", you will need to intercept the packet yourself and see what the entity ID the client transmits is. Then check that and validate the player is near. ProtocolLib makes this nearly as easy as using a Bukkit listner.
     
  7. Offline

    Marcoral

    @Zombie_Striker
    @I Al Istannen

    Yes, I fully understand what are you talking about (I write this by myself in my second post).
    So, if I have understood you well, I have to spawn main NPC in server-side, and then customize it with packets, depending on client's needings, yes?

    But there is my problem. I simply don't know how to spawn that entity. Normally to spawn server-side entities I would use something like

    Code:
    .spawn(location, EntityType.PLAYER.getEntityClass());
    .spawnEntity(location, EntityType.PLAYER);
    like in this thread, but I know it won't work. So how to spawn such an entity?
     
  8. Offline

    Zombie_Striker

    @Marcoral
    You can't spawn that entity. If you spawn any entity with the same Id as your NPC, it will override your NPC. Instead, use Protocol Lib to listen for incoming packets and modify the changes yourself.
     
  9. Offline

    Marcoral

    @Zombie_Striker
    I al Istannen wrote that entity id is unique for every entity. What did you meant by "If you spawn any entity with the same Id as your NPC"?

    My idea was to spawn NPC to server-side once (for example in onEnable void) and then only send packets to clients to describe how this NPC looks. Is my thinking correct?

    Or otherwise - does the possibility to spawn such a server-side NPC exists at all? I'm still totally confused how should it look like...
     
    Last edited: Sep 13, 2016
  10. Offline

    I Al Istannen

    @Marcoral
    If you send a packet with the same entity id, it will overwrite the current one.
    Let's say you spawn a Pig on the server (world#spawnEntity(EntityType.PIG)) and it's entity id (#getEntityID) is 10.

    Now, if you send a spawn packet with entity ID 10, it will still be a pig for the server, but a different type for the client. (May need to send a destroy packet first).

    But what you want isn't clear to me. The whole point is to spawn it client side, isn't it?

    Why does the NPC need to be server side?
     
  11. Offline

    Marcoral

    @I Al Istannen
    Let's say that I want this NPC for example to move to another location. I'll have to loop all players and send them packet informing about new location of this entity, wouldn't I?
    Generally I thought that main, server-side, NPC would improve clients-server synchronisation.

    How without server-side NPC can I get it's entity number?
    My code looks like:

    onEnable() void:
    Code:
    MinecraftServer nmsServer = ((CraftServer) Bukkit.getServer()).getServer();
    WorldServer nmsWorld = ((CraftWorld) Bukkit.getWorlds().get(0)).getHandle();
    npc = new EntityPlayer(nmsServer, nmsWorld, new GameProfile(UUID.randomUUID(), "Tom"), new PlayerInteractManager(nmsWorld));
    npc.setLocation(0, 0, 0, 0, 0);
    
    PlayerJoinEvent e:
    Code:
    PlayerConnection connection = ((CraftPlayer) e.getPlayer()).getHandle().playerConnection;
    connection.sendPacket(new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.ADD_PLAYER, npc));[/I]
    connection.sendPacket(new PacketPlayOutNamedEntitySpawn(npc));

    (all in the same, Main class)
    So how the server will know that clicked NPC's entity number is my Tom's number?
     
  12. Offline

    Zombie_Striker

    Nope. Whether it is with plugins or the main server, (you cane make) animals tick at the same rate.

    You have to store that value somewhere, such as a field like...
    Code:
    public int NPC_Id = [the id];
     
  13. Offline

    I Al Istannen

    @Zombie_Striker @Marcoral
    EntityPlayer#getID() to get the entity id.

    Subclass EntityPlayer and modify the methods setPosition to invoke super and then send the appropiate packets to all players. Maybe it sends it automatically though, test it.
     
  14. Offline

    Marcoral

    @Zombie_Striker
    @I Al Istannen

    OK, I'll use packets then. I have also created a field which holds NPC ID and it works seamlessly. I haven't created subclass with that setPosition method because it was just an example of what NPC may do, but for this moment it's not that necessary for me. But I have question about changing skin - is there a posibility to change skin of already shown entity or would I have to despawn it and then spawn it with another UUID? But wouldn't ID change then...? :/
     
  15. Offline

    Marcoral

    @timtower @JanTuck
    Guys, I'm not dumb. I don't want you to spoonfeed me, but I just can't find proper tutorial.
    JanTuck it would work only once. My problem is, that I want to change skin of already "spawned" NPC. And yes, I know that I can just despawn old NPC and spawn new one with correct skin to player, but then the ID of NPC would change and I don't want that, because I want to detect it later and associate with specific NPC-interract action.
     
  16. Offline

    I Al Istannen

    @Marcoral
    As far as I know the skin data is transmitted in the AddPlayer / SpawnNamedEntity packet. I doubt you can change that without respawning the NPC. Though that doesn't mean it changes it entity id! It depends on how you created the entity.
     
  17. Offline

    Marcoral

    @I Al Istannen
    I created the entity using packets just like in the video. I thought it was the only option...
    I see, that you have also replied in my previous thread, but my problem is still unresolved.

    So, just to clarify, here is short description of my actual problem:

    I have spawned a NPC with Steve skin. Now I want it to change its skin, so Marcoral would see, that this NPC has eg. Notch skin. Everyone but Marcoral should still see that NPC's skin is Steve.

    And I still can't find the solution. It's also important not to change entity ID.
     
  18. Offline

    JanTuck

    Why not just reset the variable when you replace the entity?

    Sent from Tapatalk
     
  19. Offline

    I Al Istannen

    @Marcoral
    How do you spawn it? If you just use packets, you can just send a destroy and named entity spawn packet with the same id. I have no motication to show a video if two lines from you can teach me the same.
    Did you use EntityPlayer or not?
     
  20. Offline

    Marcoral


    It looks like:

    Code:
    private EntityPlayer npc;
    npc = new EntityPlayer(nmsServer, nmsWorld, new GameProfile(UUID.fromString("randomUUIDhere"), "myNicknameHere"), new PlayerInteractManager(nmsWorld));
    How could I spawn NPC another way? Everytime I'm doing something I try to learn as much as possible. I would be very grateful for more informations! :)
     
  21. Online

    timtower Administrator Administrator Moderator

    Moved posts to plugin development.
     
  22. Offline

    I Al Istannen

    @Marcoral
    Okay, I had a look at it. You need a signed skin *sigh*. You can get this from the mojang API. There are other ways to get it through code. LibsDisguise does this, I think here.

    If you have this, you can use these methods:
    Code:java
    1. private void spawn() {
    2. PacketPlayOutNamedEntitySpawn spawn = new PacketPlayOutNamedEntitySpawn(this);
    3.  
    4. PacketPlayOutPlayerInfo info = new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.ADD_PLAYER, this);
    5.  
    6. sendPackets(info);
    7. sendPackets(spawn);
    8. }
    9.  
    10. private void despawn() {
    11. PacketPlayOutEntityDestroy entityDestroy = new PacketPlayOutEntityDestroy(getId());
    12. PacketPlayOutPlayerInfo info = new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.REMOVE_PLAYER, this);
    13.  
    14. sendPackets(info);
    15. sendPackets(entityDestroy);
    16. }
    17.  
    18. private void setSkin(String skin, String signature) {
    19. getProfile().getProperties().put("textures", new Property("textures", skin, signature));
    20. }
    21.  
    22. private void sendPackets(Packet packet) {
    23. Bukkit.getOnlinePlayers().forEach(player -> sendPacket(player, packet));
    24. }
    25.  
    26. private void sendPacket(Player player, Packet packet) {
    27. System.out.println("Sending to " + player.getDisplayName());
    28. ((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet);
    29. }


    Just rudimentary methods, just enough to spawn and despawn it, as well as changing the skin. The class they are in extends EntityPlayer, but you can move the methods out of the subclass. And you don't need the subclass.

    Then you can call it like this:
    Code:
    thing.despawn();
    String skin = "eyJ0aW1lc3RhbXAiOjE0NzQzOTAzMjMyMzgsInByb2ZpbGVJZCI6IjQ1NjZlNjlmYzkwNzQ4ZWU4ZDcxZDdiYTVhYTAwZDIwIiwicHJvZmlsZU5hbWUiOiJUaGlua29mZGVhdGgiLCJzaWduYXR1cmVSZXF1aXJlZCI6dHJ1ZSwidGV4dHVyZXMiOnsiU0tJTiI6eyJ1cmwiOiJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzEzZTgxYjllMTlhYjFlZjE3YTkwYzBhYTRlMTA4NWZjMTNjZDQ3Y2VkNWE3YTFhNDkyODAzYjM1NjFlNGExNWIifSwiQ0FQRSI6eyJ1cmwiOiJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzIyYjljNWVhNzYzYzg2ZmM1Y2FlYTMzZDgyYjBmYTY1YTdjMjI4ZmQzMjFiYTU0NzY2ZWE5NWEzZDBiOTc5MyJ9fX0=";
    String sign = "kDEF9RD1V856shQCah8pFx735czDzYDsvPs8hZbovQU4Z/LaAwlGRkgYjSET3VYySRTLqC7+mLNSCnjzPZLG/SSb3eUsCozEDXH5rYYphx+j7srkcbypUZZ9P+AvDu4391q/ohEvw/mzTENkrfBdLYql7N16GnU9O7TlQLNm2w9Yhsxk4aoJDvsSw0vExr0dh+mmrthCyx3VpPuNq0wHwjRt0/HRCS9a0dGhmgCG/xorthRmQAS4SXQY/htlfff9DCjUpMetI+V7JL4dMgRoREQsKij8ta7eS98C9D6am+YM6NWVemmfxki9rOyYr0oyAEs6qyVNFHGpCGZLa/LqNIs45zk1cuZhYMWQT9evbt+UXZuqqQDO/V6dsEgdlkrV9g5K10yfVJcr2ViuWxsXFnkf+aMX4mvO2M5vtF66TaBsKbgOfTRtujy3GszCB1/b72gwg74OK9BpgNpx4wtKUKeKs3ehPX9JbeqhmjgLfW7eD05fA2n8LeZYfMH6/RaXXC4I6JhxPsPk/cqKDn8aGkR+tDoEwKdtwAPjwuTschWkCKAkWzBZ67Mmvr7KIOnsqFig6UPPsSLNCyWT0DE1SY4BkytNmuolsAF8qS6kJWFfNosTJnnofed4soB8w/JCputIsl7/dg8Gcci36dbFQu3hmqGzcPlmswYJfq/bzTk=";
    thing.setSkin(skin, sign);
    
    thing.spawn();
    You will need the signature and skin for this, so find a way to get it. This one is the example on the Wiki.vg page linked and looks like a skeleton.
     
    Marcoral likes this.
  23. Offline

    Marcoral

    @I Al Istannen
    I LOVE YOU MAN! With just some more more researches I made, I finally have done what I wanted! Your post was a big clue for me, but you missed one, but very important detail:
    setSkin void should look like:
    Code:
    private void setSkin(String skin, String signature) {
        getProfile().getProperties().removeAll("textures");
        getProfile().getProperties().put("textures", new Property("textures", skin, signature));
    }
    otherwise changing skin would be available only once per Client-NPC.

    Also thanks to others of you for time you spent to help me. The thread is finally solved. Yay!
     
    okqi likes this.
  24. Offline

    I Al Istannen

    @Marcoral
    Thought that would overwrite it like it is done for a map. Guess not :p
    Thanks for that :)

    Glad I could help though!
     
Thread Status:
Not open for further replies.

Share This Page