From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Justin Date: Wed, 24 Aug 2022 05:17:20 -0700 Subject: [PATCH] Paper PR - Add Entity hidden by default flag Adds a boolean to the server Entity controlling if the entity should be hidden by default. The TrackedEntity maintains a set of all players that will be able to see the entity even when it is hidden by default. This set is modified when Player#showEntity and Player#hideEntity are invoked. Changes are made to the way that TrackedEntity updates players when the entity is hidden by default - if a player is not present in the above set, the update is abandoned. This functionality is expanded when a Player is hidden by default. The player will send out PlayerInfo packets to hide themselves from other Players when hiddenByDefault. There remains a discrepancy when trying to getHiddenPlayers - players that are hidden by default are currently not returned from this method, only those hidden with hideEntity or hidePlayer. Hiding entities by default also respects precedent of keeping entities hidden when other plugins explicitly hide them from a player. If an entity is hidden by default and by a plugin, the entity must have the plugin that hid them from a player show them in order to be shown to a player again. If no plugin had previously hidden the entity before the entity was hidden by default, then any plugin that attempts to show the entity to a player will succeed in doing so. diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java index 6ae3f09f01e7ad72d46aeb950cca83b0d2a8d88b..07985de694f76e55f8eef2ac7dca6e404980519d 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java @@ -1515,6 +1515,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider private final int range; SectionPos lastSectionPos; public final Set seenBy = new ReferenceOpenHashSet<>(); // Paper - optimise map impl + public final Set showToEvenWhenHiddenByDefault = new ReferenceOpenHashSet<>(); // Paper - Entity hiddenByDefault public TrackedEntity(Entity entity, int i, int j, boolean flag) { this.serverEntity = new ServerEntity(ChunkMap.this.level, entity, j, flag, this::broadcast, this.seenBy); // CraftBukkit diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java index e273fed8e97c98bf5735d3a8c301968990d4cf32..21d980628bcaf80ed8809122766b626713974ed4 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java @@ -400,6 +400,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { private UUID originWorld; public boolean freezeLocked = false; // Paper - Freeze Tick Lock API public boolean collidingWithWorldBorder; // Paper + public boolean hiddenByDefault; // Paper public void setOrigin(@javax.annotation.Nonnull Location location) { this.origin = location.toVector(); diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java index 57a0dbb23a32123d30c3b3572f4d129be9d97847..6286431870438767e779f37aef1e6d9dd9fbec65 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java @@ -1442,4 +1442,30 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { return !this.getHandle().level.noCollision(this.getHandle(), aabb); } // Paper End - Collision API + + // Paper start - Entity hiddenByDefault + @Override + public void setHiddenByDefault(boolean hiddenByDefault) { + this.getHandle().hiddenByDefault = hiddenByDefault; + + // We need to update the players again + ChunkMap.TrackedEntity entityTracker = this.getHandle().tracker; + + // Tracker was not initialized yet + if (entityTracker == null) { + return; + } + + // If the entity is already hiddenByDefault, we reset our "show to" set to again hide the entity from all players that may be seeing the entity + // We also clear the "show to" set when we stop hiding by default + entityTracker.showToEvenWhenHiddenByDefault.clear(); + + entityTracker.updatePlayers(this.server.getHandle().getPlayers()); + } + + @Override + public boolean isHiddenByDefault() { + return this.getHandle().hiddenByDefault; + } + // Paper end } diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java index 1bada55af5d16437da4d16f9ded55f88a6121eb4..0fbc2971cdf15799c1f0beb1b3670894c41db9c8 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java @@ -1814,6 +1814,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player { ChunkMap tracker = ((ServerLevel) this.getHandle().level).getChunkSource().chunkMap; ChunkMap.TrackedEntity entry = tracker.entityMap.get(other.getId()); if (entry != null) { + entry.showToEvenWhenHiddenByDefault.remove(this.getHandle().connection); // Paper - Entity hiddenByDefault entry.removePlayer(this.getHandle()); } @@ -1892,9 +1893,15 @@ public class CraftPlayer extends CraftHumanEntity implements Player { } ChunkMap.TrackedEntity entry = tracker.entityMap.get(other.getId()); - if (entry != null && !entry.seenBy.contains(this.getHandle().connection)) { - entry.updatePlayer(this.getHandle()); + // Paper start - Entity hiddenByDefault + if (entry != null) { + entry.showToEvenWhenHiddenByDefault.add(this.getHandle().connection); + + if (!entry.seenBy.contains(this.getHandle().connection)) { + entry.updatePlayer(this.getHandle()); + } } + // Paper end server.getPluginManager().callEvent(new PlayerShowEntityEvent(this, entity)); // Paper } // Paper start @@ -1970,7 +1977,16 @@ public class CraftPlayer extends CraftHumanEntity implements Player { @Override public boolean canSee(org.bukkit.entity.Entity entity) { - return entity.isVisibleByDefault() ^ this.invertedVisibilityEntities.containsKey(entity.getUniqueId()); + // Paper start - Entity hiddenByDefault + boolean shownWhenHiddenByDefault = true; + + if (entity.isHiddenByDefault()) { + ChunkMap.TrackedEntity tracker = ((CraftEntity) entity).getHandle().tracker; + shownWhenHiddenByDefault = tracker == null || tracker.showToEvenWhenHiddenByDefault.contains(this.getHandle().connection); + } + + return shownWhenHiddenByDefault && (entity.isVisibleByDefault() ^ this.invertedVisibilityEntities.containsKey(entity.getUniqueId())); + // Paper end } public boolean canSee(UUID uuid) { @@ -3143,6 +3159,30 @@ public class CraftPlayer extends CraftHumanEntity implements Player { } // Paper end + // Paper start - Entity hiddenByDefault + @Override + public void setHiddenByDefault(boolean hiddenByDefault) { + // Update server lists + if (hiddenByDefault) { + for (CraftPlayer player : this.server.getOnlinePlayers()) { + if (player == this || !player.canSee(this)) continue; + + player.getHandle().connection.send(new ClientboundPlayerInfoRemovePacket(List.of(this.getHandle().getUUID()))); + } + } else { + for (CraftPlayer player : this.server.getOnlinePlayers()) { + if (player == this || player.canSee(this)) continue; + + player.getHandle().connection.send(new ClientboundPlayerInfoUpdatePacket(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER, this.getHandle())); + } + } + + // Now, process trackers + super.setHiddenByDefault(hiddenByDefault); + } + // Paper end + + public Player.Spigot spigot() { return this.spigot;