Skip to content

Instantly share code, notes, and snippets.

@JanTuck
Last active December 3, 2023 20:19
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JanTuck/344a1508bb971fd85f8e21780ab07615 to your computer and use it in GitHub Desktop.
Save JanTuck/344a1508bb971fd85f8e21780ab07615 to your computer and use it in GitHub Desktop.
package me.jantuck.testing;
import com.google.gson.internal.Primitives;
import net.minecraft.server.v1_12_R1.*;
import org.bukkit.craftbukkit.v1_12_R1.entity.CraftPlayer;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class ProtocolProvider {
private static ProtocolProvider protocolProvider = new ProtocolProvider();
public static ProtocolProvider getProtocolProvider() {
return protocolProvider;
}
private Field b = getDeclaredField("b");
private Field a = getDeclaredField("a");
private Map<Class<?>, Object> dataWatcherSerializers;
{
dataWatcherSerializers = new HashMap<>();
Field[] fields = DataWatcherRegistry.class.getFields();
for (Field field : fields) {
try {
dataWatcherSerializers.put(getGenericType(field), field.get(null));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
private Field getDeclaredField(String fieldName) {
try {
Field field = PacketPlayOutEntityMetadata.class.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
return null;
}
/**
* Return the generic type of a field, in this case of a {@link DataWatcherSerializer} with the generic of T, T being the value we want to retrieve.
* @param field The actual field we want to retrieve the generic class from.
* @return Class of the generic associated with this field.
*/
private Class<?> getGenericType(Field field){
ParameterizedType parameterizedType = (ParameterizedType) field.getGenericType();
Type type = parameterizedType.getActualTypeArguments()[0];
if (type instanceof Class<?>){
return (Class<?>) type;
}
return null;
}
/**
* Get serializer of supplied type from the {@link this#dataWatcherSerializers} map.
* @param val Value that is in need of a serializer.
* @param <T> The type of the value.
* @return Serializer of value T
*/
private <T> DataWatcherSerializer<T> getSerializer(T val){
Object serializer = dataWatcherSerializers.get(val.getClass());
if (serializer == null)
serializer = dataWatcherSerializers.get(Primitives.wrap(val.getClass()));
return serializer == null ? null : ((DataWatcherSerializer<T>) serializer);
}
/**
* Returns a {@link DataWatcherObject} of type corresponding to val, with the included data of id.
* @param val The value that needs an object for itself.
* @param id The id to be held inside the DataWatcherObject
* @param <T> The Type of val.
* @return
*/
private <T> DataWatcherObject<T> getObject(T val, byte id){
DataWatcherSerializer<T> dataWatcherSerializer = getSerializer(val);
return dataWatcherSerializer == null ? null : dataWatcherSerializer.a(id);
}
/**
* Create an empty packet of {@link PacketPlayOutEntityMetadata} with the ID of entity included.
* @param entity The entity that should be affected by this packet.
* @return An empty PacketPlayOutEntityMetadata that should affect the entity above.
* @throws IllegalAccessException Reflection...
*/
private PacketPlayOutEntityMetadata createEmptyPacket(Entity entity) throws IllegalAccessException {
PacketPlayOutEntityMetadata packetPlayOutEntityMetadata = new PacketPlayOutEntityMetadata();
a.set(packetPlayOutEntityMetadata, entity.getEntityId());
return packetPlayOutEntityMetadata;
}
/**
* Send a metadata packet to player with the supplied arguments, that should affect the LivingEntity. Cheers
* This took way too long to figure out how to do... Seems easy now though.
* @param player The Player that should receive the packet.
* @param entity LivingEntity that should be affected by the Packet.
* @param id The Byte id of the packet.
* @param value The actual value/data of the packet.
* @param <T> The Type of value, used to infer what serializer should be used.
* @throws IllegalAccessException
*/
public <T> void sendMetadataPacket(Player player, Entity entity, byte id, T value) throws IllegalAccessException {
PacketPlayOutEntityMetadata packetPlayOutEntityMetadata = createEmptyPacket(entity);
b.set(packetPlayOutEntityMetadata, Collections.singletonList(new DataWatcher.Item<>(getObject(value, id), value)));
sendPacket(player, packetPlayOutEntityMetadata);
}
/**
* Send a packet to a player.
* @param player The Player who will receive the packet.
* @param packet Packet the player will receive.
*/
public void sendPacket(Player player, Packet packet){
((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment