2022-12-07 19:52:09 +01:00
|
|
|
/**
|
2022-12-14 02:38:37 +01:00
|
|
|
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. Copyright (C) 2017 Dan Mulloy
|
|
|
|
* <p>
|
|
|
|
* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public
|
|
|
|
* License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later
|
|
|
|
* version.
|
|
|
|
* <p>
|
|
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
|
|
|
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
|
|
* details.
|
|
|
|
* <p>
|
|
|
|
* You should have received a copy of the GNU General Public License along with this program; if not, write to the Free
|
|
|
|
* Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
2022-12-07 19:52:09 +01:00
|
|
|
*/
|
|
|
|
package com.comphenix.protocol.wrappers;
|
|
|
|
|
2023-04-11 15:13:03 +02:00
|
|
|
import com.comphenix.protocol.reflect.EquivalentConverter;
|
|
|
|
import com.comphenix.protocol.reflect.FuzzyReflection;
|
|
|
|
import com.comphenix.protocol.reflect.accessors.Accessors;
|
|
|
|
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
|
|
|
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
|
|
|
import com.comphenix.protocol.utility.MinecraftReflection;
|
|
|
|
|
2022-12-07 19:52:09 +01:00
|
|
|
import java.lang.reflect.Array;
|
2023-01-10 22:36:01 +01:00
|
|
|
import java.lang.reflect.Modifier;
|
2023-03-26 20:31:39 +02:00
|
|
|
import java.util.ArrayList;
|
2022-12-14 02:38:37 +01:00
|
|
|
import java.util.Collection;
|
Packet filtering for bundled packets in 1.19.4 (#2258)
Since Minecraft 1.19.4, the protocol supports bundling consecutive packets to ensure the client processes them in one tick. However, Packet Events are not called for the individual packets in such a bundle in the current dev build of ProtocolLib. For example, no packet events are currently sent for the ENTITY_METADATA packet when an entity is first spawned as the packet is bundled with the ENTITY_SPAWN packet. However, if the entity metadata is changed later on, the event will be called.
This PR proposes to fix this by unpacking the bundled packets and invoking the packet filtering for each packet.
I also want to briefly explain how the bundling works. A bundle starts with a PACKET_DELIMITER (0x00, net.minecraft.network.protocol.BundleDelimiterPacket) packet followed by all packets that should be bundled and finished with another PACKET_DELIMITER (0x00). Within the Netty pipeline, this sequence is transformed into one synthesized packet found in net.minecraft.network.protocol.game.ClientboundBundlePacket, which is essentially just a list of packets. At the stage at which ProtocolLib injects into the clientbound netty pipeline, this packet has not been unpacked yet. Thus, we need to handle the ClientboundBundlePacket, which unfortunately is not registered in ProtocolLib. The fact that two different classes map to the same packet currently requires a dirty remapping in the packet structure modifier.
2023-03-26 04:08:31 +02:00
|
|
|
import java.util.List;
|
2022-12-07 19:52:09 +01:00
|
|
|
import java.util.Optional;
|
|
|
|
import java.util.function.Function;
|
Packet filtering for bundled packets in 1.19.4 (#2258)
Since Minecraft 1.19.4, the protocol supports bundling consecutive packets to ensure the client processes them in one tick. However, Packet Events are not called for the individual packets in such a bundle in the current dev build of ProtocolLib. For example, no packet events are currently sent for the ENTITY_METADATA packet when an entity is first spawned as the packet is bundled with the ENTITY_SPAWN packet. However, if the entity metadata is changed later on, the event will be called.
This PR proposes to fix this by unpacking the bundled packets and invoking the packet filtering for each packet.
I also want to briefly explain how the bundling works. A bundle starts with a PACKET_DELIMITER (0x00, net.minecraft.network.protocol.BundleDelimiterPacket) packet followed by all packets that should be bundled and finished with another PACKET_DELIMITER (0x00). Within the Netty pipeline, this sequence is transformed into one synthesized packet found in net.minecraft.network.protocol.game.ClientboundBundlePacket, which is essentially just a list of packets. At the stage at which ProtocolLib injects into the clientbound netty pipeline, this packet has not been unpacked yet. Thus, we need to handle the ClientboundBundlePacket, which unfortunately is not registered in ProtocolLib. The fact that two different classes map to the same packet currently requires a dirty remapping in the packet structure modifier.
2023-03-26 04:08:31 +02:00
|
|
|
import java.util.function.Supplier;
|
2022-12-07 19:52:09 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Utility class for converters
|
|
|
|
* @author dmulloy2
|
|
|
|
*/
|
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
public class Converters {
|
|
|
|
|
2023-05-12 16:35:34 +02:00
|
|
|
/**
|
|
|
|
* Returns a converter that ignores null elements, so that the underlying converter doesn't have to worry about them.
|
|
|
|
* @param converter Underlying converter
|
|
|
|
* @param <T> Element type
|
|
|
|
* @return An ignore null converter
|
|
|
|
*/
|
|
|
|
public static <T> EquivalentConverter<T> ignoreNull(final EquivalentConverter<T> converter) {
|
|
|
|
return new EquivalentConverter<T>() {
|
|
|
|
@Override
|
|
|
|
public T getSpecific(Object generic) {
|
|
|
|
return generic != null ? converter.getSpecific(generic) : null;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Object getGeneric(T specific) {
|
|
|
|
return specific != null ? converter.getGeneric(specific) : null;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Class<T> getSpecificType() {
|
|
|
|
return converter.getSpecificType();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a converter that passes generic and specific values through without converting.
|
|
|
|
* @param clazz Element class
|
|
|
|
* @param <T> Element type
|
|
|
|
* @return A passthrough converter
|
|
|
|
*/
|
|
|
|
public static <T> EquivalentConverter<T> passthrough(final Class<T> clazz) {
|
|
|
|
return ignoreNull(new EquivalentConverter<T>() {
|
|
|
|
@Override
|
|
|
|
public T getSpecific(Object generic) {
|
|
|
|
return (T) generic;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Object getGeneric(T specific) {
|
|
|
|
return specific;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Class<T> getSpecificType() {
|
|
|
|
return clazz;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a simple converter for wrappers with {@code getHandle()} and {@code fromHandle(...)} methods. With Java 8,
|
|
|
|
* converters can be reduced to a single line (see {@link BukkitConverters#getWrappedGameProfileConverter()}).
|
|
|
|
* @param toHandle Function from wrapper to handle (i.e. {@code getHandle()})
|
|
|
|
* @param fromHandle Function from handle to wrapper (i.e. {@code fromHandle(Object)})
|
|
|
|
* @param <T> Wrapper type
|
|
|
|
* @return A handle converter
|
|
|
|
*/
|
|
|
|
public static <T> EquivalentConverter<T> handle(final Function<T, Object> toHandle,
|
|
|
|
final Function<Object, T> fromHandle, final Class<T> specificType) {
|
|
|
|
return new EquivalentConverter<T>() {
|
|
|
|
@Override
|
|
|
|
public T getSpecific(Object generic) {
|
|
|
|
return fromHandle.apply(generic);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Object getGeneric(T specific) {
|
|
|
|
return toHandle.apply(specific);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Class<T> getSpecificType() {
|
|
|
|
return specificType;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a generic array converter. Converts a NMS object array to and from a wrapper array by converting
|
|
|
|
* each element individually.
|
|
|
|
*
|
|
|
|
* @param nmsClass NMS class
|
|
|
|
* @param converter Underlying converter
|
|
|
|
* @param <T> Generic type
|
|
|
|
* @return An array converter
|
|
|
|
*/
|
|
|
|
public static <T> EquivalentConverter<T[]> array(final Class<?> nmsClass, final EquivalentConverter<T> converter) {
|
|
|
|
return new EquivalentConverter<T[]>() {
|
|
|
|
@Override
|
|
|
|
public T[] getSpecific(Object generic) {
|
|
|
|
Object[] array = (Object[]) generic;
|
|
|
|
Class<T[]> clazz = getSpecificType();
|
|
|
|
T[] result = clazz.cast(Array.newInstance(clazz.getComponentType(), array.length));
|
|
|
|
|
|
|
|
// Unwrap every item
|
|
|
|
for (int i = 0; i < result.length; i++) {
|
|
|
|
result[i] = converter.getSpecific(array[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Object getGeneric(T[] specific) {
|
|
|
|
Object[] result = (Object[]) Array.newInstance(nmsClass, specific.length);
|
|
|
|
|
|
|
|
// Wrap every item
|
|
|
|
for (int i = 0; i < result.length; i++) {
|
|
|
|
result[i] = converter.getGeneric(specific[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Class<T[]> getSpecificType() {
|
|
|
|
return (Class<T[]>) MinecraftReflection.getArrayClass(converter.getSpecificType());
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
public static <T> EquivalentConverter<Optional<T>> optional(final EquivalentConverter<T> converter) {
|
|
|
|
return new EquivalentConverter<Optional<T>>() {
|
|
|
|
@Override
|
|
|
|
public Object getGeneric(Optional<T> specific) {
|
|
|
|
return specific.map(converter::getGeneric);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Optional<T> getSpecific(Object generic) {
|
|
|
|
Optional<Object> optional = (Optional<Object>) generic;
|
|
|
|
return optional.map(converter::getSpecific);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Class<Optional<T>> getSpecificType() {
|
|
|
|
return (Class<Optional<T>>) Optional.empty().getClass();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
public static <T, C extends Collection<T>> EquivalentConverter<C> collection(
|
|
|
|
final EquivalentConverter<T> elementConverter,
|
|
|
|
final Function<Collection<Object>, C> genericToSpecificCollectionFactory,
|
|
|
|
final Function<C, Collection<?>> specificToGenericCollectionFactory
|
|
|
|
) {
|
|
|
|
return ignoreNull(new EquivalentConverter<C>() {
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Object getGeneric(C specific) {
|
|
|
|
// generics are very cool, thank you java
|
|
|
|
Collection<Object> targetCollection = (Collection<Object>) specificToGenericCollectionFactory.apply(specific);
|
|
|
|
for (T element : specific) {
|
|
|
|
Object generic = elementConverter.getGeneric(element);
|
|
|
|
if (generic != null) {
|
|
|
|
targetCollection.add(generic);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return targetCollection;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public C getSpecific(Object generic) {
|
|
|
|
if (generic instanceof Collection<?>) {
|
|
|
|
Collection<Object> sourceCollection = (Collection<Object>) generic;
|
|
|
|
C targetCollection = genericToSpecificCollectionFactory.apply(sourceCollection);
|
|
|
|
// copy over all elements into a new collection
|
|
|
|
for (Object element : sourceCollection) {
|
|
|
|
T specific = elementConverter.getSpecific(element);
|
|
|
|
if (specific != null) {
|
|
|
|
targetCollection.add(specific);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return targetCollection;
|
|
|
|
}
|
|
|
|
// not valid
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Class<C> getSpecificType() {
|
|
|
|
return (Class) Collection.class;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public static <T> EquivalentConverter<Iterable<T>> iterable(
|
|
|
|
final EquivalentConverter<T> elementConverter,
|
|
|
|
final Supplier<List<T>> specificCollectionFactory,
|
|
|
|
final Supplier<List<?>> genericCollectionFactory
|
|
|
|
) {
|
|
|
|
return ignoreNull(new EquivalentConverter<Iterable<T>>() {
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Object getGeneric(Iterable<T> specific) {
|
|
|
|
// generics are very cool, thank you java
|
|
|
|
List<Object> targetCollection = (List<Object>) genericCollectionFactory.get();
|
|
|
|
for (T element : specific) {
|
|
|
|
Object generic = elementConverter.getGeneric(element);
|
|
|
|
if (generic != null) {
|
|
|
|
targetCollection.add(generic);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return targetCollection;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Iterable<T> getSpecific(Object generic) {
|
|
|
|
if (generic instanceof Iterable<?>) {
|
|
|
|
Iterable<Object> sourceCollection = (Iterable<Object>) generic;
|
|
|
|
List<T> targetCollection = specificCollectionFactory.get();
|
|
|
|
// copy over all elements into a new collection
|
|
|
|
for (Object element : sourceCollection) {
|
|
|
|
T specific = elementConverter.getSpecific(element);
|
|
|
|
if (specific != null) {
|
|
|
|
targetCollection.add(specific);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return targetCollection;
|
|
|
|
}
|
|
|
|
// not valid
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Class<Iterable<T>> getSpecificType() {
|
|
|
|
return (Class) Iterable.class;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
private static MethodAccessor holderGetValue;
|
|
|
|
|
|
|
|
public static <T> EquivalentConverter<T> holder(final EquivalentConverter<T> converter,
|
|
|
|
final WrappedRegistry registry) {
|
|
|
|
return new EquivalentConverter<T>() {
|
|
|
|
@Override
|
|
|
|
public Object getGeneric(T specific) {
|
|
|
|
Object generic = converter.getGeneric(specific);
|
|
|
|
return registry.getHolder(generic);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public T getSpecific(Object generic) {
|
|
|
|
if (holderGetValue == null) {
|
|
|
|
Class<?> holderClass = MinecraftReflection.getHolderClass();
|
|
|
|
FuzzyReflection fuzzy = FuzzyReflection.fromClass(holderClass, false);
|
|
|
|
holderGetValue = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract
|
|
|
|
.newBuilder()
|
|
|
|
.parameterCount(0)
|
|
|
|
.banModifier(Modifier.STATIC)
|
|
|
|
.returnTypeExact(Object.class)
|
|
|
|
.build()));
|
|
|
|
}
|
|
|
|
|
|
|
|
Object value = holderGetValue.invoke(generic);
|
|
|
|
return converter.getSpecific(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Class<T> getSpecificType() {
|
|
|
|
return converter.getSpecificType();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
public static <T> List<T> toList(Iterable<? extends T> iterable) {
|
|
|
|
if (iterable instanceof List) {
|
|
|
|
return (List<T>) iterable;
|
|
|
|
}
|
|
|
|
|
|
|
|
List<T> result = new ArrayList<>();
|
|
|
|
if (iterable instanceof Collection) {
|
|
|
|
Collection<T> coll = (Collection<T>) iterable;
|
|
|
|
result.addAll(coll);
|
|
|
|
} else {
|
|
|
|
for (T elem : iterable) {
|
|
|
|
result.add(elem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
2022-12-07 19:52:09 +01:00
|
|
|
}
|