Allow invisible/hidden players to be attacked

Discussion in 'Resources' started by Comphenix, Dec 6, 2012.

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

    Comphenix

    Players that have been hidden with the built-in vanish API (hidePlayer and showPlayer) can't be attacked or otherwise interacted with, but sometimes that is an desirable effect.

    Maybe you want to create a more perfect invisibility effect (level two) that doesn't emit particles or show the player's armor. Or you want to fix this bug, preventing unmodified clients from showing invisible players.

    The method uses ProtocolLib, but it could probably be rewritten to use CraftBukkit code only. It was just a lot easier this way. You can download the full source code on GitHub. The classes themselves are licensed under the LGPL v.2.

    Here is the main class:
    Code:java
    1. public class ExampleMod extends JavaPlugin implements Listener {
    2. private ProtocolManager protocolManager;
    3.  
    4. public void onLoad() {
    5. protocolManager = ProtocolLibrary.getProtocolManager();
    6. }
    7.  
    8. public void onEnable() {
    9. PluginManager pm = getServer().getPluginManager();
    10. pm.registerEvents(this, this);
    11.  
    12. // This is where the magic happens
    13. protocolManager.getAsynchronousManager().registerAsyncHandler(
    14. new PacketAdapter(this, ConnectionSide.CLIENT_SIDE, Packets.Server.ARM_ANIMATION) {
    15.  
    16. @Override
    17. public void onPacketReceiving(PacketEvent event) {
    18. final int ATTACK_REACH = 4;
    19. Random rnd = new Random();
    20.  
    21. Player observer = event.getPlayer();
    22. Location observerPos = observer.getEyeLocation();
    23. Vector3D observerDir = new Vector3D(observerPos.getDirection());
    24.  
    25. Vector3D observerStart = new Vector3D(observerPos);
    26. Vector3D observerEnd = observerStart.add(observerDir.multiply(ATTACK_REACH));
    27.  
    28. Player hit = null;
    29.  
    30. // Get nearby entities
    31. for (Player target : protocolManager.getEntityTrackers(observer)) {
    32. // No need to simulate an attack if the player is already visible
    33. if (!observer.canSee(target)) {
    34. // Bounding box of the given player
    35. Vector3D targetPos = new Vector3D(target.getLocation());
    36. Vector3D minimum = targetPos.add(-0.5, 0, -0.5);
    37. Vector3D maximum = targetPos.add(0.5, 1.67, 0.5);
    38.  
    39. if (hasIntersection(observerStart, observerEnd, minimum, maximum)) {
    40. if (hit == null || hit.getLocation().distanceSquared(observerPos) > target.getLocation().distanceSquared(observerPos)) {
    41. hit = target;
    42. }
    43. }
    44. }
    45. }
    46.  
    47. // Simulate a hit against the closest player
    48. if (hit != null) {
    49. PacketContainer useEntity = protocolManager.createPacket(Packets.Client.USE_ENTITY, false);
    50. useEntity.getIntegers().
    51. write(0, observer.getEntityId()).
    52. write(1, hit.getEntityId()).
    53. write(2, 1 /* LEFT_CLICK */);
    54.  
    55. // Chance of breaking the visibility
    56. if (rnd.nextDouble() < 0.3) {
    57. toggleVisibilityNative(observer, hit);
    58. }
    59.  
    60. try {
    61. protocolManager.recieveClientPacket(event.getPlayer(), useEntity);
    62. } catch (Exception e) {
    63. e.printStackTrace();
    64. }
    65. }
    66. }
    67.  
    68. // Get entity trackers is not thread safe
    69. }).syncStart();
    70. }
    71.  
    72. // Source:
    73. // [url]http://www.gamedev.net/topic/338987-aabb---line-segment-intersection-test/[/url]
    74. private boolean hasIntersection(Vector3D p1, Vector3D p2, Vector3D min, Vector3D max) {
    75. final double epsilon = 0.0001f;
    76.  
    77. Vector3D d = p2.subtract(p1).multiply(0.5);
    78. Vector3D e = max.subtract(min).multiply(0.5);
    79. Vector3D c = p1.add(d).subtract(min.add(max).multiply(0.5));
    80. Vector3D ad = d.abs();
    81.  
    82. if (Math.abs(c.x) > e.x + ad.x)
    83. return false;
    84. if (Math.abs(c.y) > e.y + ad.y)
    85. return false;
    86. if (Math.abs(c.z) > e.z + ad.z)
    87. return false;
    88.  
    89. if (Math.abs(d.y * c.z - d.z * c.y) > e.y * ad.z + e.z * ad.y + epsilon)
    90. return false;
    91. if (Math.abs(d.z * c.x - d.x * c.z) > e.z * ad.x + e.x * ad.z + epsilon)
    92. return false;
    93. if (Math.abs(d.x * c.y - d.y * c.x) > e.x * ad.y + e.y * ad.x + epsilon)
    94. return false;
    95.  
    96. return true;
    97. }
    98.  
    99. private void toggleVisibilityNative(Player observer, Player target) {
    100. if (observer.canSee(target)) {
    101. observer.hidePlayer(target);
    102. } else {
    103. observer.showPlayer(target);
    104. }
    105. }
    106.  
    107. @Override
    108. public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
    109. Player observer = null;
    110. Player target = null;
    111.  
    112. // Get the target argument
    113. if (args.length > 0) {
    114. target = getServer().getPlayerExact(args[0]);
    115. } else {
    116. sender.sendMessage(ChatColor.RED + "This command requires at least one argument.");
    117. return true;
    118. }
    119.  
    120. // Get the observer argument
    121. if (args.length == 2) {
    122. observer = getServer().getPlayerExact(args[1]);
    123. } else {
    124. if (sender instanceof Player) {
    125. observer = (Player) sender;
    126. } else {
    127. sender.sendMessage(ChatColor.RED + "Optional parameter is only valid for player commands.");
    128. return true;
    129. }
    130. }
    131.  
    132. toggleVisibilityNative(observer, target);
    133. return true;
    134. }
    135. }


    I tried using the Bukkit Vector class, but it's a bit annoying as you have to clone it before you can modify it. So, I wrote my own:
    Code:java
    1. package com.comphenix.example;
    2.  
    3. import org.bukkit.Location;
    4. import org.bukkit.util.Vector;
    5.  
    6. public class Vector3D {
    7. /**
    8.   * Represents the null (0, 0, 0) origin.
    9.   */
    10. public static final Vector3D ORIGIN = new Vector3D(0, 0, 0);
    11.  
    12. // Use protected members, like Bukkit
    13. public final double x;
    14. public final double y;
    15. public final double z;
    16.  
    17. /**
    18.   * Construct an immutable 3D vector.
    19.   */
    20. public Vector3D(double x, double y, double z) {
    21. this.x = x;
    22. this.y = y;
    23. this.z = z;
    24. }
    25.  
    26. /**
    27.   * Construct an immutable floating point 3D vector from a location object.
    28.   * @param location - the location to copy.
    29.   */
    30. public Vector3D(Location location) {
    31. this(location.toVector());
    32. }
    33.  
    34. /**
    35.   * Construct an immutable floating point 3D vector from a mutable Bukkit vector.
    36.   * @param vector - the mutable real Bukkit vector to copy.
    37.   */
    38. public Vector3D(Vector vector) {
    39. if (vector == null)
    40. throw new IllegalArgumentException("Vector cannot be NULL.");
    41. this.x = vector.getX();
    42. this.y = vector.getY();
    43. this.z = vector.getZ();
    44. }
    45.  
    46. /**
    47.   * Convert this instance to an equivalent real 3D vector.
    48.   * @return Real 3D vector.
    49.   */
    50. public Vector toVector() {
    51. return new Vector(x, y, z);
    52. }
    53.  
    54. /**
    55.   * Adds the current vector and a given position vector, producing a result vector.
    56.   * @param other - the other vector.
    57.   * @return The new result vector.
    58.   */
    59. public Vector3D add(Vector3D other) {
    60. if (other == null)
    61. throw new IllegalArgumentException("other cannot be NULL");
    62. return new Vector3D(x + other.x, y + other.y, z + other.z);
    63. }
    64.  
    65. /**
    66.   * Adds the current vector and a given vector together, producing a result vector.
    67.   * @param other - the other vector.
    68.   * @return The new result vector.
    69.   */
    70. public Vector3D add(double x, double y, double z) {
    71. return new Vector3D(this.x + x, this.y + y, this.z + z);
    72. }
    73.  
    74. /**
    75.   * Substracts the current vector and a given vector, producing a result position.
    76.   * @param other - the other position.
    77.   * @return The new result position.
    78.   */
    79. public Vector3D subtract(Vector3D other) {
    80. if (other == null)
    81. throw new IllegalArgumentException("other cannot be NULL");
    82. return new Vector3D(x - other.x, y - other.y, z - other.z);
    83. }
    84.  
    85. /**
    86.   * Substracts the current vector and a given vector together, producing a result vector.
    87.   * @param other - the other vector.
    88.   * @return The new result vector.
    89.   */
    90. public Vector3D subtract(double x, double y, double z) {
    91. return new Vector3D(this.x - x, this.y - y, this.z - z);
    92. }
    93.  
    94. /**
    95.   * Multiply each dimension in the current vector by the given factor.
    96.   * @param factor - multiplier.
    97.   * @return The new result.
    98.   */
    99. public Vector3D multiply(int factor) {
    100. return new Vector3D(x * factor, y * factor, z * factor);
    101. }
    102.  
    103. /**
    104.   * Multiply each dimension in the current vector by the given factor.
    105.   * @param factor - multiplier.
    106.   * @return The new result.
    107.   */
    108. public Vector3D multiply(double factor) {
    109. return new Vector3D(x * factor, y * factor, z * factor);
    110. }
    111.  
    112. /**
    113.   * Divide each dimension in the current vector by the given divisor.
    114.   * @param divisor - the divisor.
    115.   * @return The new result.
    116.   */
    117. public Vector3D divide(int divisor) {
    118. if (divisor == 0)
    119. throw new IllegalArgumentException("Cannot divide by null.");
    120. return new Vector3D(x / divisor, y / divisor, z / divisor);
    121. }
    122.  
    123. /**
    124.   * Divide each dimension in the current vector by the given divisor.
    125.   * @param divisor - the divisor.
    126.   * @return The new result.
    127.   */
    128. public Vector3D divide(double divisor) {
    129. if (divisor == 0)
    130. throw new IllegalArgumentException("Cannot divide by null.");
    131. return new Vector3D(x / divisor, y / divisor, z / divisor);
    132. }
    133.  
    134. /**
    135.   * Retrieve the absolute value of this vector.
    136.   * @return The new result.
    137.   */
    138. public Vector3D abs() {
    139. return new Vector3D(Math.abs(x), Math.abs(y), Math.abs(z));
    140. }
    141.  
    142. @Override
    143. public String toString() {
    144. return String.format("[x: %s, y: %s, z: %s]", x, y, z);
    145. }
    146. }
     
  2. Offline

    chasechocolate

    Kewl! Totally going to add this to my plugin...
     
  3. Offline

    HamOmlet

    Simply amazing! This will really come in handy for a particular project I'm working on -- thanks!
     
    Comphenix likes this.
Thread Status:
Not open for further replies.

Share This Page