Last active
March 10, 2025 15:52
-
-
Save aadnk/5871793 to your computer and use it in GitHub Desktop.
Hide or show entities
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.comphenix.example; | |
import static com.comphenix.protocol.PacketType.Play.Server.*; | |
import java.lang.reflect.InvocationTargetException; | |
import java.util.Arrays; | |
import java.util.Map; | |
import org.bukkit.entity.Entity; | |
import org.bukkit.entity.Player; | |
import org.bukkit.event.EventHandler; | |
import org.bukkit.event.HandlerList; | |
import org.bukkit.event.Listener; | |
import org.bukkit.event.entity.EntityDeathEvent; | |
import org.bukkit.event.player.PlayerQuitEvent; | |
import org.bukkit.event.world.ChunkUnloadEvent; | |
import org.bukkit.plugin.Plugin; | |
import com.comphenix.protocol.PacketType; | |
import com.comphenix.protocol.ProtocolLibrary; | |
import com.comphenix.protocol.ProtocolManager; | |
import com.comphenix.protocol.events.PacketAdapter; | |
import com.comphenix.protocol.events.PacketContainer; | |
import com.comphenix.protocol.events.PacketEvent; | |
import com.google.common.base.Preconditions; | |
import com.google.common.collect.HashBasedTable; | |
import com.google.common.collect.Table; | |
public class EntityHider implements Listener { | |
protected Table<Integer, Integer, Boolean> observerEntityMap = HashBasedTable.create(); | |
// Packets that update remote player entities | |
private static final PacketType[] ENTITY_PACKETS = { | |
ENTITY_EQUIPMENT, BED, ANIMATION, NAMED_ENTITY_SPAWN, | |
COLLECT, SPAWN_ENTITY, SPAWN_ENTITY_LIVING, SPAWN_ENTITY_PAINTING, SPAWN_ENTITY_EXPERIENCE_ORB, | |
ENTITY_VELOCITY, REL_ENTITY_MOVE, ENTITY_LOOK, ENTITY_MOVE_LOOK, ENTITY_MOVE_LOOK, | |
ENTITY_TELEPORT, ENTITY_HEAD_ROTATION, ENTITY_STATUS, ATTACH_ENTITY, ENTITY_METADATA, | |
ENTITY_EFFECT, REMOVE_ENTITY_EFFECT, BLOCK_BREAK_ANIMATION | |
// We don't handle DESTROY_ENTITY though | |
}; | |
/** | |
* The current entity visibility policy. | |
* @author Kristian | |
*/ | |
public enum Policy { | |
/** | |
* All entities are invisible by default. Only entities specifically made visible may be seen. | |
*/ | |
WHITELIST, | |
/** | |
* All entities are visible by default. An entity can only be hidden explicitly. | |
*/ | |
BLACKLIST, | |
} | |
private ProtocolManager manager; | |
// Listeners | |
private Listener bukkitListener; | |
private PacketAdapter protocolListener; | |
// Current policy | |
protected final Policy policy; | |
/** | |
* Construct a new entity hider. | |
* @param plugin - the plugin that controls this entity hider. | |
* @param policy - the default visibility policy. | |
*/ | |
public EntityHider(Plugin plugin, Policy policy) { | |
Preconditions.checkNotNull(plugin, "plugin cannot be NULL."); | |
// Save policy | |
this.policy = policy; | |
this.manager = ProtocolLibrary.getProtocolManager(); | |
// Register events and packet listener | |
plugin.getServer().getPluginManager().registerEvents( | |
bukkitListener = constructBukkit(), plugin); | |
manager.addPacketListener( | |
protocolListener = constructProtocol(plugin)); | |
} | |
/** | |
* Set the visibility status of a given entity for a particular observer. | |
* @param observer - the observer player. | |
* @param entity - ID of the entity that will be hidden or made visible. | |
* @param visible - TRUE if the entity should be made visible, FALSE if not. | |
* @return TRUE if the entity was visible before this method call, FALSE otherwise. | |
*/ | |
protected boolean setVisibility(Player observer, int entityID, boolean visible) { | |
switch (policy) { | |
case BLACKLIST: | |
// Non-membership means they are visible | |
return !setMembership(observer, entityID, !visible); | |
case WHITELIST: | |
return setMembership(observer, entityID, visible); | |
default : | |
throw new IllegalArgumentException("Unknown policy: " + policy); | |
} | |
} | |
/** | |
* Add or remove the given entity and observer entry from the table. | |
* @param observer - the player observer. | |
* @param entityID - ID of the entity. | |
* @param member - TRUE if they should be present in the table, FALSE otherwise. | |
* @return TRUE if they already were present, FALSE otherwise. | |
*/ | |
// Helper method | |
protected boolean setMembership(Player observer, int entityID, boolean member) { | |
if (member) { | |
return observerEntityMap.put(observer.getEntityId(), entityID, true) != null; | |
} else { | |
return observerEntityMap.remove(observer.getEntityId(), entityID) != null; | |
} | |
} | |
/** | |
* Determine if the given entity and observer is present in the table. | |
* @param observer - the player observer. | |
* @param entityID - ID of the entity. | |
* @return TRUE if they are present, FALSE otherwise. | |
*/ | |
protected boolean getMembership(Player observer, int entityID) { | |
return observerEntityMap.contains(observer.getEntityId(), entityID); | |
} | |
/** | |
* Determine if a given entity is visible for a particular observer. | |
* @param observer - the observer player. | |
* @param entityID - ID of the entity that we are testing for visibility. | |
* @return TRUE if the entity is visible, FALSE otherwise. | |
*/ | |
protected boolean isVisible(Player observer, int entityID) { | |
// If we are using a whitelist, presence means visibility - if not, the opposite is the case | |
boolean presence = getMembership(observer, entityID); | |
return policy == Policy.WHITELIST ? presence : !presence; | |
} | |
/** | |
* Remove the given entity from the underlying map. | |
* @param entity - the entity to remove. | |
* @param destroyed - TRUE if the entity was killed, FALSE if it is merely unloading. | |
*/ | |
protected void removeEntity(Entity entity, boolean destroyed) { | |
int entityID = entity.getEntityId(); | |
for (Map<Integer, Boolean> maps : observerEntityMap.rowMap().values()) { | |
maps.remove(entityID); | |
} | |
} | |
/** | |
* Invoked when a player logs out. | |
* @param player - the player that jused logged out. | |
*/ | |
protected void removePlayer(Player player) { | |
// Cleanup | |
observerEntityMap.rowMap().remove(player.getEntityId()); | |
} | |
/** | |
* Construct the Bukkit event listener. | |
* @return Our listener. | |
*/ | |
private Listener constructBukkit() { | |
return new Listener() { | |
@EventHandler | |
public void onEntityDeath(EntityDeathEvent e) { | |
removeEntity(e.getEntity(), true); | |
} | |
@EventHandler | |
public void onChunkUnload(ChunkUnloadEvent e) { | |
for (Entity entity : e.getChunk().getEntities()) { | |
removeEntity(entity, false); | |
} | |
} | |
@EventHandler | |
public void onPlayerQuit(PlayerQuitEvent e) { | |
removePlayer(e.getPlayer()); | |
} | |
}; | |
} | |
/** | |
* Construct the packet listener that will be used to intercept every entity-related packet. | |
* @param plugin - the parent plugin. | |
* @return The packet listener. | |
*/ | |
private PacketAdapter constructProtocol(Plugin plugin) { | |
return new PacketAdapter(plugin, ENTITY_PACKETS) { | |
@Override | |
public void onPacketSending(PacketEvent event) { | |
int entityID = event.getPacket().getIntegers().read(0); | |
// See if this packet should be cancelled | |
if (!isVisible(event.getPlayer(), entityID)) { | |
event.setCancelled(true); | |
} | |
} | |
}; | |
} | |
/** | |
* Toggle the visibility status of an entity for a player. | |
* <p> | |
* If the entity is visible, it will be hidden. If it is hidden, it will become visible. | |
* @param observer - the player observer. | |
* @param entity - the entity to toggle. | |
* @return TRUE if the entity was visible before, FALSE otherwise. | |
*/ | |
public final boolean toggleEntity(Player observer, Entity entity) { | |
if (isVisible(observer, entity.getEntityId())) { | |
return hideEntity(observer, entity); | |
} else { | |
return !showEntity(observer, entity); | |
} | |
} | |
/** | |
* Allow the observer to see an entity that was previously hidden. | |
* @param observer - the observer. | |
* @param entity - the entity to show. | |
* @return TRUE if the entity was hidden before, FALSE otherwise. | |
*/ | |
public final boolean showEntity(Player observer, Entity entity) { | |
validate(observer, entity); | |
boolean hiddenBefore = !setVisibility(observer, entity.getEntityId(), true); | |
// Resend packets | |
if (manager != null && hiddenBefore) { | |
manager.updateEntity(entity, Arrays.asList(observer)); | |
} | |
return hiddenBefore; | |
} | |
/** | |
* Prevent the observer from seeing a given entity. | |
* @param observer - the player observer. | |
* @param entity - the entity to hide. | |
* @return TRUE if the entity was previously visible, FALSE otherwise. | |
*/ | |
public final boolean hideEntity(Player observer, Entity entity) { | |
validate(observer, entity); | |
boolean visibleBefore = setVisibility(observer, entity.getEntityId(), false); | |
if (visibleBefore) { | |
PacketContainer destroyEntity = new PacketContainer(ENTITY_DESTROY); | |
destroyEntity.getIntegerArrays().write(0, new int[] { entity.getEntityId() }); | |
// Make the entity disappear | |
try { | |
manager.sendServerPacket(observer, destroyEntity); | |
} catch (InvocationTargetException e) { | |
throw new RuntimeException("Cannot send server packet.", e); | |
} | |
} | |
return visibleBefore; | |
} | |
/** | |
* Determine if the given entity has been hidden from an observer. | |
* <p> | |
* Note that the entity may very well be occluded or out of range from the perspective | |
* of the observer. This method simply checks if an entity has been completely hidden | |
* for that observer. | |
* @param observer - the observer. | |
* @param entity - the entity that may be hidden. | |
* @return TRUE if the player may see the entity, FALSE if the entity has been hidden. | |
*/ | |
public final boolean canSee(Player observer, Entity entity) { | |
validate(observer, entity); | |
return isVisible(observer, entity.getEntityId()); | |
} | |
// For valdiating the input parameters | |
private void validate(Player observer, Entity entity) { | |
Preconditions.checkNotNull(observer, "observer cannot be NULL."); | |
Preconditions.checkNotNull(entity, "entity cannot be NULL."); | |
} | |
/** | |
* Retrieve the current visibility policy. | |
* @return The current visibility policy. | |
*/ | |
public Policy getPolicy() { | |
return policy; | |
} | |
public void close() { | |
if (manager != null) { | |
HandlerList.unregisterAll(bukkitListener); | |
manager.removePacketListener(protocolListener); | |
manager = null; | |
} | |
} | |
} |
❤️
Hello, i don't know if u awnser just I can't seem to hide a potion from a player when another player throws it.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
thank you <3