Fix entity tracker methods for 1.17 (#1354)

- In 1.17, EntityTrackerEntries use ServerPlayerConnections instead of EntityPlayers as they did before. This caused the updateEntity to silently fail when removing the players from the trackedPlayers collection (of connections). This was resolved by retrieving the connections of the players before removing them from the list on 1.17+.
The getEntityTrackers method failed because it could not find any players for the same reason. This was resolved by retrieving the player from the connection before retrieving the Bukkit player from the EntityPlayer object when running on 1.17+.
- This fixes dmulloy2/ProtocolLib#1340
This commit is contained in:
Pim van der Loos 2021-07-27 08:51:24 +02:00 committed by GitHub
parent 4c0c18d7c6
commit 5dda8c8ab1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 58 additions and 8 deletions

View File

@ -28,6 +28,7 @@ import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.MinecraftFields;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.wrappers.WrappedIntHashMap;
@ -67,7 +68,10 @@ class EntityUtilities {
Collection<?> trackedPlayers = getTrackedPlayers(entity);
List<Object> nmsPlayers = unwrapBukkit(observers);
trackedPlayers.removeAll(nmsPlayers);
List<Object> removingEntries = MinecraftVersion.CAVES_CLIFFS_1.atOrAbove() ?
getPlayerConnections(nmsPlayers) : nmsPlayers;
trackedPlayers.removeAll(removingEntries);
Object trackerEntry = getEntityTrackerEntry(entity.getWorld(), entity.getEntityId());
@ -103,7 +107,9 @@ class EntityUtilities {
// Wrap every player - we also ensure that the underlying tracker list is immutable
for (Object tracker : trackedPlayers) {
if (MinecraftReflection.isMinecraftPlayer(tracker)) {
if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove() && MinecraftReflection.isServerHandler(tracker)) {
result.add(MinecraftReflection.getBukkitPlayerFromConnection(tracker));
} else if (MinecraftReflection.isMinecraftPlayer(tracker)) {
result.add((Player) MinecraftReflection.getBukkitEntity(tracker));
}
}
@ -266,6 +272,12 @@ class EntityUtilities {
}
}
private List<Object> getPlayerConnections(List<Object> nmsPlayers) {
List<Object> connections = new ArrayList<>(nmsPlayers.size());
nmsPlayers.forEach(nmsPlayer -> connections.add(MinecraftFields.getPlayerConnection(nmsPlayer)));
return connections;
}
private List<Object> unwrapBukkit(List<Player> players) {
List<Object> output = Lists.newArrayList();
BukkitUnwrapper unwrapper = new BukkitUnwrapper();

View File

@ -15,15 +15,16 @@ public class MinecraftFields {
// Cached accessors
private static volatile FieldAccessor CONNECTION_ACCESSOR;
private static volatile FieldAccessor NETWORK_ACCESSOR;
private static volatile FieldAccessor CONNECTION_ENTITY_ACCESSOR;
private MinecraftFields() {
// Not constructable
}
/**
* Retrieve the network mananger associated with a particular player.
* Retrieve the network manager associated with a particular player.
* @param player - the player.
* @return The network manager, or NULL if no network manager has been asssociated yet.
* @return The network manager, or NULL if no network manager has been associated yet.
*/
public static Object getNetworkManager(Player player) {
Object nmsPlayer = BukkitUnwrapper.getInstance().unwrapItem(player);
@ -50,9 +51,13 @@ public class MinecraftFields {
Preconditions.checkNotNull(player, "player cannot be null!");
return getPlayerConnection(BukkitUnwrapper.getInstance().unwrapItem(player));
}
// Retrieve player connection from a native instance
private static Object getPlayerConnection(Object nmsPlayer) {
/**
* Retrieve the PlayerConnection (or NetServerHandler) associated with a player.
* @param nmsPlayer - the NMS player.
* @return The player connection.
*/
public static Object getPlayerConnection(Object nmsPlayer) {
Preconditions.checkNotNull(nmsPlayer, "nmsPlayer cannot be null!");
if (CONNECTION_ACCESSOR == null) {
@ -61,4 +66,21 @@ public class MinecraftFields {
}
return CONNECTION_ACCESSOR.get(nmsPlayer);
}
/**
* Retrieves the EntityPlayer player field from a PlayerConnection.
*
* @param playerConnection The PlayerConnection object from which to retrieve the EntityPlayer field.
* @return The value of the EntityPlayer field in the PlayerConnection.
*/
public static Object getPlayerFromConnection(Object playerConnection) {
Preconditions.checkNotNull(playerConnection, "playerConnection cannot be null!");
if (CONNECTION_ENTITY_ACCESSOR == null) {
Class<?> connectionClass = MinecraftReflection.getPlayerConnectionClass();
Class<?> entityPlayerClass = MinecraftReflection.getEntityPlayerClass();
CONNECTION_ENTITY_ACCESSOR = Accessors.getFieldAccessor(connectionClass, entityPlayerClass, true);
}
return CONNECTION_ENTITY_ACCESSOR.get(playerConnection);
}
}

View File

@ -41,6 +41,7 @@ import javax.annotation.Nonnull;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import com.comphenix.protocol.PacketType;
@ -376,6 +377,21 @@ public class MinecraftReflection {
}
}
/**
* Retrieve the Bukkit player from a given PlayerConnection.
* @param playerConnection The PlayerConnection.
* @return A bukkit player.
* @throws RuntimeException If we were unable to retrieve the Bukkit player.
*/
public static Player getBukkitPlayerFromConnection(Object playerConnection)
{
try {
return (Player) getBukkitEntity(MinecraftFields.getPlayerFromConnection(playerConnection));
} catch (Exception e) {
throw new IllegalArgumentException("Cannot get Bukkit entity from connection " + playerConnection, e);
}
}
/**
* Determine if a given object can be found within the package net.minecraft.server.
* @param obj - the object to test.