Solved Selective player transparency

Discussion in 'Plugin Development' started by sirrus86, Jan 4, 2015.

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

    sirrus86

    I'm looking for help on how to proceed with a rather specific situation. Ultimately what I want to accomplish is selective player transparency, in short making specific players semi-transparent but not invisible. I know that using scoreboard this is relatively easy, but what I'm trying to accomplish may be too specific for this route.

    Let's say we have players A, B, and C:
    Player A is normal
    Player B drank an invisibility potion
    Player C is marked to be semi-transparent

    What I want is for players A & B to be able to see player C as semi-transparent, player C to see player A as normal, and neither players A nor C can see player B (except for his equipment, so normally invisible). As an added (though optional) bonus, I'd like this to work without interfering with anything else that may use the scoreboard system.

    My plugin uses @Comphenix 's ProtocolLib for modifying/creating packets and I am semi-comfortable with digging through NMS. I'm mostly looking to see if this is possible, and if so maybe an idea of where to start.

    Any help would be appreciated.
     
  2. Offline

    ColonelHedgehog

  3. Offline

    sirrus86

    I appreciate the help, but this doesn't accomplish what I'm trying to do.

    Using teams alone, in order for player C to appear semi-transparent, all three players would need to be on the same team. The problem is this also allows players A and C to see player B as semi-transparent instead of invisible, which I don't want to happen.
     
  4. Offline

    xMakerx

    Isn't there a hidePlayer method included with the Player class?
     
  5. Offline

    sirrus86

    @xMakerx Yes, or I could use packets to just outright hide player B from the other two, but this would also hide their floating equipment (which I don't mind but users of my plugin might). If that becomes my only option then I may go that route, I'm just trying to see if an alternative may exist.
     
  6. @sirrus86 I wouldn't think this is an option, since you need to be on the same team to see, and the canSeeInvisible setting is per-team, not per-player. Hiding outright seems like the only possibility.
     
  7. Offline

    xMakerx

    How about giving the player an Invisibility Potion effect to keep their armor on, store that you gave them that effect, and block attempts for people to use milk with the effect on.
     
  8. @xMakerx That's not quite what he's after - he's looking for some people who have the invisibility to be visible as a semi-transparent player, and other people with invisibility to be actually invisible (with visible armour)
     
  9. Offline

    sirrus86

    @ColonelHedgehog @xMakerx @AdamQpzm Thanks for your help guys, I took some time and I think I figured out a way to make this work.

    In case anyone else attempts the same thing, my solution involves packets, specifically PacketPlayOutScoreboardTeam.

    Keep in mind I'm using ProtocolLib to work with packets.

    Code:
    private final ProtocolManager pm = ProtocolLibrary.getProtocolManager(); //Hooks into ProtocolLib
    private Set<UUID> ghosts = new HashSet<UUID>(); //Keeps track of players who should be semi-transparent
    
    public GhostMaker(Plugin plugin) {
        pm.addPacketListener(new PacketAdapter(plugin, PacketType.Play.Server.SPAWN_ENTITY) { //Listen for anytime a player may see another entity
            @Override
            public void onPacketSending(PacketEvent event) {
                Entity entity = event.getPacket().getEntityModifier(event).read(0);
                if (entity != null
                        && entity instanceof Player
                        && ghosts.contains(entity.getUniqueId()) { //Player can potentially see a ghost
                    showAsGhost(event.getPlayer(), (Player) entity); //Render the ghost as semi-transparent
                }
            }
        });
    }
    
    public void addGhost(Player player) {
        if (ghosts.add(player.getUniqueId())) {
            player.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, Integer.MAX_VALUE, 2), true); //Apply invisibility to the ghost
            showAsGhost(player, player); //Let ghost see themselves as a ghost
            for (Player viewer : pm.getEntityTrackers(player)) { //Send a packet to anyone who can "see" the ghost
                showAsGhost(viewer, player);
            }
        }
    }
    
    public void removeGhost(Player player) {
        if (ghosts.remove(player.getUniqueId())) {
            player.removePotionEffect(PotionEffectType.INVISIBILITY); //Remove invisibility
            for (Player viewer : Bukkit.getServer().getOnlinePlayers()) { //Send removal packets to every player (some that recv'd the addGhost packet may no longer be in range)
                PacketContainer packet = pm.createPacket(PacketType.Play.Server.SCOREBOARD_TEAM, true);
                packet.getStrings().write(0, viewer.getEntityId() + "." + player.getEntityId()); //Make the team name unique to both the viewer and the ghost
                packet.getIntegers().write(1, 1); //We are removing this team
                try {
                    pm.sendServerPacket(viewer, packet); //Only the viewer needs to be sent the packet
                } catch (InvocationTargetException e) {
                    e.printStackTRace();
                }
            }
        }
    }
    
    private void showAsGhost(Player viewer, Player player) {
        PacketContainer packet = pm.createPacket(PacketType.Play.Server.SCOREBOARD_TEAM, true);
        packet.getStrings().write(0, viewer.getEntityId() + "." + player.getEntityId()); //Make the team name unique to both the viewer and the ghost
        packet.getIntegers().write(1, 0); //We are creating a new team
        packet.getModifier().write(6, Lists.newArrayList(viewer.getName(), player.getName())); //Team only consists of the ghost and the viewer
        packet.getIntegers().write(2, 3); //Ghost can be seen and attacked by the viewer
        try {
            pm.sendServerPacket(viewer, packet); //Only the viewer needs to be sent the packet
        } catch (InvocationTargetException e) {
            e.printStackTRace();
        }
    }
    It's not perfect, but it so far seems to accomplish what I was after without interfering with actual teams ("/scoreboard teams list" shows no teams while ghosts are active).
     
    Last edited: Jan 5, 2015
    madmac, xMakerx and AdamQpzm like this.
Thread Status:
Not open for further replies.

Share This Page