Solved NPC Mount: fake with packets or use real entity?

Discussion in 'Plugin Development' started by Totom3, Apr 10, 2015.

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

    Totom3

    Hello there,

    I am making a plugin that extends Citizens. I want to give the NPCs the ability to sit down. I see two solutions for this:

    1) Send a packet to nearby players telling them the NPC is riding a fake entity.
    Pros:
    - Because the vehicle entity is fake, it cannot move, die, get picked up, make sounds, etc...
    - I have more control over it.
    Cons:
    - I need to reproduce the server's role: send the packet to nearby players when the NPC sits, and also send it to incoming players that weren't there at first.

    2) Create an entity and set it's passenger to the NPC.
    Pros:
    - Easy: I only need to set the passenger.
    Cons:
    - The entity used as vehicle may die, produce sounds, move, be picked up.
    - In order to prevent these side-effects, I need to cancel multiple events, packets, etc... Which makes it hard in the end.
    - Vehicle will be ticked uselessly.
    - Other plugins will detect it as well.

    So to summarize, I'm not asking how to achieve this. I'd like to hear some advice from you guys, on which approach I should go for.

    Thanks in advance :D
     
    Last edited: Apr 11, 2015
  2. Offline

    teej107

    And you don't want this to happen?
     
    mine-care likes this.
  3. Offline

    Totom3

    @teej107 Sorry, I meant the entity used as vehicle. There's no need to update it as I don't want the players to see it in any case. I just want to make the NPC sit down (e.g. on a chair). It's similar to what chairs plugins achieve, but with NPCs. For the info, they use an arrow and teleport it inside the stair block so that you can't see it, but this may produce some side-effects.
     
  4. Offline

    Totom3

    Bumpidado!
     
  5. Offline

    teej107

    @Totom3 I would try and go with the Packets
     
    mine-care and Totom3 like this.
  6. Offline

    nverdier

    @Totom3 This solved now? If so, please mark it as that.
     
  7. Offline

    Totom3

    I knew someone would end up dropping that.. =.='

    No. I finished implementing it using packets today and I'm still experimenting with it. Why is it such a problem when threads aren't marked as solved? I'm still open to feedback.
     
  8. Offline

    nverdier

  9. Offline

    Totom3

    Alright, thanks.

    So I think I got everything, except for one part. The vehicle entity is always facing at yaw:0 and pitch:0, which makes the npc sit in a odd position. I explicitly set the vehicle's yaw and pitch; didn't work. I sent a Entity Look packet, didn't work either. My client still sees the squid/chicken (vehicle) facing in the wrong directions. Just to help you visualize:
    NPC Sitting (on top of squid) (open)
    [​IMG]
    NPC Sitting (on top of chicken) (open)

    [​IMG]

    So I'm not sure whether I forgot something, or did something wrong. I know for sure that the packets are being sent, in the following order:

    1. NPC spawn (by Citizens)
    2. Vehicle spawn
    3. Vehicle look (doesn't work)
    4. NPC mounting the vehicle

    EDIT: I didn't make them invisible to show what's happening.

    EDIT 2: below is the code I made for sitting the NPC down.
    Code for sitting NPC down (open)
    Code:
    public boolean sitDown(NPC npc, Location loc) {
        ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager();
    
        Entity handle = ((CraftEntity) npc.getEntity()).getHandle();
        int npcID = handle.getId();
        if (vehicles.containsKey(npcID)) {
            throw new IllegalArgumentException("NPC " + npc.getName() + " is already sitting.");
        }
    
        // Ignore the checks above, the problem is in the following lines:
    
        EntityLiving vehicle = makeVehicle(handle.getWorld());
        vehicles.put(npcID, vehicle.getId());
    
        prepareToSpawn(npc, vehicle, loc);
    
        PacketContainer spawnPacket = makeSpawnPacket(vehicle);
        PacketContainer attachPacket = makeSitPacket(npcID, vehicle.getId());
        PacketContainer lookPacket = makeLookPacket(npc, vehicle);
    
        List<Player> entityTrackers = protocolManager.getEntityTrackers(handle.getBukkitEntity());
        for (Player p : entityTrackers) {
            try {
            protocolManager.sendServerPacket(p, spawnPacket, false);
            protocolManager.sendServerPacket(p, lookPacket, false);
            protocolManager.sendServerPacket(p, attachPacket, false);
            } catch (InvocationTargetException ex) {
            return false;
            }
        }
    
        return true;
    }
    
    private EntityLiving makeVehicle(World world) {
        // I tried with EntityChicken, didn't work either. Therefore I don't think it's related to the type of the entity
        return new EntitySquid(world);
    }
    
    private EntityLiving prepareToSpawn(NPC npc, EntityLiving vehicle, Location loc) {
        // Here setting the yaw and pitch don't seem to change anything
        vehicle.setLocation(loc.getX(), loc.getY() - 0.45, loc.getZ(), loc.getYaw(), loc.getPitch());
        vehicle.setInvisible(INVISIBLE); // false for debugging, will be true when fixed
        return vehicle;
    }
    
    private PacketContainer makeSpawnPacket(EntityLiving vehicle) {
        // SPAWN_TYPE = PacketType.Play.Server.SPAWN_ENTITY_LIVING
        return new PacketContainer(SPAWN_TYPE, new PacketPlayOutSpawnEntityLiving(vehicle));
    }
    
    private PacketContainer makeLookPacket(NPC npc, EntityLiving vehicle) {
        Location loc = npc.getEntity().getLocation();
        PacketPlayOutEntityLook packet = new PacketPlayOutEntityLook(
            vehicle.getId(),
            (byte) (loc.getYaw() * 255 / 360),
            (byte) (loc.getPitch() * 255 / 360),
            false
        );
        // LOOK_TYPE = PacketType.Play.Server.ENTITY_LOOK;
        return new PacketContainer(LOOK_TYPE, packet);
    }
    
    private PacketContainer makeSitPacket(int npcID, int vehicleID) {
        WrapperPlayServerAttachEntity wrapper = new WrapperPlayServerAttachEntity();
        wrapper.setLeash(0);
        wrapper.setEntityId(npcID);
        wrapper.setVehicleId(vehicleID);
        return wrapper.getHandle();
    }
     
    Last edited: Apr 14, 2015
  10. Offline

    Totom3

    Alright, solved the damn thing finally.

    Solution was simple: entity look packet concerns the body, not the head (facepalm). Use Entity Head Rotation packet instead.
     
Thread Status:
Not open for further replies.

Share This Page