2012-12-27 14:28:33 +01:00
|
|
|
/*
|
|
|
|
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
|
|
|
* Copyright (C) 2012 Kristian S. Stangeland
|
|
|
|
*
|
2014-12-06 03:41:55 +01:00
|
|
|
* 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
|
2012-12-27 14:28:33 +01:00
|
|
|
* the License, or (at your option) any later version.
|
|
|
|
*
|
2014-12-06 03:41:55 +01:00
|
|
|
* 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.
|
2012-12-27 14:28:33 +01:00
|
|
|
* See the GNU General Public License for more details.
|
|
|
|
*
|
2014-12-06 03:41:55 +01:00
|
|
|
* 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
|
2012-12-27 14:28:33 +01:00
|
|
|
* 02111-1307 USA
|
|
|
|
*/
|
2012-11-13 14:34:07 +01:00
|
|
|
package com.comphenix.protocol.wrappers;
|
|
|
|
|
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 com.comphenix.protocol.events.PacketContainer;
|
2022-07-26 19:29:34 +02:00
|
|
|
import com.comphenix.protocol.wrappers.Either.Left;
|
|
|
|
import com.comphenix.protocol.wrappers.Either.Right;
|
2022-06-08 03:24:31 +02:00
|
|
|
import com.comphenix.protocol.wrappers.WrappedProfilePublicKey.WrappedProfileKeyData;
|
2012-11-13 16:10:56 +01:00
|
|
|
import java.lang.ref.WeakReference;
|
2021-06-24 23:13:40 +02:00
|
|
|
import java.lang.reflect.*;
|
2021-06-20 04:20:54 +02:00
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.Collection;
|
|
|
|
import java.util.HashMap;
|
|
|
|
import java.util.HashSet;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.Map;
|
2013-12-10 12:17:15 +01:00
|
|
|
import java.util.Map.Entry;
|
2021-06-20 04:20:54 +02:00
|
|
|
import java.util.Optional;
|
|
|
|
import java.util.Set;
|
2016-12-22 17:42:52 +01:00
|
|
|
import java.util.concurrent.ConcurrentHashMap;
|
2019-05-08 04:58:59 +02:00
|
|
|
import java.util.function.Supplier;
|
2020-07-27 01:33:05 +02:00
|
|
|
import java.util.stream.Collectors;
|
2012-11-13 14:34:07 +01:00
|
|
|
|
2013-12-09 23:09:08 +01:00
|
|
|
import com.comphenix.protocol.PacketType;
|
2012-11-13 16:10:56 +01:00
|
|
|
import com.comphenix.protocol.ProtocolLibrary;
|
2019-05-08 04:58:59 +02:00
|
|
|
import com.comphenix.protocol.ProtocolLogger;
|
2012-11-13 16:10:56 +01:00
|
|
|
import com.comphenix.protocol.ProtocolManager;
|
2014-05-02 03:49:33 +02:00
|
|
|
import com.comphenix.protocol.injector.BukkitUnwrapper;
|
2013-11-19 00:50:55 +01:00
|
|
|
import com.comphenix.protocol.injector.PacketConstructor;
|
|
|
|
import com.comphenix.protocol.injector.PacketConstructor.Unwrapper;
|
2012-11-13 14:34:07 +01:00
|
|
|
import com.comphenix.protocol.reflect.EquivalentConverter;
|
2012-11-13 16:10:56 +01:00
|
|
|
import com.comphenix.protocol.reflect.FieldAccessException;
|
2013-11-01 02:09:59 +01:00
|
|
|
import com.comphenix.protocol.reflect.FuzzyReflection;
|
2013-11-19 00:50:55 +01:00
|
|
|
import com.comphenix.protocol.reflect.StructureModifier;
|
2013-12-10 13:21:31 +01:00
|
|
|
import com.comphenix.protocol.reflect.accessors.Accessors;
|
2020-07-27 01:33:05 +02:00
|
|
|
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
|
2014-05-02 03:49:33 +02:00
|
|
|
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
2013-12-10 13:21:31 +01:00
|
|
|
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
2021-02-27 21:38:05 +01:00
|
|
|
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
|
2013-12-10 13:21:31 +01:00
|
|
|
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
2012-12-06 03:37:20 +01:00
|
|
|
import com.comphenix.protocol.utility.MinecraftReflection;
|
2020-07-06 05:47:06 +02:00
|
|
|
import com.comphenix.protocol.utility.MinecraftVersion;
|
|
|
|
import com.comphenix.protocol.wrappers.EnumWrappers.Dimension;
|
|
|
|
import com.comphenix.protocol.wrappers.EnumWrappers.FauxEnumConverter;
|
2013-01-08 22:24:07 +01:00
|
|
|
import com.comphenix.protocol.wrappers.nbt.NbtBase;
|
2013-01-07 10:14:25 +01:00
|
|
|
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
|
2021-06-20 04:20:54 +02:00
|
|
|
|
2013-01-27 15:35:39 +01:00
|
|
|
import com.google.common.base.Objects;
|
2013-11-19 00:50:55 +01:00
|
|
|
import com.google.common.collect.ImmutableList;
|
2013-07-26 17:30:47 +02:00
|
|
|
import com.google.common.collect.ImmutableMap;
|
2013-12-09 23:09:08 +01:00
|
|
|
import com.google.common.collect.Lists;
|
2012-11-13 14:34:07 +01:00
|
|
|
|
2021-06-20 04:20:54 +02:00
|
|
|
import org.bukkit.Bukkit;
|
|
|
|
import org.bukkit.Material;
|
|
|
|
import org.bukkit.Sound;
|
|
|
|
import org.bukkit.World;
|
|
|
|
import org.bukkit.WorldType;
|
2017-07-24 20:15:56 +02:00
|
|
|
import org.bukkit.advancement.Advancement;
|
2017-05-24 20:35:22 +02:00
|
|
|
import org.bukkit.entity.Entity;
|
2019-10-29 21:52:30 +01:00
|
|
|
import org.bukkit.entity.EntityType;
|
2017-05-24 20:35:22 +02:00
|
|
|
import org.bukkit.inventory.ItemStack;
|
2020-07-27 01:33:05 +02:00
|
|
|
import org.bukkit.inventory.MerchantRecipe;
|
2017-05-24 20:35:22 +02:00
|
|
|
import org.bukkit.potion.PotionEffect;
|
|
|
|
import org.bukkit.potion.PotionEffectType;
|
|
|
|
import org.bukkit.util.Vector;
|
|
|
|
|
2020-06-28 21:59:30 +02:00
|
|
|
import static com.comphenix.protocol.utility.MinecraftReflection.getCraftBukkitClass;
|
|
|
|
import static com.comphenix.protocol.utility.MinecraftReflection.getMinecraftClass;
|
|
|
|
import static com.comphenix.protocol.wrappers.Converters.handle;
|
|
|
|
import static com.comphenix.protocol.wrappers.Converters.ignoreNull;
|
2017-07-24 20:15:56 +02:00
|
|
|
|
2012-11-13 14:34:07 +01:00
|
|
|
/**
|
|
|
|
* Contains several useful equivalent converters for normal Bukkit types.
|
|
|
|
*
|
|
|
|
* @author Kristian
|
|
|
|
*/
|
2017-07-24 20:15:56 +02:00
|
|
|
@SuppressWarnings("unchecked")
|
2012-11-13 14:34:07 +01:00
|
|
|
public class BukkitConverters {
|
|
|
|
// Check whether or not certain classes exists
|
|
|
|
private static boolean hasWorldType = false;
|
2013-07-28 02:02:27 +02:00
|
|
|
private static boolean hasAttributeSnapshot = false;
|
2012-11-13 14:34:07 +01:00
|
|
|
|
2013-07-26 17:30:47 +02:00
|
|
|
// The static maps
|
|
|
|
private static Map<Class<?>, EquivalentConverter<Object>> genericConverters;
|
2013-11-19 00:50:55 +01:00
|
|
|
private static List<Unwrapper> unwrappers;
|
2013-07-26 17:30:47 +02:00
|
|
|
|
2012-12-06 03:37:20 +01:00
|
|
|
// Used to access the world type
|
|
|
|
private static Method worldTypeName;
|
|
|
|
private static Method worldTypeGetType;
|
2018-07-19 19:29:20 +02:00
|
|
|
|
2013-11-19 00:50:55 +01:00
|
|
|
// Used for potion effect conversion
|
|
|
|
private static volatile Constructor<?> mobEffectConstructor;
|
|
|
|
private static volatile StructureModifier<Object> mobEffectModifier;
|
|
|
|
|
2014-05-02 03:49:33 +02:00
|
|
|
// Used for fetching the CraftWorld associated with a WorldServer
|
|
|
|
private static FieldAccessor craftWorldField;
|
|
|
|
|
2012-11-13 14:34:07 +01:00
|
|
|
static {
|
|
|
|
try {
|
2013-07-28 02:02:27 +02:00
|
|
|
MinecraftReflection.getWorldTypeClass();
|
2012-11-13 14:34:07 +01:00
|
|
|
hasWorldType = true;
|
2013-07-28 02:02:27 +02:00
|
|
|
} catch (Exception e) {
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
MinecraftReflection.getAttributeSnapshotClass();
|
|
|
|
hasAttributeSnapshot = true;
|
|
|
|
} catch (Exception e) {
|
2012-11-13 14:34:07 +01:00
|
|
|
}
|
2014-05-02 03:49:33 +02:00
|
|
|
|
|
|
|
// Fetch CraftWorld field
|
|
|
|
try {
|
|
|
|
craftWorldField = Accessors.getFieldAccessor(
|
2014-12-06 03:41:55 +01:00
|
|
|
MinecraftReflection.getNmsWorldClass(),
|
2014-05-02 03:49:33 +02:00
|
|
|
MinecraftReflection.getCraftWorldClass(), true);
|
|
|
|
} catch (Exception e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
2012-11-13 14:34:07 +01:00
|
|
|
}
|
|
|
|
|
2013-01-27 15:35:39 +01:00
|
|
|
/**
|
|
|
|
* Represents a typical equivalence converter.
|
|
|
|
*
|
|
|
|
* @author Kristian
|
2017-05-24 20:35:22 +02:00
|
|
|
* @param <TType> - type that can be converted.
|
2017-07-24 20:15:56 +02:00
|
|
|
* @deprecated Replaced by {@link Converters#ignoreNull(EquivalentConverter)}
|
2013-01-27 15:35:39 +01:00
|
|
|
*/
|
2017-07-24 20:15:56 +02:00
|
|
|
@Deprecated
|
2016-07-06 21:47:24 +02:00
|
|
|
public static abstract class IgnoreNullConverter<TType> implements EquivalentConverter<TType> {
|
2014-12-06 03:41:55 +01:00
|
|
|
@Override
|
2017-07-24 20:15:56 +02:00
|
|
|
public final Object getGeneric(TType specific) {
|
2013-01-27 15:35:39 +01:00
|
|
|
if (specific != null)
|
2017-07-24 20:15:56 +02:00
|
|
|
return getGenericValue(specific);
|
2013-01-27 15:35:39 +01:00
|
|
|
else
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve a copy of the actual generic value.
|
|
|
|
* @param specific - the specific type-
|
|
|
|
* @return A copy of the specific type.
|
|
|
|
*/
|
2017-07-24 20:15:56 +02:00
|
|
|
public abstract Object getGenericValue(TType specific);
|
2013-01-27 15:35:39 +01:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public final TType getSpecific(Object generic) {
|
|
|
|
if (generic != null)
|
|
|
|
return getSpecificValue(generic);
|
|
|
|
else
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve a copy of the specific type using an instance of the generic type.
|
|
|
|
* @param generic - generic type.
|
|
|
|
* @return A copy of the specific type.
|
|
|
|
*/
|
2017-07-24 20:15:56 +02:00
|
|
|
public abstract TType getSpecificValue(Object generic);
|
2013-01-27 15:35:39 +01:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean equals(Object obj) {
|
2016-08-06 19:19:14 +02:00
|
|
|
if (this == obj) return true;
|
|
|
|
|
2013-01-27 15:35:39 +01:00
|
|
|
if (obj instanceof EquivalentConverter) {
|
2016-08-06 19:19:14 +02:00
|
|
|
EquivalentConverter<?> that = (EquivalentConverter<?>) obj;
|
|
|
|
return Objects.equal(this.getSpecificType(), that.getSpecificType());
|
2013-01-27 15:35:39 +01:00
|
|
|
}
|
2016-08-06 19:19:14 +02:00
|
|
|
|
2013-01-27 15:35:39 +01:00
|
|
|
return false;
|
|
|
|
}
|
2016-08-06 19:19:14 +02:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public int hashCode() {
|
|
|
|
return Objects.hashCode(this.getSpecificType());
|
|
|
|
}
|
2013-01-27 15:35:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Represents a converter that is only valid in a given world.
|
|
|
|
*
|
|
|
|
* @author Kristian
|
|
|
|
* @param <TType> - instance types it converts.
|
|
|
|
*/
|
2017-07-24 20:15:56 +02:00
|
|
|
private static abstract class WorldSpecificConverter<TType> implements EquivalentConverter<TType> {
|
2013-01-27 15:35:39 +01:00
|
|
|
protected World world;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initialize a new world-specificn converter.
|
|
|
|
* @param world - the given world.
|
|
|
|
*/
|
2017-07-24 20:15:56 +02:00
|
|
|
WorldSpecificConverter(World world) {
|
2013-01-27 15:35:39 +01:00
|
|
|
super();
|
|
|
|
this.world = world;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean equals(Object obj) {
|
2016-08-06 19:19:14 +02:00
|
|
|
if (obj == this) return true;
|
|
|
|
|
2013-01-27 15:35:39 +01:00
|
|
|
// Add another constraint
|
|
|
|
if (obj instanceof WorldSpecificConverter && super.equals(obj)) {
|
2016-08-06 19:19:14 +02:00
|
|
|
WorldSpecificConverter<?> that = (WorldSpecificConverter<?>) obj;
|
|
|
|
return Objects.equal(this.world, that.world);
|
2013-01-27 15:35:39 +01:00
|
|
|
}
|
2016-08-06 19:19:14 +02:00
|
|
|
|
2013-01-27 15:35:39 +01:00
|
|
|
return false;
|
|
|
|
}
|
2016-08-06 19:19:14 +02:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public int hashCode() {
|
|
|
|
return Objects.hashCode(this.getSpecificType(), this.world);
|
|
|
|
}
|
2013-01-27 15:35:39 +01:00
|
|
|
}
|
2016-08-06 19:19:14 +02:00
|
|
|
|
2017-07-24 20:15:56 +02:00
|
|
|
public static <K, V> EquivalentConverter<Map<K, V>> getMapConverter(EquivalentConverter<K> keyConverter,
|
|
|
|
EquivalentConverter<V> valConverter) {
|
|
|
|
return new EquivalentConverter<Map<K, V>>() {
|
|
|
|
@Override
|
|
|
|
public Map<K, V> getSpecific(Object generic) {
|
|
|
|
Map<Object, Object> genericMap = (Map<Object, Object>) generic;
|
|
|
|
Map<K, V> newMap;
|
|
|
|
|
|
|
|
try {
|
|
|
|
newMap = (Map<K, V>) genericMap.getClass().newInstance();
|
|
|
|
} catch (ReflectiveOperationException ex) {
|
|
|
|
newMap = new HashMap<>();
|
2014-01-02 07:23:27 +01:00
|
|
|
}
|
|
|
|
|
2017-07-24 20:15:56 +02:00
|
|
|
for (Map.Entry<Object, Object> entry : genericMap.entrySet()) {
|
|
|
|
newMap.put(keyConverter.getSpecific(entry.getKey()), valConverter.getSpecific(entry.getValue()));
|
|
|
|
}
|
|
|
|
|
|
|
|
return newMap;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Object getGeneric(Map<K, V> specific) {
|
|
|
|
Map<Object, Object> newMap;
|
|
|
|
|
|
|
|
try {
|
|
|
|
newMap = specific.getClass().newInstance();
|
|
|
|
} catch (ReflectiveOperationException ex) {
|
|
|
|
newMap = new HashMap<>();
|
2014-01-02 07:23:27 +01:00
|
|
|
}
|
|
|
|
|
2017-07-24 20:15:56 +02:00
|
|
|
for (Map.Entry<K, V> entry : specific.entrySet()) {
|
|
|
|
newMap.put(keyConverter.getGeneric(entry.getKey()), valConverter.getGeneric(entry.getValue()));
|
2014-01-02 07:23:27 +01:00
|
|
|
}
|
2017-07-24 20:15:56 +02:00
|
|
|
|
|
|
|
return newMap;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Class<Map<K, V>> getSpecificType() {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
};
|
2014-01-02 07:23:27 +01:00
|
|
|
}
|
2017-07-24 20:15:56 +02:00
|
|
|
|
2021-07-09 05:22:56 +02:00
|
|
|
private static final Map<Class<?>, Supplier<List<Object>>> LIST_SUPPLIERS = new ConcurrentHashMap<>();
|
|
|
|
|
|
|
|
private static <T> Object getGenericList(Class<?> listClass, List<T> specific, EquivalentConverter<T> itemConverter) {
|
|
|
|
List<Object> newList;
|
|
|
|
Supplier<List<Object>> supplier = LIST_SUPPLIERS.get(listClass);
|
|
|
|
if (supplier == null) {
|
|
|
|
try {
|
|
|
|
Constructor<?> ctor = listClass.getConstructor();
|
|
|
|
newList = (List<Object>) ctor.newInstance();
|
|
|
|
supplier = () -> {
|
|
|
|
try {
|
|
|
|
return (List<Object>) ctor.newInstance();
|
|
|
|
} catch (ReflectiveOperationException ex) {
|
|
|
|
throw new RuntimeException(ex);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
} catch (ReflectiveOperationException ex) {
|
2021-07-10 17:09:56 +02:00
|
|
|
// ex.printStackTrace();
|
2021-07-09 05:22:56 +02:00
|
|
|
supplier = ArrayList::new;
|
|
|
|
newList = new ArrayList<>();
|
|
|
|
}
|
|
|
|
|
|
|
|
LIST_SUPPLIERS.put(listClass, supplier);
|
|
|
|
} else {
|
|
|
|
newList = supplier.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Convert each object
|
|
|
|
for (T position : specific) {
|
|
|
|
if (position != null) {
|
|
|
|
Object converted = itemConverter.getGeneric(position);
|
|
|
|
if (converted != null) {
|
|
|
|
newList.add(converted);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
newList.add(null);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return newList;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static <T> List<T> getSpecificList(Object generic, EquivalentConverter<T> itemConverter) {
|
|
|
|
if (generic instanceof Collection) {
|
|
|
|
List<T> items = new ArrayList<>();
|
|
|
|
|
|
|
|
// Copy everything to a new list
|
|
|
|
for (Object item : (Collection<Object>) generic) {
|
|
|
|
T result = itemConverter.getSpecific(item);
|
|
|
|
|
|
|
|
if (item != null)
|
|
|
|
items.add(result);
|
|
|
|
}
|
|
|
|
return items;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Not valid
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static <T> EquivalentConverter<List<T>> getListConverter(final Class<?> listClass, final EquivalentConverter<T> itemConverter) {
|
|
|
|
return ignoreNull(new EquivalentConverter<List<T>>() {
|
|
|
|
@Override
|
|
|
|
public List<T> getSpecific(Object generic) {
|
|
|
|
return getSpecificList(generic, itemConverter);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Object getGeneric(List<T> specific) {
|
|
|
|
return getGenericList(listClass, specific, itemConverter);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Class<List<T>> getSpecificType() {
|
|
|
|
// Damn you Java
|
|
|
|
Class<?> dummy = List.class;
|
|
|
|
return (Class<List<T>>) dummy;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2021-06-24 23:13:40 +02:00
|
|
|
|
2013-07-28 02:02:27 +02:00
|
|
|
/**
|
|
|
|
* Retrieve an equivalent converter for a list of generic items.
|
2015-06-17 21:08:58 +02:00
|
|
|
* @param <T> Type
|
2013-07-28 02:02:27 +02:00
|
|
|
* @param itemConverter - an equivalent converter for the generic type.
|
|
|
|
* @return An equivalent converter.
|
|
|
|
*/
|
2017-07-24 20:15:56 +02:00
|
|
|
public static <T> EquivalentConverter<List<T>> getListConverter(final EquivalentConverter<T> itemConverter) {
|
2012-11-13 14:34:07 +01:00
|
|
|
// Convert to and from the wrapper
|
2017-07-24 20:15:56 +02:00
|
|
|
return ignoreNull(new EquivalentConverter<List<T>>() {
|
|
|
|
@Override
|
|
|
|
public List<T> getSpecific(Object generic) {
|
2021-07-09 05:22:56 +02:00
|
|
|
return getSpecificList(generic, itemConverter);
|
2017-07-24 20:15:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Object getGeneric(List<T> specific) {
|
2021-07-09 05:22:56 +02:00
|
|
|
return getGenericList(specific.getClass(), specific, itemConverter);
|
2017-07-24 20:15:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Class<List<T>> getSpecificType() {
|
|
|
|
// Damn you Java
|
|
|
|
Class<?> dummy = List.class;
|
|
|
|
return (Class<List<T>>) dummy;
|
|
|
|
}
|
|
|
|
});
|
2012-11-13 14:34:07 +01:00
|
|
|
}
|
2015-06-23 18:59:13 +02:00
|
|
|
|
2020-07-05 19:29:52 +02:00
|
|
|
@SuppressWarnings("rawtypes")
|
|
|
|
public static <A, B> EquivalentConverter<Pair<A, B>> getPairConverter(final EquivalentConverter<A> firstConverter,
|
|
|
|
final EquivalentConverter<B> secondConverter) {
|
|
|
|
return ignoreNull(new EquivalentConverter<Pair<A, B>>() {
|
|
|
|
@Override
|
|
|
|
public Object getGeneric(Pair<A, B> specific) {
|
|
|
|
Object first = firstConverter.getGeneric(specific.getFirst());
|
|
|
|
Object second = secondConverter.getGeneric(specific.getSecond());
|
|
|
|
|
|
|
|
return new com.mojang.datafixers.util.Pair(first, second);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Pair<A, B> getSpecific(Object generic) {
|
|
|
|
com.mojang.datafixers.util.Pair mjPair = (com.mojang.datafixers.util.Pair) generic;
|
|
|
|
|
|
|
|
A first = firstConverter.getSpecific(mjPair.getFirst());
|
|
|
|
B second = secondConverter.getSpecific(mjPair.getSecond());
|
|
|
|
|
|
|
|
return new Pair(first, second);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Class<Pair<A, B>> getSpecificType() {
|
|
|
|
Class<?> dummy = Pair.class;
|
|
|
|
return (Class<Pair<A, B>>) dummy;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-07-26 19:29:34 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @param leftConverter convert the left value if available
|
|
|
|
* @param rightConverter convert the right value if available
|
|
|
|
* @return converter for Mojang either class
|
|
|
|
* @param <A> converted left type
|
|
|
|
* @param <B> converted right type
|
|
|
|
*/
|
|
|
|
public static <A, B> EquivalentConverter<Either<A, B>> getEitherConverter(EquivalentConverter<A> leftConverter,
|
|
|
|
EquivalentConverter<B> rightConverter) {
|
|
|
|
return ignoreNull(new EquivalentConverter<Either<A, B>>() {
|
|
|
|
@Override
|
|
|
|
public Object getGeneric(Either<A, B> specific) {
|
|
|
|
return specific.map(
|
|
|
|
left -> com.mojang.datafixers.util.Either.left(leftConverter.getGeneric(left)),
|
|
|
|
right -> com.mojang.datafixers.util.Either.right(rightConverter.getGeneric(right))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Either<A, B> getSpecific(Object generic) {
|
|
|
|
com.mojang.datafixers.util.Either<A, B> mjEither = (com.mojang.datafixers.util.Either<A, B>) generic;
|
|
|
|
|
|
|
|
return mjEither.map(
|
|
|
|
left -> new Left<>(leftConverter.getSpecific(left)),
|
|
|
|
right -> new Right<>(rightConverter.getSpecific(right))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Class<Either<A, B>> getSpecificType() {
|
|
|
|
Class<?> dummy = Either.class;
|
|
|
|
return (Class<Either<A, B>>) dummy;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2015-06-23 18:59:13 +02:00
|
|
|
/**
|
|
|
|
* Retrieve an equivalent converter for a set of generic items.
|
2017-07-24 20:15:56 +02:00
|
|
|
* @param <T> Element type
|
2015-06-23 18:59:13 +02:00
|
|
|
* @param itemConverter - an equivalent converter for the generic type.
|
|
|
|
* @return An equivalent converter.
|
|
|
|
*/
|
|
|
|
@SuppressWarnings("unchecked")
|
2017-07-24 20:15:56 +02:00
|
|
|
public static <T> EquivalentConverter<Set<T>> getSetConverter(final EquivalentConverter<T> itemConverter) {
|
2015-06-23 18:59:13 +02:00
|
|
|
// Convert to and from the wrapper
|
2017-07-24 20:15:56 +02:00
|
|
|
return ignoreNull(new EquivalentConverter<Set<T>>() {
|
2015-06-23 18:59:13 +02:00
|
|
|
|
|
|
|
@Override
|
2017-07-24 20:15:56 +02:00
|
|
|
public Set<T> getSpecific(Object generic) {
|
2015-06-23 18:59:13 +02:00
|
|
|
if (generic instanceof Collection) {
|
2017-07-24 20:15:56 +02:00
|
|
|
Set<T> items = new HashSet<>();
|
2015-06-23 18:59:13 +02:00
|
|
|
|
|
|
|
// Copy everything to a new list
|
|
|
|
for (Object item : (Collection<Object>) generic) {
|
|
|
|
T result = itemConverter.getSpecific(item);
|
|
|
|
|
|
|
|
if (item != null)
|
|
|
|
items.add(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
return items;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Not valid
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-07-24 20:15:56 +02:00
|
|
|
public Object getGeneric(Set<T> specific) {
|
|
|
|
Set<Object> newList;
|
|
|
|
|
|
|
|
try {
|
|
|
|
newList = (Set<Object>) specific.getClass().newInstance();
|
|
|
|
} catch (ReflectiveOperationException ex) {
|
|
|
|
newList = new HashSet<>();
|
|
|
|
}
|
2015-06-23 18:59:13 +02:00
|
|
|
|
|
|
|
// Convert each object
|
|
|
|
for (T position : specific) {
|
2017-07-24 20:15:56 +02:00
|
|
|
if (position != null) {
|
|
|
|
Object converted = itemConverter.getGeneric(position);
|
|
|
|
if (converted != null) {
|
|
|
|
newList.add(converted);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
newList.add(null);
|
|
|
|
}
|
2015-06-23 18:59:13 +02:00
|
|
|
}
|
|
|
|
|
2017-07-24 20:15:56 +02:00
|
|
|
return newList;
|
2015-06-23 18:59:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Class<Set<T>> getSpecificType() {
|
|
|
|
// Damn you Java
|
|
|
|
Class<?> dummy = Set.class;
|
|
|
|
return (Class<Set<T>>) dummy;
|
|
|
|
}
|
2017-07-24 20:15:56 +02:00
|
|
|
});
|
2015-06-23 18:59:13 +02:00
|
|
|
}
|
|
|
|
|
2013-12-09 23:09:08 +01:00
|
|
|
/**
|
|
|
|
* Retrieve an equivalent converter for an array of generic items.
|
2015-06-17 21:08:58 +02:00
|
|
|
* @param <T> Type
|
2013-12-09 23:09:08 +01:00
|
|
|
* <p>
|
|
|
|
* The array is wrapped in a list.
|
|
|
|
* @param genericItemType - the generic item type.
|
|
|
|
* @param itemConverter - an equivalent converter for the generic type.
|
|
|
|
* @return An equivalent converter.
|
|
|
|
*/
|
2017-07-24 20:15:56 +02:00
|
|
|
public static <T> EquivalentConverter<Iterable<? extends T>> getArrayConverter(final Class<?> genericItemType,
|
|
|
|
final EquivalentConverter<T> itemConverter) {
|
2013-12-09 23:09:08 +01:00
|
|
|
// Convert to and from the wrapper
|
2017-07-24 20:15:56 +02:00
|
|
|
return ignoreNull(new EquivalentConverter<Iterable<? extends T>>() {
|
|
|
|
@Override
|
|
|
|
public List<T> getSpecific(Object generic) {
|
|
|
|
if (generic instanceof Object[]) {
|
|
|
|
ImmutableList.Builder<T> builder = ImmutableList.builder();
|
2013-12-09 23:09:08 +01:00
|
|
|
|
2017-07-24 20:15:56 +02:00
|
|
|
// Copy everything to a new list
|
|
|
|
for (Object item : (Object[]) generic) {
|
|
|
|
T result = itemConverter.getSpecific(item);
|
|
|
|
builder.add(result);
|
2013-12-09 23:09:08 +01:00
|
|
|
}
|
2017-07-24 20:15:56 +02:00
|
|
|
return builder.build();
|
2013-12-09 23:09:08 +01:00
|
|
|
}
|
|
|
|
|
2017-07-24 20:15:56 +02:00
|
|
|
// Not valid
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Object getGeneric(Iterable<? extends T> specific) {
|
|
|
|
List<T> list = Lists.newArrayList(specific);
|
|
|
|
Object[] output = (Object[]) Array.newInstance(genericItemType, list.size());
|
|
|
|
|
|
|
|
// Convert each object
|
|
|
|
for (int i = 0; i < output.length; i++) {
|
|
|
|
Object converted = itemConverter.getGeneric(list.get(i));
|
|
|
|
output[i] = converted;
|
2013-12-09 23:09:08 +01:00
|
|
|
}
|
2017-07-24 20:15:56 +02:00
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Class<Iterable<? extends T>> getSpecificType() {
|
|
|
|
// Damn you Java
|
|
|
|
Class<?> dummy = Iterable.class;
|
|
|
|
return (Class<Iterable<? extends T>>) dummy;
|
|
|
|
}
|
|
|
|
});
|
2013-12-09 23:09:08 +01:00
|
|
|
}
|
|
|
|
|
2013-12-06 02:31:50 +01:00
|
|
|
/**
|
2017-07-24 20:15:56 +02:00
|
|
|
* Retrieve a converter for wrapped game profiles.
|
|
|
|
* @return Wrapped game profile converter.
|
2013-12-06 02:31:50 +01:00
|
|
|
*/
|
|
|
|
public static EquivalentConverter<WrappedGameProfile> getWrappedGameProfileConverter() {
|
2020-10-19 22:30:42 +02:00
|
|
|
return ignoreNull(handle(WrappedGameProfile::getHandle, WrappedGameProfile::fromHandle, WrappedGameProfile.class));
|
2013-12-06 02:31:50 +01:00
|
|
|
}
|
|
|
|
|
2013-12-08 23:45:35 +01:00
|
|
|
/**
|
|
|
|
* Retrieve a converter for wrapped chat components.
|
2015-01-26 23:26:07 +01:00
|
|
|
* @return Wrapped chat component.
|
2013-12-08 23:45:35 +01:00
|
|
|
*/
|
|
|
|
public static EquivalentConverter<WrappedChatComponent> getWrappedChatComponentConverter() {
|
2020-10-19 22:30:42 +02:00
|
|
|
return ignoreNull(handle(WrappedChatComponent::getHandle, WrappedChatComponent::fromHandle, WrappedChatComponent.class));
|
2013-12-08 23:45:35 +01:00
|
|
|
}
|
|
|
|
|
2015-01-26 23:26:07 +01:00
|
|
|
/**
|
|
|
|
* Retrieve a converter for wrapped block data.
|
|
|
|
* @return Wrapped block data.
|
|
|
|
*/
|
|
|
|
public static EquivalentConverter<WrappedBlockData> getWrappedBlockDataConverter() {
|
2020-10-19 22:30:42 +02:00
|
|
|
return ignoreNull(handle(WrappedBlockData::getHandle, WrappedBlockData::fromHandle, WrappedBlockData.class));
|
2015-01-26 23:26:07 +01:00
|
|
|
}
|
|
|
|
|
2013-07-28 02:02:27 +02:00
|
|
|
/**
|
|
|
|
* Retrieve a converter for wrapped attribute snapshots.
|
|
|
|
* @return Wrapped attribute snapshot converter.
|
|
|
|
*/
|
|
|
|
public static EquivalentConverter<WrappedAttribute> getWrappedAttributeConverter() {
|
2020-10-19 22:30:42 +02:00
|
|
|
return ignoreNull(handle(WrappedAttribute::getHandle, WrappedAttribute::fromHandle, WrappedAttribute.class));
|
2013-07-28 02:02:27 +02:00
|
|
|
}
|
2022-06-08 03:24:31 +02:00
|
|
|
|
|
|
|
public static EquivalentConverter<WrappedProfilePublicKey> getWrappedProfilePublicKeyConverter() {
|
|
|
|
return ignoreNull(handle(WrappedProfilePublicKey::getHandle, WrappedProfilePublicKey::new, WrappedProfilePublicKey.class));
|
|
|
|
}
|
|
|
|
|
|
|
|
public static EquivalentConverter<WrappedProfileKeyData> getWrappedPublicKeyDataConverter() {
|
|
|
|
return ignoreNull(handle(WrappedProfileKeyData::getHandle, WrappedProfileKeyData::new, WrappedProfileKeyData.class));
|
|
|
|
}
|
|
|
|
|
2023-04-29 21:49:51 +02:00
|
|
|
public static EquivalentConverter<WrappedRemoteChatSessionData> getWrappedRemoteChatSessionDataConverter() {
|
|
|
|
return ignoreNull(handle(WrappedRemoteChatSessionData::getHandle, WrappedRemoteChatSessionData::new, WrappedRemoteChatSessionData.class));
|
|
|
|
}
|
|
|
|
|
2022-07-26 19:29:34 +02:00
|
|
|
/**
|
|
|
|
* @return converter for cryptographic signature data that are used in login and chat packets
|
|
|
|
*/
|
|
|
|
public static EquivalentConverter<WrappedSaltedSignature> getWrappedSignatureConverter() {
|
|
|
|
return ignoreNull(handle(WrappedSaltedSignature::getHandle, WrappedSaltedSignature::new, WrappedSaltedSignature.class));
|
|
|
|
}
|
|
|
|
|
2023-05-01 05:28:19 +02:00
|
|
|
/**
|
|
|
|
* @return converter for an encoded cryptographic message signature
|
|
|
|
*/
|
|
|
|
public static EquivalentConverter<WrappedMessageSignature> getWrappedMessageSignatureConverter() {
|
|
|
|
return ignoreNull(handle(WrappedMessageSignature::getHandle, WrappedMessageSignature::new, WrappedMessageSignature.class));
|
|
|
|
}
|
|
|
|
|
2022-08-07 00:52:42 +02:00
|
|
|
public static EquivalentConverter<WrappedLevelChunkData.ChunkData> getWrappedChunkDataConverter() {
|
|
|
|
return ignoreNull(handle(WrappedLevelChunkData.ChunkData::getHandle, WrappedLevelChunkData.ChunkData::new, WrappedLevelChunkData.ChunkData.class));
|
|
|
|
}
|
|
|
|
|
|
|
|
public static EquivalentConverter<WrappedLevelChunkData.LightData> getWrappedLightDataConverter() {
|
|
|
|
return ignoreNull(handle(WrappedLevelChunkData.LightData::getHandle, WrappedLevelChunkData.LightData::new, WrappedLevelChunkData.LightData.class));
|
|
|
|
}
|
|
|
|
|
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
|
|
|
public static EquivalentConverter<PacketContainer> getPacketContainerConverter() {
|
2023-04-11 15:13:03 +02:00
|
|
|
return ignoreNull(handle(PacketContainer::getHandle, PacketContainer::fromPacket, PacketContainer.class));
|
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
|
|
|
}
|
|
|
|
|
2012-11-13 14:34:07 +01:00
|
|
|
/**
|
|
|
|
* Retrieve a converter for watchable objects and the respective wrapper.
|
|
|
|
* @return A watchable object converter.
|
|
|
|
*/
|
|
|
|
public static EquivalentConverter<WrappedWatchableObject> getWatchableObjectConverter() {
|
2017-07-24 20:15:56 +02:00
|
|
|
return ignoreNull(new EquivalentConverter<WrappedWatchableObject>() {
|
2012-11-13 14:34:07 +01:00
|
|
|
@Override
|
2017-07-24 20:15:56 +02:00
|
|
|
public Object getGeneric(WrappedWatchableObject specific) {
|
2012-11-13 14:34:07 +01:00
|
|
|
return specific.getHandle();
|
|
|
|
}
|
2017-07-24 20:15:56 +02:00
|
|
|
|
2014-12-06 03:41:55 +01:00
|
|
|
@Override
|
2017-07-24 20:15:56 +02:00
|
|
|
public WrappedWatchableObject getSpecific(Object generic) {
|
2022-07-24 16:16:05 +02:00
|
|
|
if (MinecraftReflection.is(MinecraftReflection.getDataWatcherItemClass(), generic))
|
2012-12-06 03:37:20 +01:00
|
|
|
return new WrappedWatchableObject(generic);
|
2012-11-13 14:34:07 +01:00
|
|
|
else if (generic instanceof WrappedWatchableObject)
|
|
|
|
return (WrappedWatchableObject) generic;
|
|
|
|
else
|
|
|
|
throw new IllegalArgumentException("Unrecognized type " + generic.getClass());
|
2017-07-24 20:15:56 +02:00
|
|
|
}
|
|
|
|
|
2012-11-13 14:34:07 +01:00
|
|
|
@Override
|
|
|
|
public Class<WrappedWatchableObject> getSpecificType() {
|
|
|
|
return WrappedWatchableObject.class;
|
|
|
|
}
|
2017-07-24 20:15:56 +02:00
|
|
|
});
|
2012-11-13 14:34:07 +01:00
|
|
|
}
|
2022-12-14 02:38:37 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve a converter for data values in 1.19.3+.
|
|
|
|
* @return A data value converter.
|
|
|
|
*/
|
|
|
|
public static EquivalentConverter<WrappedDataValue> getDataValueConverter() {
|
|
|
|
return ignoreNull(new EquivalentConverter<WrappedDataValue>() {
|
|
|
|
@Override
|
|
|
|
public Object getGeneric(WrappedDataValue specific) {
|
|
|
|
return specific.getHandle();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public WrappedDataValue getSpecific(Object generic) {
|
|
|
|
return new WrappedDataValue(generic);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Class<WrappedDataValue> getSpecificType() {
|
|
|
|
return WrappedDataValue.class;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2012-11-13 14:34:07 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve a converter for the NMS DataWatcher class and our wrapper.
|
|
|
|
* @return A DataWatcher converter.
|
|
|
|
*/
|
|
|
|
public static EquivalentConverter<WrappedDataWatcher> getDataWatcherConverter() {
|
2017-07-24 20:15:56 +02:00
|
|
|
return ignoreNull(new EquivalentConverter<WrappedDataWatcher>() {
|
2012-11-13 14:34:07 +01:00
|
|
|
@Override
|
2017-07-24 20:15:56 +02:00
|
|
|
public Object getGeneric(WrappedDataWatcher specific) {
|
2012-11-13 14:34:07 +01:00
|
|
|
return specific.getHandle();
|
|
|
|
}
|
2017-07-24 20:15:56 +02:00
|
|
|
|
2012-11-13 14:34:07 +01:00
|
|
|
@Override
|
2017-07-24 20:15:56 +02:00
|
|
|
public WrappedDataWatcher getSpecific(Object generic) {
|
2012-12-06 03:37:20 +01:00
|
|
|
if (MinecraftReflection.isDataWatcher(generic))
|
|
|
|
return new WrappedDataWatcher(generic);
|
2012-11-13 14:34:07 +01:00
|
|
|
else if (generic instanceof WrappedDataWatcher)
|
|
|
|
return (WrappedDataWatcher) generic;
|
|
|
|
else
|
|
|
|
throw new IllegalArgumentException("Unrecognized type " + generic.getClass());
|
|
|
|
}
|
2017-07-24 20:15:56 +02:00
|
|
|
|
2012-11-13 14:34:07 +01:00
|
|
|
@Override
|
|
|
|
public Class<WrappedDataWatcher> getSpecificType() {
|
|
|
|
return WrappedDataWatcher.class;
|
|
|
|
}
|
2017-07-24 20:15:56 +02:00
|
|
|
});
|
2012-11-13 14:34:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve a converter for Bukkit's world type enum and the NMS equivalent.
|
|
|
|
* @return A world type enum converter.
|
|
|
|
*/
|
|
|
|
public static EquivalentConverter<WorldType> getWorldTypeConverter() {
|
|
|
|
// Check that we can actually use this converter
|
|
|
|
if (!hasWorldType)
|
|
|
|
return null;
|
2017-07-24 20:15:56 +02:00
|
|
|
|
2013-11-01 02:09:59 +01:00
|
|
|
final Class<?> worldType = MinecraftReflection.getWorldTypeClass();
|
2017-07-24 20:15:56 +02:00
|
|
|
|
|
|
|
return ignoreNull(new EquivalentConverter<WorldType>() {
|
2012-11-13 14:34:07 +01:00
|
|
|
@Override
|
2017-07-24 20:15:56 +02:00
|
|
|
public Object getGeneric(WorldType specific) {
|
2012-12-06 03:37:20 +01:00
|
|
|
try {
|
2013-11-01 02:09:59 +01:00
|
|
|
// Deduce getType method by parameters alone
|
|
|
|
if (worldTypeGetType == null) {
|
|
|
|
worldTypeGetType = FuzzyReflection.fromClass(worldType).
|
2022-07-24 16:16:05 +02:00
|
|
|
getMethodByReturnTypeAndParameters("getType", worldType, new Class<?>[]{String.class});
|
2013-11-01 02:09:59 +01:00
|
|
|
}
|
2017-07-24 20:15:56 +02:00
|
|
|
|
2012-12-06 03:37:20 +01:00
|
|
|
// Convert to the Bukkit world type
|
|
|
|
return worldTypeGetType.invoke(this, specific.getName());
|
2017-07-24 20:15:56 +02:00
|
|
|
|
2012-12-06 03:37:20 +01:00
|
|
|
} catch (Exception e) {
|
|
|
|
throw new FieldAccessException("Cannot find the WorldType.getType() method.", e);
|
2014-12-06 03:41:55 +01:00
|
|
|
}
|
2012-11-13 14:34:07 +01:00
|
|
|
}
|
2012-12-23 18:52:00 +01:00
|
|
|
|
2012-11-13 14:34:07 +01:00
|
|
|
@Override
|
2017-07-24 20:15:56 +02:00
|
|
|
public WorldType getSpecific(Object generic) {
|
2012-12-06 03:37:20 +01:00
|
|
|
try {
|
2013-11-01 02:09:59 +01:00
|
|
|
if (worldTypeName == null) {
|
|
|
|
try {
|
|
|
|
worldTypeName = worldType.getMethod("name");
|
|
|
|
} catch (Exception e) {
|
|
|
|
// Assume the first method is the one
|
|
|
|
worldTypeName = FuzzyReflection.fromClass(worldType).
|
2022-07-24 16:16:05 +02:00
|
|
|
getMethodByReturnTypeAndParameters("name", String.class, new Class<?>[]{});
|
2013-11-01 02:09:59 +01:00
|
|
|
}
|
|
|
|
}
|
2017-07-24 20:15:56 +02:00
|
|
|
|
2012-12-06 03:37:20 +01:00
|
|
|
// Dynamically call the namne method
|
|
|
|
String name = (String) worldTypeName.invoke(generic);
|
|
|
|
return WorldType.getByName(name);
|
2017-07-24 20:15:56 +02:00
|
|
|
|
2012-12-06 03:37:20 +01:00
|
|
|
} catch (Exception e) {
|
|
|
|
throw new FieldAccessException("Cannot call the name method in WorldType.", e);
|
|
|
|
}
|
2012-11-13 14:34:07 +01:00
|
|
|
}
|
2017-07-24 20:15:56 +02:00
|
|
|
|
2012-11-13 14:34:07 +01:00
|
|
|
@Override
|
|
|
|
public Class<WorldType> getSpecificType() {
|
|
|
|
return WorldType.class;
|
|
|
|
}
|
2017-07-24 20:15:56 +02:00
|
|
|
});
|
2012-11-13 14:34:07 +01:00
|
|
|
}
|
|
|
|
|
2013-01-07 10:14:25 +01:00
|
|
|
/**
|
|
|
|
* Retrieve an equivalent converter for net.minecraft.server NBT classes and their wrappers.
|
|
|
|
* @return An equivalent converter for NBT.
|
|
|
|
*/
|
2013-01-08 22:24:07 +01:00
|
|
|
public static EquivalentConverter<NbtBase<?>> getNbtConverter() {
|
2017-07-24 20:15:56 +02:00
|
|
|
return ignoreNull(new EquivalentConverter<NbtBase<?>>() {
|
2013-01-07 10:14:25 +01:00
|
|
|
@Override
|
2017-07-24 20:15:56 +02:00
|
|
|
public Object getGeneric(NbtBase<?> specific) {
|
2013-01-08 22:24:07 +01:00
|
|
|
return NbtFactory.fromBase(specific).getHandle();
|
2013-01-07 10:14:25 +01:00
|
|
|
}
|
2017-07-24 20:15:56 +02:00
|
|
|
|
2013-01-07 10:14:25 +01:00
|
|
|
@Override
|
2017-07-24 20:15:56 +02:00
|
|
|
public NbtBase<?> getSpecific(Object generic) {
|
2013-12-05 06:52:20 +01:00
|
|
|
return NbtFactory.fromNMS(generic, null);
|
2013-01-07 10:14:25 +01:00
|
|
|
}
|
2017-07-24 20:15:56 +02:00
|
|
|
|
2013-01-07 10:14:25 +01:00
|
|
|
@Override
|
|
|
|
@SuppressWarnings("unchecked")
|
2013-01-08 22:24:07 +01:00
|
|
|
public Class<NbtBase<?>> getSpecificType() {
|
2013-01-07 10:14:25 +01:00
|
|
|
// Damn you Java AGAIN
|
2013-01-08 22:24:07 +01:00
|
|
|
Class<?> dummy = NbtBase.class;
|
|
|
|
return (Class<NbtBase<?>>) dummy;
|
2013-01-07 10:14:25 +01:00
|
|
|
}
|
2017-07-24 20:15:56 +02:00
|
|
|
});
|
2013-01-07 10:14:25 +01:00
|
|
|
}
|
|
|
|
|
2012-11-13 14:34:07 +01:00
|
|
|
/**
|
|
|
|
* Retrieve a converter for NMS entities and Bukkit entities.
|
|
|
|
* @param world - the current world.
|
|
|
|
* @return A converter between the underlying NMS entity and Bukkit's wrapper.
|
|
|
|
*/
|
|
|
|
public static EquivalentConverter<Entity> getEntityConverter(World world) {
|
2017-07-24 20:15:56 +02:00
|
|
|
final WeakReference<ProtocolManager> managerRef = new WeakReference<>(ProtocolLibrary.getProtocolManager());
|
2012-11-13 14:34:07 +01:00
|
|
|
|
2013-01-27 15:35:39 +01:00
|
|
|
return new WorldSpecificConverter<Entity>(world) {
|
2012-11-13 14:34:07 +01:00
|
|
|
@Override
|
2017-07-24 20:15:56 +02:00
|
|
|
public Object getGeneric(Entity specific) {
|
2012-11-13 14:34:07 +01:00
|
|
|
// Simple enough
|
|
|
|
return specific.getEntityId();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-07-24 20:15:56 +02:00
|
|
|
public Entity getSpecific(Object generic) {
|
2012-11-13 14:34:07 +01:00
|
|
|
try {
|
|
|
|
Integer id = (Integer) generic;
|
2012-11-20 06:46:21 +01:00
|
|
|
ProtocolManager manager = managerRef.get();
|
2012-11-13 14:34:07 +01:00
|
|
|
|
2013-01-27 15:35:39 +01:00
|
|
|
// Use the entity ID to get a reference to the entity
|
2021-09-22 16:51:47 +02:00
|
|
|
if (id != null && id >= 0 && manager != null) {
|
2013-01-27 15:35:39 +01:00
|
|
|
return manager.getEntityFromID(world, id);
|
2012-11-13 14:34:07 +01:00
|
|
|
} else {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2012-11-13 16:10:56 +01:00
|
|
|
} catch (FieldAccessException e) {
|
|
|
|
throw new RuntimeException("Cannot retrieve entity from ID.", e);
|
2012-11-13 14:34:07 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Class<Entity> getSpecificType() {
|
|
|
|
return Entity.class;
|
|
|
|
}
|
2013-01-27 15:35:39 +01:00
|
|
|
};
|
2012-11-13 14:34:07 +01:00
|
|
|
}
|
2019-10-29 21:52:30 +01:00
|
|
|
|
|
|
|
private static MethodAccessor getEntityTypeName;
|
|
|
|
private static MethodAccessor entityTypeFromName;
|
|
|
|
|
|
|
|
public static EquivalentConverter<EntityType> getEntityTypeConverter() {
|
|
|
|
return ignoreNull(new EquivalentConverter<EntityType>() {
|
|
|
|
@Override
|
|
|
|
public Object getGeneric(EntityType specific) {
|
|
|
|
if (entityTypeFromName == null) {
|
2021-06-13 17:36:44 +02:00
|
|
|
Class<?> entityTypesClass = MinecraftReflection.getEntityTypes();
|
2019-10-29 21:52:30 +01:00
|
|
|
entityTypeFromName = Accessors.getMethodAccessor(
|
|
|
|
FuzzyReflection
|
|
|
|
.fromClass(entityTypesClass, false)
|
|
|
|
.getMethod(FuzzyMethodContract
|
|
|
|
.newBuilder()
|
|
|
|
.returnDerivedOf(Optional.class)
|
|
|
|
.parameterExactArray(new Class<?>[]{ String.class })
|
|
|
|
.build()));
|
|
|
|
}
|
|
|
|
|
|
|
|
Optional<?> opt = (Optional<?>) entityTypeFromName.invoke(null, specific.getName());
|
|
|
|
return opt.orElse(null);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public EntityType getSpecific(Object generic) {
|
|
|
|
if (getEntityTypeName == null) {
|
2021-06-13 17:36:44 +02:00
|
|
|
Class<?> entityTypesClass = MinecraftReflection.getEntityTypes();
|
2019-10-29 21:52:30 +01:00
|
|
|
getEntityTypeName = Accessors.getMethodAccessor(
|
|
|
|
FuzzyReflection
|
|
|
|
.fromClass(entityTypesClass, false)
|
|
|
|
.getMethod(FuzzyMethodContract
|
|
|
|
.newBuilder()
|
|
|
|
.returnTypeExact(MinecraftReflection.getMinecraftKeyClass())
|
|
|
|
.parameterExactArray(new Class<?>[]{ entityTypesClass })
|
|
|
|
.build()));
|
|
|
|
}
|
|
|
|
|
|
|
|
MinecraftKey key = MinecraftKey.fromHandle(getEntityTypeName.invoke(null, generic));
|
|
|
|
return EntityType.fromName(key.getKey());
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Class<EntityType> getSpecificType() {
|
|
|
|
return EntityType.class;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2012-11-13 14:34:07 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the converter used to convert NMS ItemStacks to Bukkit's ItemStack.
|
|
|
|
* @return Item stack converter.
|
|
|
|
*/
|
|
|
|
public static EquivalentConverter<ItemStack> getItemStackConverter() {
|
2016-11-20 22:29:23 +01:00
|
|
|
return new EquivalentConverter<ItemStack>() {
|
2013-01-27 15:35:39 +01:00
|
|
|
@Override
|
2016-11-20 22:29:23 +01:00
|
|
|
public ItemStack getSpecific(Object generic) {
|
|
|
|
return MinecraftReflection.getBukkitItemStack(generic);
|
2012-11-13 14:34:07 +01:00
|
|
|
}
|
2016-11-20 22:29:23 +01:00
|
|
|
|
2012-11-13 14:34:07 +01:00
|
|
|
@Override
|
2017-07-24 20:15:56 +02:00
|
|
|
public Object getGeneric(ItemStack specific) {
|
2016-11-20 22:29:23 +01:00
|
|
|
return MinecraftReflection.getMinecraftItemStack(specific);
|
2012-11-13 14:34:07 +01:00
|
|
|
}
|
2016-11-20 22:29:23 +01:00
|
|
|
|
2012-11-13 14:34:07 +01:00
|
|
|
@Override
|
|
|
|
public Class<ItemStack> getSpecificType() {
|
|
|
|
return ItemStack.class;
|
|
|
|
}
|
2013-01-27 15:35:39 +01:00
|
|
|
};
|
2012-11-13 14:34:07 +01:00
|
|
|
}
|
2016-11-20 22:29:23 +01:00
|
|
|
|
2013-12-09 23:09:08 +01:00
|
|
|
/**
|
2017-07-24 20:15:56 +02:00
|
|
|
* Retrieve the converter for the ServerPing packet in {@link PacketType.Status.Server#SERVER_INFO}.
|
2013-12-09 23:09:08 +01:00
|
|
|
* @return Server ping converter.
|
|
|
|
*/
|
|
|
|
public static EquivalentConverter<WrappedServerPing> getWrappedServerPingConverter() {
|
2020-10-19 22:30:42 +02:00
|
|
|
return ignoreNull(handle(WrappedServerPing::getHandle, WrappedServerPing::fromHandle, WrappedServerPing.class));
|
2013-12-09 23:09:08 +01:00
|
|
|
}
|
|
|
|
|
2014-01-02 07:23:27 +01:00
|
|
|
/**
|
|
|
|
* Retrieve the converter for a statistic.
|
|
|
|
* @return Statistic converter.
|
|
|
|
*/
|
|
|
|
public static EquivalentConverter<WrappedStatistic> getWrappedStatisticConverter() {
|
2020-10-19 22:30:42 +02:00
|
|
|
return ignoreNull(handle(WrappedStatistic::getHandle, WrappedStatistic::fromHandle, WrappedStatistic.class));
|
2014-01-02 07:23:27 +01:00
|
|
|
}
|
2018-07-19 19:13:06 +02:00
|
|
|
|
2018-07-19 19:29:20 +02:00
|
|
|
private static MethodAccessor BLOCK_FROM_MATERIAL;
|
|
|
|
private static MethodAccessor MATERIAL_FROM_BLOCK;
|
|
|
|
|
2013-12-10 13:21:31 +01:00
|
|
|
/**
|
|
|
|
* Retrieve a converter for block instances.
|
|
|
|
* @return A converter for block instances.
|
|
|
|
*/
|
|
|
|
public static EquivalentConverter<Material> getBlockConverter() {
|
2018-07-19 19:29:20 +02:00
|
|
|
if (BLOCK_FROM_MATERIAL == null || MATERIAL_FROM_BLOCK == null) {
|
|
|
|
Class<?> magicNumbers = MinecraftReflection.getCraftBukkitClass("util.CraftMagicNumbers");
|
|
|
|
Class<?> block = MinecraftReflection.getBlockClass();
|
|
|
|
|
|
|
|
FuzzyReflection fuzzy = FuzzyReflection.fromClass(magicNumbers);
|
|
|
|
FuzzyMethodContract.Builder builder = FuzzyMethodContract
|
|
|
|
.newBuilder()
|
|
|
|
.requireModifier(Modifier.STATIC)
|
|
|
|
.returnTypeExact(Material.class)
|
|
|
|
.parameterExactArray(block);
|
|
|
|
MATERIAL_FROM_BLOCK = Accessors.getMethodAccessor(fuzzy.getMethod(builder.build()));
|
|
|
|
|
|
|
|
builder = FuzzyMethodContract
|
|
|
|
.newBuilder()
|
|
|
|
.requireModifier(Modifier.STATIC)
|
|
|
|
.returnTypeExact(block)
|
|
|
|
.parameterExactArray(Material.class);
|
|
|
|
BLOCK_FROM_MATERIAL = Accessors.getMethodAccessor(fuzzy.getMethod(builder.build()));
|
|
|
|
}
|
|
|
|
|
2017-07-24 20:15:56 +02:00
|
|
|
return ignoreNull(new EquivalentConverter<Material>() {
|
2015-10-08 00:12:50 +02:00
|
|
|
@Override
|
2017-07-24 20:15:56 +02:00
|
|
|
public Object getGeneric(Material specific) {
|
2018-07-19 19:29:20 +02:00
|
|
|
return BLOCK_FROM_MATERIAL.invoke(null, specific);
|
2015-10-08 00:12:50 +02:00
|
|
|
}
|
2017-07-24 20:15:56 +02:00
|
|
|
|
2015-10-08 00:12:50 +02:00
|
|
|
@Override
|
2017-07-24 20:15:56 +02:00
|
|
|
public Material getSpecific(Object generic) {
|
2018-07-19 19:29:20 +02:00
|
|
|
return (Material) MATERIAL_FROM_BLOCK.invoke(null, generic);
|
2015-10-08 00:12:50 +02:00
|
|
|
}
|
2017-07-24 20:15:56 +02:00
|
|
|
|
2015-10-08 00:12:50 +02:00
|
|
|
@Override
|
|
|
|
public Class<Material> getSpecificType() {
|
|
|
|
return Material.class;
|
|
|
|
}
|
2017-07-24 20:15:56 +02:00
|
|
|
});
|
2015-10-08 00:12:50 +02:00
|
|
|
}
|
|
|
|
|
2014-05-02 03:49:33 +02:00
|
|
|
/**
|
|
|
|
* Retrieve the converter used to convert between a NMS World and a Bukkit world.
|
2014-05-02 17:17:01 +02:00
|
|
|
* @return The world converter.
|
2014-05-02 03:49:33 +02:00
|
|
|
*/
|
2014-12-06 03:41:55 +01:00
|
|
|
public static EquivalentConverter<World> getWorldConverter() {
|
2017-07-24 20:15:56 +02:00
|
|
|
return ignoreNull(new EquivalentConverter<World>() {
|
2014-05-02 03:49:33 +02:00
|
|
|
@Override
|
2017-07-24 20:15:56 +02:00
|
|
|
public Object getGeneric(World specific) {
|
2014-05-02 03:49:33 +02:00
|
|
|
return BukkitUnwrapper.getInstance().unwrapItem(specific);
|
|
|
|
}
|
2017-07-24 20:15:56 +02:00
|
|
|
|
2014-05-02 03:49:33 +02:00
|
|
|
@Override
|
2017-07-24 20:15:56 +02:00
|
|
|
public World getSpecific(Object generic) {
|
2014-05-02 03:49:33 +02:00
|
|
|
return (World) craftWorldField.get(generic);
|
|
|
|
}
|
2017-07-24 20:15:56 +02:00
|
|
|
|
2014-05-02 03:49:33 +02:00
|
|
|
@Override
|
|
|
|
public Class<World> getSpecificType() {
|
|
|
|
return World.class;
|
|
|
|
}
|
2017-07-24 20:15:56 +02:00
|
|
|
});
|
2014-05-02 03:49:33 +02:00
|
|
|
}
|
|
|
|
|
2012-11-13 14:34:07 +01:00
|
|
|
/**
|
2013-11-19 00:50:55 +01:00
|
|
|
* Retrieve the converter used to convert between a PotionEffect and the equivalent NMS Mobeffect.
|
|
|
|
* @return The potion effect converter.
|
|
|
|
*/
|
2014-12-06 03:41:55 +01:00
|
|
|
public static EquivalentConverter<PotionEffect> getPotionEffectConverter() {
|
2017-07-24 20:15:56 +02:00
|
|
|
return ignoreNull(new EquivalentConverter<PotionEffect>() {
|
2013-11-19 00:50:55 +01:00
|
|
|
@Override
|
2017-07-24 20:15:56 +02:00
|
|
|
public Object getGeneric(PotionEffect specific) {
|
2013-11-19 00:50:55 +01:00
|
|
|
// Locate the constructor
|
|
|
|
if (mobEffectConstructor == null) {
|
|
|
|
try {
|
|
|
|
mobEffectConstructor = MinecraftReflection.getMobEffectClass().
|
|
|
|
getConstructor(int.class, int.class, int.class, boolean.class);
|
|
|
|
} catch (Exception e) {
|
|
|
|
throw new RuntimeException("Cannot find mob effect constructor (int, int, int, boolean).", e);
|
|
|
|
}
|
|
|
|
}
|
2017-07-24 20:15:56 +02:00
|
|
|
|
2013-11-19 00:50:55 +01:00
|
|
|
// Create the generic value
|
|
|
|
try {
|
|
|
|
return mobEffectConstructor.newInstance(
|
2017-07-24 20:15:56 +02:00
|
|
|
specific.getType().getId(), specific.getDuration(),
|
|
|
|
specific.getAmplifier(), specific.isAmbient());
|
2013-11-19 00:50:55 +01:00
|
|
|
} catch (Exception e) {
|
|
|
|
throw new RuntimeException("Cannot construct MobEffect.", e);
|
|
|
|
}
|
|
|
|
}
|
2017-07-24 20:15:56 +02:00
|
|
|
|
2013-11-19 00:50:55 +01:00
|
|
|
@Override
|
2017-07-24 20:15:56 +02:00
|
|
|
public PotionEffect getSpecific(Object generic) {
|
2013-11-19 00:50:55 +01:00
|
|
|
if (mobEffectModifier == null) {
|
2022-07-24 16:16:05 +02:00
|
|
|
mobEffectModifier = new StructureModifier<>(MinecraftReflection.getMobEffectClass());
|
2013-11-19 00:50:55 +01:00
|
|
|
}
|
|
|
|
StructureModifier<Integer> ints = mobEffectModifier.withTarget(generic).withType(int.class);
|
|
|
|
StructureModifier<Boolean> bools = mobEffectModifier.withTarget(generic).withType(boolean.class);
|
2017-07-24 20:15:56 +02:00
|
|
|
|
2013-11-19 00:50:55 +01:00
|
|
|
return new PotionEffect(
|
2017-07-24 20:15:56 +02:00
|
|
|
PotionEffectType.getById(ints.read(0)), /* effectId */
|
|
|
|
ints.read(1), /* duration */
|
|
|
|
ints.read(2), /* amplification */
|
|
|
|
bools.read(1) /* ambient */
|
2013-11-19 00:50:55 +01:00
|
|
|
);
|
|
|
|
}
|
2017-07-24 20:15:56 +02:00
|
|
|
|
2013-11-19 00:50:55 +01:00
|
|
|
@Override
|
|
|
|
public Class<PotionEffect> getSpecificType() {
|
|
|
|
return PotionEffect.class;
|
|
|
|
}
|
2017-07-24 20:15:56 +02:00
|
|
|
});
|
2013-11-19 00:50:55 +01:00
|
|
|
}
|
2014-12-06 03:41:55 +01:00
|
|
|
|
|
|
|
private static Constructor<?> vec3dConstructor;
|
|
|
|
private static StructureModifier<Object> vec3dModifier;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the converter used to convert between a Vector and the equivalent NMS Vec3d.
|
|
|
|
* @return The Vector converter.
|
|
|
|
*/
|
|
|
|
public static EquivalentConverter<Vector> getVectorConverter() {
|
2017-07-24 20:15:56 +02:00
|
|
|
return ignoreNull(new EquivalentConverter<Vector>() {
|
2014-12-06 03:41:55 +01:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public Class<Vector> getSpecificType() {
|
|
|
|
return Vector.class;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-07-24 20:15:56 +02:00
|
|
|
public Object getGeneric(Vector specific) {
|
2014-12-06 03:41:55 +01:00
|
|
|
if (vec3dConstructor == null) {
|
|
|
|
try {
|
2014-12-06 03:45:45 +01:00
|
|
|
vec3dConstructor = MinecraftReflection.getVec3DClass().getConstructor(
|
2014-12-06 03:41:55 +01:00
|
|
|
double.class, double.class, double.class);
|
|
|
|
} catch (Throwable ex) {
|
|
|
|
throw new RuntimeException("Could not find Vec3d constructor (double, double, double)");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
return vec3dConstructor.newInstance(specific.getX(), specific.getY(), specific.getZ());
|
|
|
|
} catch (Throwable ex) {
|
|
|
|
throw new RuntimeException("Could not construct Vec3d.", ex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-07-24 20:15:56 +02:00
|
|
|
public Vector getSpecific(Object generic) {
|
2014-12-06 03:41:55 +01:00
|
|
|
if (vec3dModifier == null) {
|
2022-07-24 16:16:05 +02:00
|
|
|
vec3dModifier = new StructureModifier<>(MinecraftReflection.getVec3DClass());
|
2014-12-06 03:41:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
StructureModifier<Double> doubles = vec3dModifier.withTarget(generic).withType(double.class);
|
|
|
|
return new Vector(
|
|
|
|
doubles.read(0), /* x */
|
|
|
|
doubles.read(1), /* y */
|
|
|
|
doubles.read(2) /* z */
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2017-07-24 20:15:56 +02:00
|
|
|
});
|
2014-12-06 03:41:55 +01:00
|
|
|
}
|
2016-03-05 03:24:12 +01:00
|
|
|
|
2023-03-31 00:07:34 +02:00
|
|
|
static MethodAccessor getSound = null;
|
|
|
|
static MethodAccessor getSoundEffect = null;
|
|
|
|
static FieldAccessor soundKey = null;
|
2016-03-05 03:24:12 +01:00
|
|
|
|
2023-03-31 00:07:34 +02:00
|
|
|
static MethodAccessor getSoundEffectByKey = null;
|
|
|
|
static MethodAccessor getSoundEffectBySound = null;
|
|
|
|
static MethodAccessor getSoundByEffect = null;
|
2021-01-03 05:55:23 +01:00
|
|
|
|
2023-03-31 00:07:34 +02:00
|
|
|
static Map<String, Sound> soundIndex = null;
|
2016-12-22 17:42:52 +01:00
|
|
|
|
2016-03-05 03:24:12 +01:00
|
|
|
public static EquivalentConverter<Sound> getSoundConverter() {
|
2021-01-03 05:55:23 +01:00
|
|
|
// Try to create sound converter for new versions greater 1.16.4
|
2023-03-31 00:07:34 +02:00
|
|
|
if (MinecraftVersion.NETHER_UPDATE_4.atOrAbove()) {
|
2021-01-03 05:55:23 +01:00
|
|
|
if (getSoundEffectByKey == null || getSoundEffectBySound == null || getSoundByEffect == null) {
|
|
|
|
Class<?> craftSound = MinecraftReflection.getCraftSoundClass();
|
|
|
|
FuzzyReflection fuzzy = FuzzyReflection.fromClass(craftSound, true);
|
|
|
|
|
2022-07-24 16:16:05 +02:00
|
|
|
getSoundEffectByKey = Accessors.getMethodAccessor(fuzzy.getMethodByReturnTypeAndParameters(
|
2021-01-03 05:55:23 +01:00
|
|
|
"getSoundEffect",
|
|
|
|
MinecraftReflection.getSoundEffectClass(),
|
2023-03-31 00:07:34 +02:00
|
|
|
String.class
|
2021-01-03 05:55:23 +01:00
|
|
|
));
|
|
|
|
|
2022-07-24 16:16:05 +02:00
|
|
|
getSoundEffectBySound = Accessors.getMethodAccessor(fuzzy.getMethodByReturnTypeAndParameters(
|
2021-01-03 05:55:23 +01:00
|
|
|
"getSoundEffect",
|
|
|
|
MinecraftReflection.getSoundEffectClass(),
|
2023-03-31 00:07:34 +02:00
|
|
|
Sound.class
|
2021-01-03 05:55:23 +01:00
|
|
|
));
|
|
|
|
|
2022-07-24 16:16:05 +02:00
|
|
|
getSoundByEffect = Accessors.getMethodAccessor(fuzzy.getMethodByReturnTypeAndParameters(
|
2021-01-03 05:55:23 +01:00
|
|
|
"getBukkit",
|
|
|
|
Sound.class,
|
2023-03-31 00:07:34 +02:00
|
|
|
MinecraftReflection.getSoundEffectClass()
|
2021-01-03 05:55:23 +01:00
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
return ignoreNull(new EquivalentConverter<Sound>() {
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Class<Sound> getSpecificType() {
|
|
|
|
return Sound.class;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Object getGeneric(Sound specific) {
|
|
|
|
return getSoundEffectBySound.invoke(null, specific);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Sound getSpecific(Object generic) {
|
2023-03-31 00:07:34 +02:00
|
|
|
try {
|
|
|
|
return (Sound) getSoundByEffect.invoke(null, generic);
|
|
|
|
} catch (IllegalStateException ex) {
|
|
|
|
if (ex.getCause() instanceof NullPointerException) {
|
|
|
|
// "null" sounds cause NPEs inside getSoundByEffect
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
throw ex;
|
|
|
|
}
|
2021-01-03 05:55:23 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fall back to sound converter from legacy versions before 1.16.4
|
2016-12-22 17:42:52 +01:00
|
|
|
if (getSound == null || getSoundEffect == null) {
|
|
|
|
Class<?> craftSound = MinecraftReflection.getCraftSoundClass();
|
|
|
|
FuzzyReflection fuzzy = FuzzyReflection.fromClass(craftSound, true);
|
2017-07-24 20:15:56 +02:00
|
|
|
getSound = Accessors.getMethodAccessor(
|
2023-03-31 00:07:34 +02:00
|
|
|
fuzzy.getMethodByReturnTypeAndParameters("getSound", String.class, Sound.class));
|
2022-07-24 16:16:05 +02:00
|
|
|
getSoundEffect = Accessors.getMethodAccessor(fuzzy.getMethodByReturnTypeAndParameters("getSoundEffect",
|
2023-03-31 00:07:34 +02:00
|
|
|
MinecraftReflection.getSoundEffectClass(), String.class));
|
2016-12-22 17:42:52 +01:00
|
|
|
}
|
|
|
|
|
2017-07-24 20:15:56 +02:00
|
|
|
return ignoreNull(new EquivalentConverter<Sound>() {
|
2016-03-05 03:24:12 +01:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public Class<Sound> getSpecificType() {
|
|
|
|
return Sound.class;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-07-24 20:15:56 +02:00
|
|
|
public Object getGeneric(Sound specific) {
|
2016-12-22 17:42:52 +01:00
|
|
|
// Getting the SoundEffect is easy, Bukkit provides us the methods
|
|
|
|
String key = (String) getSound.invoke(null, specific);
|
|
|
|
return getSoundEffect.invoke(null, key);
|
2016-03-05 03:24:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-07-24 20:15:56 +02:00
|
|
|
public Sound getSpecific(Object generic) {
|
2016-12-22 17:42:52 +01:00
|
|
|
// Getting the Sound is a bit more complicated...
|
2016-03-05 03:24:12 +01:00
|
|
|
if (soundKey == null) {
|
|
|
|
Class<?> soundEffect = generic.getClass();
|
|
|
|
FuzzyReflection fuzzy = FuzzyReflection.fromClass(soundEffect, true);
|
2017-07-24 20:15:56 +02:00
|
|
|
soundKey = Accessors.getFieldAccessor(
|
|
|
|
fuzzy.getFieldByType("key", MinecraftReflection.getMinecraftKeyClass()));
|
2016-03-05 03:24:12 +01:00
|
|
|
}
|
|
|
|
|
2016-12-22 17:42:52 +01:00
|
|
|
MinecraftKey minecraftKey = MinecraftKey.fromHandle(soundKey.get(generic));
|
|
|
|
String key = minecraftKey.getKey();
|
|
|
|
|
|
|
|
// Use our index if it already exists
|
|
|
|
if (soundIndex != null) {
|
|
|
|
return soundIndex.get(key);
|
|
|
|
}
|
|
|
|
|
|
|
|
// If it doesn't, try to guess the enum name
|
|
|
|
try {
|
|
|
|
return Sound.valueOf(minecraftKey.getEnumFormat());
|
|
|
|
} catch (IllegalArgumentException ignored) {
|
|
|
|
}
|
|
|
|
|
|
|
|
// Worst case we index all the sounds and use it later
|
|
|
|
soundIndex = new ConcurrentHashMap<>();
|
|
|
|
for (Sound sound : Sound.values()) {
|
|
|
|
String index = (String) getSound.invoke(null, sound);
|
|
|
|
soundIndex.put(index, sound);
|
|
|
|
}
|
|
|
|
|
|
|
|
return soundIndex.get(key);
|
2016-03-05 03:24:12 +01:00
|
|
|
}
|
2017-07-24 20:15:56 +02:00
|
|
|
});
|
2016-03-05 03:24:12 +01:00
|
|
|
}
|
|
|
|
|
2018-08-01 23:38:56 +02:00
|
|
|
public static EquivalentConverter<WrappedParticle> getParticleConverter() {
|
2020-10-19 22:30:42 +02:00
|
|
|
return ignoreNull(handle(WrappedParticle::getHandle, WrappedParticle::fromHandle, WrappedParticle.class));
|
2018-08-01 23:38:56 +02:00
|
|
|
}
|
|
|
|
|
2017-07-24 20:15:56 +02:00
|
|
|
public static EquivalentConverter<Advancement> getAdvancementConverter() {
|
|
|
|
return ignoreNull(new EquivalentConverter<Advancement>() {
|
2013-01-27 15:35:39 +01:00
|
|
|
@Override
|
2017-07-24 20:15:56 +02:00
|
|
|
public Advancement getSpecific(Object generic) {
|
|
|
|
try {
|
|
|
|
return (Advancement) getCraftBukkitClass("advancement.CraftAdvancement")
|
2021-06-13 17:36:44 +02:00
|
|
|
.getConstructor(getMinecraftClass("advancements.Advancement", "Advancement"))
|
2017-07-24 20:15:56 +02:00
|
|
|
.newInstance(generic);
|
|
|
|
} catch (ReflectiveOperationException ex) {
|
|
|
|
throw new RuntimeException(ex);
|
|
|
|
}
|
2012-11-13 14:34:07 +01:00
|
|
|
}
|
2017-07-24 20:15:56 +02:00
|
|
|
|
2012-11-13 14:34:07 +01:00
|
|
|
@Override
|
2017-07-24 20:15:56 +02:00
|
|
|
public Object getGeneric(Advancement specific) {
|
|
|
|
return BukkitUnwrapper.getInstance().unwrapItem(specific);
|
2012-11-13 14:34:07 +01:00
|
|
|
}
|
2017-07-24 20:15:56 +02:00
|
|
|
|
2012-11-13 14:34:07 +01:00
|
|
|
@Override
|
2017-07-24 20:15:56 +02:00
|
|
|
public Class<Advancement> getSpecificType() {
|
|
|
|
return Advancement.class;
|
2012-11-13 14:34:07 +01:00
|
|
|
}
|
2017-07-24 20:15:56 +02:00
|
|
|
});
|
2012-11-13 14:34:07 +01:00
|
|
|
}
|
2017-07-24 20:15:56 +02:00
|
|
|
|
2013-11-19 00:50:55 +01:00
|
|
|
/**
|
|
|
|
* Retrieve an equivalent unwrapper for the converter.
|
|
|
|
* @param nativeType - the native NMS type the converter produces.
|
|
|
|
* @param converter - the converter.
|
|
|
|
* @return The equivalent unwrapper.
|
|
|
|
*/
|
|
|
|
public static Unwrapper asUnwrapper(final Class<?> nativeType, final EquivalentConverter<Object> converter) {
|
2017-07-24 20:15:56 +02:00
|
|
|
return wrappedObject -> {
|
|
|
|
Class<?> type = PacketConstructor.getClass(wrappedObject);
|
|
|
|
|
|
|
|
// Ensure the type is correct before we test
|
|
|
|
if (converter.getSpecificType().isAssignableFrom(type)) {
|
|
|
|
if (wrappedObject instanceof Class)
|
|
|
|
return nativeType;
|
|
|
|
else
|
|
|
|
return converter.getGeneric(wrappedObject);
|
2013-11-19 00:50:55 +01:00
|
|
|
}
|
2017-07-24 20:15:56 +02:00
|
|
|
return null;
|
2013-11-19 00:50:55 +01:00
|
|
|
};
|
|
|
|
}
|
2017-07-24 20:15:56 +02:00
|
|
|
|
2013-07-26 17:30:47 +02:00
|
|
|
/**
|
|
|
|
* Retrieve every converter that is associated with a generic class.
|
|
|
|
* @return Every converter with a unique generic class.
|
|
|
|
*/
|
2017-07-24 20:15:56 +02:00
|
|
|
@SuppressWarnings("rawtypes")
|
2013-07-28 02:02:27 +02:00
|
|
|
public static Map<Class<?>, EquivalentConverter<Object>> getConvertersForGeneric() {
|
2013-07-26 17:30:47 +02:00
|
|
|
if (genericConverters == null) {
|
2019-05-08 04:58:59 +02:00
|
|
|
ImmutableMap.Builder<Class<?>, EquivalentConverter<Object>> builder = ImmutableMap.builder();
|
|
|
|
addConverter(builder, MinecraftReflection::getDataWatcherClass, BukkitConverters::getDataWatcherConverter);
|
|
|
|
addConverter(builder, MinecraftReflection::getItemStackClass, BukkitConverters::getItemStackConverter);
|
|
|
|
addConverter(builder, MinecraftReflection::getNBTBaseClass, BukkitConverters::getNbtConverter);
|
|
|
|
addConverter(builder, MinecraftReflection::getNBTCompoundClass, BukkitConverters::getNbtConverter);
|
|
|
|
addConverter(builder, MinecraftReflection::getDataWatcherItemClass, BukkitConverters::getWatchableObjectConverter);
|
|
|
|
addConverter(builder, MinecraftReflection::getMobEffectClass, BukkitConverters::getPotionEffectConverter);
|
|
|
|
addConverter(builder, MinecraftReflection::getNmsWorldClass, BukkitConverters::getWorldConverter);
|
|
|
|
addConverter(builder, MinecraftReflection::getWorldTypeClass, BukkitConverters::getWorldTypeConverter);
|
|
|
|
addConverter(builder, MinecraftReflection::getAttributeSnapshotClass, BukkitConverters::getWrappedAttributeConverter);
|
|
|
|
addConverter(builder, MinecraftReflection::getBlockClass, BukkitConverters::getBlockConverter);
|
|
|
|
addConverter(builder, MinecraftReflection::getGameProfileClass, BukkitConverters::getWrappedGameProfileConverter);
|
|
|
|
addConverter(builder, MinecraftReflection::getServerPingClass, BukkitConverters::getWrappedServerPingConverter);
|
|
|
|
addConverter(builder, MinecraftReflection::getStatisticClass, BukkitConverters::getWrappedStatisticConverter);
|
|
|
|
addConverter(builder, MinecraftReflection::getIBlockDataClass, BukkitConverters::getWrappedBlockDataConverter);
|
|
|
|
|
|
|
|
for (Entry<Class<?>, EquivalentConverter<?>> entry : EnumWrappers.getFromNativeMap().entrySet()) {
|
|
|
|
addConverter(builder, entry::getKey, entry::getValue);
|
2013-12-08 23:45:35 +01:00
|
|
|
}
|
2019-05-08 04:58:59 +02:00
|
|
|
|
2013-07-26 18:42:06 +02:00
|
|
|
genericConverters = builder.build();
|
2013-07-26 17:30:47 +02:00
|
|
|
}
|
2019-05-08 04:58:59 +02:00
|
|
|
|
2013-07-26 17:30:47 +02:00
|
|
|
return genericConverters;
|
|
|
|
}
|
2019-05-08 04:58:59 +02:00
|
|
|
|
|
|
|
private static void addConverter(ImmutableMap.Builder<Class<?>, EquivalentConverter<Object>> builder,
|
|
|
|
Supplier<Class<?>> getClass, Supplier<EquivalentConverter> getConverter) {
|
|
|
|
try {
|
|
|
|
Class<?> clazz = getClass.get();
|
|
|
|
if (clazz != null) {
|
|
|
|
EquivalentConverter converter = getConverter.get();
|
|
|
|
if (converter != null) {
|
|
|
|
builder.put(clazz, converter);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (Exception ex) {
|
|
|
|
ProtocolLogger.debug("Exception registering converter", ex);
|
|
|
|
}
|
|
|
|
}
|
2013-11-19 00:50:55 +01:00
|
|
|
|
|
|
|
/**
|
2015-06-17 20:25:39 +02:00
|
|
|
* Retrieve every NMS to/from Bukkit converter as unwrappers.
|
2013-11-19 00:50:55 +01:00
|
|
|
* @return Every unwrapper.
|
|
|
|
*/
|
|
|
|
public static List<Unwrapper> getUnwrappers() {
|
|
|
|
if (unwrappers == null) {
|
|
|
|
ImmutableList.Builder<Unwrapper> builder = ImmutableList.builder();
|
|
|
|
|
|
|
|
for (Map.Entry<Class<?>, EquivalentConverter<Object>> entry : getConvertersForGeneric().entrySet()) {
|
|
|
|
builder.add(asUnwrapper(entry.getKey(), entry.getValue()));
|
|
|
|
}
|
|
|
|
unwrappers = builder.build();
|
|
|
|
}
|
|
|
|
return unwrappers;
|
|
|
|
}
|
2016-03-20 03:30:01 +01:00
|
|
|
|
|
|
|
private static MethodAccessor getMobEffectId = null;
|
|
|
|
private static MethodAccessor getMobEffect = null;
|
|
|
|
|
|
|
|
public static EquivalentConverter<PotionEffectType> getEffectTypeConverter() {
|
2017-07-24 20:15:56 +02:00
|
|
|
return ignoreNull(new EquivalentConverter<PotionEffectType>() {
|
2016-03-20 03:30:01 +01:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public Class<PotionEffectType> getSpecificType() {
|
|
|
|
return PotionEffectType.class;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-07-24 20:15:56 +02:00
|
|
|
public Object getGeneric(PotionEffectType specific) {
|
|
|
|
Class<?> clazz = MinecraftReflection.getMobEffectListClass();
|
2016-03-20 03:30:01 +01:00
|
|
|
if (getMobEffect == null) {
|
2021-06-21 22:10:58 +02:00
|
|
|
FuzzyReflection fuzzy = FuzzyReflection.fromClass(clazz, false);
|
|
|
|
getMobEffect = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
|
|
|
|
.parameterExactArray(int.class)
|
|
|
|
.returnTypeExact(clazz)
|
|
|
|
.requireModifier(Modifier.STATIC)
|
|
|
|
.build()));
|
2016-03-20 03:30:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int id = specific.getId();
|
|
|
|
return getMobEffect.invoke(null, id);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2017-07-24 20:15:56 +02:00
|
|
|
public PotionEffectType getSpecific(Object generic) {
|
2016-03-20 03:30:01 +01:00
|
|
|
Class<?> clazz = MinecraftReflection.getMobEffectListClass();
|
|
|
|
if (getMobEffectId == null) {
|
2021-06-21 22:10:58 +02:00
|
|
|
FuzzyReflection fuzzy = FuzzyReflection.fromClass(clazz, false);
|
|
|
|
getMobEffectId = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
|
|
|
|
.parameterExactArray(clazz)
|
|
|
|
.returnTypeExact(int.class)
|
|
|
|
.requireModifier(Modifier.STATIC)
|
|
|
|
.build()));
|
2016-03-20 03:30:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int id = (int) getMobEffectId.invoke(null, generic);
|
|
|
|
return PotionEffectType.getById(id);
|
|
|
|
}
|
2017-07-24 20:15:56 +02:00
|
|
|
});
|
2016-03-20 03:30:01 +01:00
|
|
|
}
|
2018-09-18 23:01:55 +02:00
|
|
|
|
2020-07-06 05:47:06 +02:00
|
|
|
private static Class<?> dimensionManager;
|
|
|
|
private static FauxEnumConverter<Dimension> dimensionConverter;
|
2020-08-24 19:19:49 +02:00
|
|
|
private static FauxEnumConverter<DimensionImpl> dimensionImplConverter;
|
2020-07-06 05:47:06 +02:00
|
|
|
|
2018-09-18 23:01:55 +02:00
|
|
|
private static MethodAccessor dimensionFromId = null;
|
|
|
|
private static MethodAccessor idFromDimension = null;
|
|
|
|
|
2020-08-05 01:03:59 +02:00
|
|
|
private static FieldAccessor worldKeyField = null;
|
|
|
|
private static MethodAccessor getServer = null;
|
|
|
|
private static MethodAccessor getWorldServer = null;
|
|
|
|
private static MethodAccessor getWorld = null;
|
|
|
|
|
|
|
|
public static EquivalentConverter<World> getWorldKeyConverter() {
|
|
|
|
return ignoreNull(new EquivalentConverter<World>() {
|
|
|
|
@Override
|
|
|
|
public Object getGeneric(World specific) {
|
|
|
|
Object nmsWorld = getWorldConverter().getGeneric(specific);
|
|
|
|
|
|
|
|
if (worldKeyField == null) {
|
|
|
|
Class<?> worldClass = MinecraftReflection.getNmsWorldClass();
|
2021-06-13 17:36:44 +02:00
|
|
|
Class<?> resourceKeyClass = MinecraftReflection.getResourceKey();
|
2020-08-05 01:03:59 +02:00
|
|
|
|
|
|
|
FuzzyReflection fuzzy = FuzzyReflection.fromClass(nmsWorld.getClass(), true);
|
|
|
|
worldKeyField = Accessors.getFieldAccessor(fuzzy.getParameterizedField(resourceKeyClass, worldClass));
|
|
|
|
}
|
|
|
|
|
|
|
|
return worldKeyField.get(nmsWorld);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public World getSpecific(Object generic) {
|
|
|
|
if (getServer == null) {
|
|
|
|
getServer = Accessors.getMethodAccessor(Bukkit.getServer().getClass(), "getServer");
|
|
|
|
}
|
|
|
|
|
|
|
|
Object server = getServer.invoke(Bukkit.getServer());
|
|
|
|
if (getWorldServer == null) {
|
|
|
|
FuzzyReflection fuzzy = FuzzyReflection.fromClass(server.getClass(), false);
|
|
|
|
getWorldServer = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract
|
|
|
|
.newBuilder()
|
|
|
|
.parameterExactArray(generic.getClass())
|
|
|
|
.returnTypeExact(MinecraftReflection.getWorldServerClass())
|
|
|
|
.build()));
|
|
|
|
}
|
|
|
|
|
|
|
|
Object worldServer = getWorldServer.invoke(server, generic);
|
|
|
|
if (getWorld == null) {
|
|
|
|
getWorld = Accessors.getMethodAccessor(worldServer.getClass(), "getWorld");
|
|
|
|
}
|
|
|
|
|
|
|
|
return (World) getWorld.invoke(worldServer);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Class<World> getSpecificType() {
|
|
|
|
return World.class;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-08-24 19:19:49 +02:00
|
|
|
enum DimensionImpl {
|
|
|
|
OVERWORLD_IMPL(0),
|
|
|
|
THE_NETHER_IMPL(-1),
|
|
|
|
THE_END_IMPL(1);
|
|
|
|
|
|
|
|
int id;
|
|
|
|
DimensionImpl(int id) {
|
|
|
|
this.id = id;
|
|
|
|
}
|
|
|
|
|
|
|
|
static DimensionImpl fromId(int id) {
|
|
|
|
switch (id) {
|
|
|
|
case 0: return OVERWORLD_IMPL;
|
|
|
|
case -1: return THE_NETHER_IMPL;
|
|
|
|
case 1: return THE_END_IMPL;
|
|
|
|
default: throw new IllegalArgumentException("Invalid dimension " + id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-13 23:18:36 +02:00
|
|
|
private static FieldAccessor dimensionKey;
|
2021-06-20 04:20:54 +02:00
|
|
|
private static MethodAccessor worldHandleAccessor;
|
|
|
|
private static MethodAccessor worldHandleDimensionManagerAccessor;
|
2021-06-13 23:18:36 +02:00
|
|
|
|
|
|
|
public static EquivalentConverter<World> getDimensionConverter() {
|
|
|
|
return ignoreNull(new EquivalentConverter<World>() {
|
|
|
|
@Override
|
|
|
|
public Object getGeneric(World specific) {
|
2021-06-20 04:20:54 +02:00
|
|
|
return getWorldHandleDimensionManagerAccessor().invoke(getWorldHandleAccessor().invoke(specific));
|
2021-06-13 23:18:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public World getSpecific(Object generic) {
|
|
|
|
for (World world : Bukkit.getWorlds()) {
|
2021-06-20 04:20:54 +02:00
|
|
|
if (getGeneric(world) == generic) {
|
2021-06-13 23:18:36 +02:00
|
|
|
return world;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
throw new IllegalArgumentException();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Class<World> getSpecificType() {
|
|
|
|
return World.class;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-06-20 04:20:54 +02:00
|
|
|
private static MethodAccessor getWorldHandleAccessor() {
|
|
|
|
if (worldHandleAccessor == null) {
|
|
|
|
Method handleMethod = FuzzyReflection.fromClass(MinecraftReflection.getCraftWorldClass())
|
|
|
|
.getMethod(FuzzyMethodContract.newBuilder()
|
|
|
|
.nameExact("getHandle") // i guess this will never change
|
|
|
|
.returnTypeExact(MinecraftReflection.getWorldServerClass())
|
|
|
|
.build());
|
|
|
|
worldHandleAccessor = Accessors.getMethodAccessor(handleMethod);
|
|
|
|
}
|
|
|
|
return worldHandleAccessor;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static MethodAccessor getWorldHandleDimensionManagerAccessor() {
|
|
|
|
if (worldHandleDimensionManagerAccessor == null) {
|
|
|
|
Method dimensionGetter = FuzzyReflection.fromClass(MinecraftReflection.getWorldServerClass())
|
|
|
|
.getMethod(FuzzyMethodContract.newBuilder()
|
|
|
|
.returnTypeExact(MinecraftReflection.getDimensionManager())
|
|
|
|
.build());
|
|
|
|
worldHandleDimensionManagerAccessor = Accessors.getMethodAccessor(dimensionGetter);
|
|
|
|
}
|
|
|
|
return worldHandleDimensionManagerAccessor;
|
|
|
|
}
|
|
|
|
|
2018-09-18 23:01:55 +02:00
|
|
|
public static EquivalentConverter<Integer> getDimensionIDConverter() {
|
2020-05-03 21:59:55 +02:00
|
|
|
return ignoreNull(new EquivalentConverter<Integer>() {
|
2018-09-18 23:01:55 +02:00
|
|
|
@Override
|
|
|
|
public Object getGeneric(Integer specific) {
|
2020-07-06 05:47:06 +02:00
|
|
|
if (dimensionManager == null) {
|
2021-06-13 17:36:44 +02:00
|
|
|
dimensionManager = MinecraftReflection.getDimensionManager();
|
2020-07-06 05:47:06 +02:00
|
|
|
}
|
|
|
|
|
2021-06-13 23:18:36 +02:00
|
|
|
if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
|
|
|
|
World world = null;
|
|
|
|
if (specific == 0) {
|
|
|
|
world = Bukkit.getWorlds().get(0);
|
|
|
|
} else if (specific == -1) {
|
|
|
|
for (World world1 : Bukkit.getWorlds()) {
|
|
|
|
if (world1.getEnvironment() == World.Environment.NETHER) {
|
|
|
|
world = world1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (specific == 1) {
|
|
|
|
for (World world1 : Bukkit.getWorlds()) {
|
|
|
|
if (world1.getEnvironment() == World.Environment.THE_END) {
|
|
|
|
world = world1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (world != null) {
|
2021-06-20 04:20:54 +02:00
|
|
|
try {
|
|
|
|
return getWorldHandleDimensionManagerAccessor().invoke(getWorldHandleAccessor().invoke(world));
|
|
|
|
} catch (Exception ignored) {
|
|
|
|
// method not available, fall through
|
|
|
|
}
|
2021-06-13 23:18:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
throw new IllegalArgumentException();
|
|
|
|
}
|
2020-08-24 19:19:49 +02:00
|
|
|
if (MinecraftVersion.NETHER_UPDATE_2.atOrAbove()) {
|
|
|
|
if (dimensionImplConverter == null) {
|
|
|
|
dimensionImplConverter = new FauxEnumConverter<>(DimensionImpl.class, dimensionManager);
|
|
|
|
}
|
|
|
|
|
|
|
|
DimensionImpl dimension = DimensionImpl.fromId(specific);
|
|
|
|
return dimensionImplConverter.getGeneric(dimension);
|
|
|
|
} else if (MinecraftVersion.NETHER_UPDATE.atOrAbove()) {
|
2020-07-06 05:47:06 +02:00
|
|
|
if (dimensionConverter == null) {
|
|
|
|
dimensionConverter = new FauxEnumConverter<>(Dimension.class, dimensionManager);
|
|
|
|
}
|
|
|
|
|
|
|
|
Dimension dimension = Dimension.fromId(specific);
|
|
|
|
return dimensionConverter.getGeneric(dimension);
|
2020-08-24 19:19:49 +02:00
|
|
|
} else {
|
|
|
|
if (dimensionFromId == null) {
|
|
|
|
FuzzyReflection reflection = FuzzyReflection.fromClass(dimensionManager, false);
|
|
|
|
FuzzyMethodContract contract = FuzzyMethodContract
|
|
|
|
.newBuilder()
|
|
|
|
.requireModifier(Modifier.STATIC)
|
|
|
|
.parameterExactType(int.class)
|
|
|
|
.returnTypeExact(dimensionManager)
|
|
|
|
.build();
|
|
|
|
dimensionFromId = Accessors.getMethodAccessor(reflection.getMethod(contract));
|
|
|
|
}
|
2020-07-06 05:47:06 +02:00
|
|
|
|
2020-08-24 19:19:49 +02:00
|
|
|
return dimensionFromId.invoke(null, specific);
|
2018-09-18 23:01:55 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Integer getSpecific(Object generic) {
|
2020-07-06 05:47:06 +02:00
|
|
|
if (dimensionManager == null) {
|
2021-06-13 17:36:44 +02:00
|
|
|
dimensionManager = MinecraftReflection.getDimensionManager();
|
2020-07-06 05:47:06 +02:00
|
|
|
}
|
|
|
|
|
2021-06-13 23:18:36 +02:00
|
|
|
if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
|
|
|
|
if (dimensionKey == null) {
|
|
|
|
FuzzyReflection fuzzy = FuzzyReflection.fromClass(dimensionManager, false);
|
|
|
|
dimensionKey = Accessors.getFieldAccessor(fuzzy.getField(FuzzyFieldContract
|
|
|
|
.newBuilder()
|
|
|
|
.typeExact(MinecraftReflection.getMinecraftKeyClass())
|
|
|
|
.banModifier(Modifier.STATIC)
|
|
|
|
.build()));
|
|
|
|
}
|
|
|
|
|
|
|
|
MinecraftKey key = MinecraftKey.fromHandle(dimensionKey.get(generic));
|
|
|
|
switch (key.getKey()) {
|
|
|
|
case "overworld":
|
|
|
|
return Dimension.OVERWORLD.getId();
|
|
|
|
case "the_nether":
|
|
|
|
return Dimension.THE_NETHER.getId();
|
|
|
|
case "the_end":
|
|
|
|
return Dimension.THE_END.getId();
|
|
|
|
default:
|
|
|
|
throw new IllegalArgumentException("id not supported for extra dimensions");
|
|
|
|
}
|
|
|
|
}
|
2020-08-24 19:19:49 +02:00
|
|
|
if (MinecraftVersion.NETHER_UPDATE_2.atOrAbove()) {
|
|
|
|
if (dimensionImplConverter == null) {
|
|
|
|
dimensionImplConverter = new FauxEnumConverter<>(DimensionImpl.class, dimensionManager);
|
|
|
|
}
|
|
|
|
|
|
|
|
DimensionImpl dimension = dimensionImplConverter.getSpecific(generic);
|
|
|
|
return dimension.id;
|
|
|
|
} else if (MinecraftVersion.NETHER_UPDATE.atOrAbove()) {
|
2020-07-06 05:47:06 +02:00
|
|
|
if (dimensionConverter == null) {
|
|
|
|
dimensionConverter = new FauxEnumConverter<>(Dimension.class, dimensionManager);
|
|
|
|
}
|
|
|
|
|
|
|
|
Dimension dimension = dimensionConverter.getSpecific(generic);
|
|
|
|
return dimension.getId();
|
2020-08-24 19:19:49 +02:00
|
|
|
} else {
|
|
|
|
if (idFromDimension == null) {
|
|
|
|
FuzzyReflection reflection = FuzzyReflection.fromClass(dimensionManager, false);
|
|
|
|
FuzzyMethodContract contract = FuzzyMethodContract
|
|
|
|
.newBuilder()
|
|
|
|
.banModifier(Modifier.STATIC)
|
|
|
|
.returnTypeExact(int.class)
|
|
|
|
.parameterCount(0)
|
|
|
|
.build();
|
|
|
|
idFromDimension = Accessors.getMethodAccessor(reflection.getMethod(contract));
|
|
|
|
}
|
2020-07-06 05:47:06 +02:00
|
|
|
|
2020-08-24 19:19:49 +02:00
|
|
|
return (Integer) idFromDimension.invoke(generic);
|
2018-09-18 23:01:55 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Class<Integer> getSpecificType() {
|
|
|
|
return Integer.class;
|
|
|
|
}
|
2020-05-03 21:59:55 +02:00
|
|
|
});
|
2018-09-18 23:01:55 +02:00
|
|
|
}
|
2020-07-27 01:33:05 +02:00
|
|
|
|
|
|
|
private static ConstructorAccessor merchantRecipeListConstructor = null;
|
|
|
|
private static MethodAccessor bukkitMerchantRecipeToCraft = null;
|
|
|
|
private static MethodAccessor craftMerchantRecipeToNMS = null;
|
|
|
|
private static MethodAccessor nmsMerchantRecipeToBukkit = null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a converter from a MerchantRecipeList (which is just an ArrayList of MerchantRecipe wrapper)
|
|
|
|
* to a {@link List} of {@link MerchantRecipe}. Primarily for the packet OPEN_WINDOW_MERCHANT which is present
|
|
|
|
* in 1.13+.
|
|
|
|
*
|
|
|
|
* @return The MerchantRecipeList converter.
|
|
|
|
*/
|
|
|
|
public static EquivalentConverter<List<MerchantRecipe>> getMerchantRecipeListConverter() {
|
|
|
|
return ignoreNull(new EquivalentConverter<List<MerchantRecipe>>() {
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Object getGeneric(List<MerchantRecipe> specific) {
|
|
|
|
if (merchantRecipeListConstructor == null) {
|
2021-06-13 17:36:44 +02:00
|
|
|
Class<?> merchantRecipeListClass = MinecraftReflection.getMerchantRecipeList();
|
2020-07-27 01:33:05 +02:00
|
|
|
merchantRecipeListConstructor = Accessors.getConstructorAccessor(merchantRecipeListClass);
|
|
|
|
Class<?> craftMerchantRecipeClass = MinecraftReflection.getCraftBukkitClass("inventory.CraftMerchantRecipe");
|
|
|
|
FuzzyReflection reflection = FuzzyReflection.fromClass(craftMerchantRecipeClass, false);
|
|
|
|
bukkitMerchantRecipeToCraft = Accessors.getMethodAccessor(reflection.getMethodByName("fromBukkit"));
|
|
|
|
craftMerchantRecipeToNMS = Accessors.getMethodAccessor(reflection.getMethodByName("toMinecraft"));
|
|
|
|
}
|
|
|
|
return specific.stream().map(recipe -> craftMerchantRecipeToNMS.invoke(bukkitMerchantRecipeToCraft.invoke(null, recipe)))
|
|
|
|
.collect(() -> (List<Object>)merchantRecipeListConstructor.invoke(), List::add, List::addAll);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public List<MerchantRecipe> getSpecific(Object generic) {
|
|
|
|
if (nmsMerchantRecipeToBukkit == null) {
|
2021-06-13 17:36:44 +02:00
|
|
|
Class<?> merchantRecipeClass = MinecraftReflection.getMinecraftClass(
|
2022-08-24 02:44:48 +02:00
|
|
|
"world.item.trading.MerchantRecipe", "world.item.trading.MerchantOffer","MerchantRecipe"
|
2021-06-13 17:36:44 +02:00
|
|
|
);
|
2020-07-27 01:33:05 +02:00
|
|
|
FuzzyReflection reflection = FuzzyReflection.fromClass(merchantRecipeClass, false);
|
|
|
|
nmsMerchantRecipeToBukkit = Accessors.getMethodAccessor(reflection.getMethodByName("asBukkit"));
|
|
|
|
}
|
|
|
|
return ((List<Object>)generic).stream().map(o -> (MerchantRecipe)nmsMerchantRecipeToBukkit.invoke(o)).collect(Collectors.toList());
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Class<List<MerchantRecipe>> getSpecificType() {
|
|
|
|
// Damn you Java
|
|
|
|
Class<?> dummy = List.class;
|
|
|
|
return (Class<List<MerchantRecipe>>) dummy;
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
}
|
2020-08-24 19:19:49 +02:00
|
|
|
|
|
|
|
private static MethodAccessor sectionPositionCreate;
|
|
|
|
private static Class<?> sectionPositionClass;
|
|
|
|
|
|
|
|
public static EquivalentConverter<BlockPosition> getSectionPositionConverter() {
|
|
|
|
return ignoreNull(new EquivalentConverter<BlockPosition>() {
|
|
|
|
@Override
|
|
|
|
public Object getGeneric(BlockPosition specific) {
|
|
|
|
if (sectionPositionClass == null) {
|
2021-06-13 17:36:44 +02:00
|
|
|
sectionPositionClass = MinecraftReflection.getSectionPosition();
|
2020-08-24 19:19:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (sectionPositionCreate == null) {
|
|
|
|
sectionPositionCreate = Accessors.getMethodAccessor(
|
|
|
|
FuzzyReflection.fromClass(sectionPositionClass).getMethod(FuzzyMethodContract
|
|
|
|
.newBuilder()
|
|
|
|
.requireModifier(Modifier.STATIC)
|
|
|
|
.returnTypeExact(sectionPositionClass)
|
|
|
|
.parameterExactArray(int.class, int.class, int.class)
|
|
|
|
.build())
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return sectionPositionCreate.invoke(null, specific.x, specific.y, specific.z);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public BlockPosition getSpecific(Object generic) {
|
|
|
|
StructureModifier<Integer> modifier = new StructureModifier<>(generic.getClass()).withTarget(generic).withType(int.class);
|
|
|
|
return new BlockPosition(modifier.readSafely(0), modifier.readSafely(1), modifier.readSafely(2));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Class<BlockPosition> getSpecificType() {
|
|
|
|
return BlockPosition.class;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2021-02-27 21:38:05 +01:00
|
|
|
|
|
|
|
private static Field gameStateMapField;
|
|
|
|
private static Field gameStateIdField;
|
|
|
|
|
|
|
|
public static EquivalentConverter<Integer> getGameStateConverter() {
|
|
|
|
return new EquivalentConverter<Integer>() {
|
|
|
|
@Override
|
|
|
|
public Object getGeneric(Integer specific) {
|
|
|
|
if (specific == null) {
|
|
|
|
specific = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (MinecraftVersion.NETHER_UPDATE.atOrAbove()) {
|
|
|
|
if (gameStateMapField == null) {
|
|
|
|
Class<?> stateClass = MinecraftReflection.getGameStateClass();
|
|
|
|
gameStateMapField = FuzzyReflection
|
|
|
|
.fromClass(stateClass, true)
|
|
|
|
.getField(FuzzyFieldContract
|
|
|
|
.newBuilder()
|
|
|
|
.typeDerivedOf(Map.class)
|
|
|
|
.build());
|
|
|
|
gameStateMapField.setAccessible(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
Map<Integer, Object> map = (Map<Integer, Object>) gameStateMapField.get(null);
|
|
|
|
return map.get(specific);
|
|
|
|
} catch (ReflectiveOperationException ex) {
|
|
|
|
throw new RuntimeException(ex);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return specific;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Integer getSpecific(Object generic) {
|
|
|
|
if (generic == null) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (MinecraftVersion.NETHER_UPDATE.atOrAbove()) {
|
|
|
|
if (gameStateIdField == null) {
|
|
|
|
Class<?> stateClass = MinecraftReflection.getGameStateClass();
|
|
|
|
gameStateIdField = FuzzyReflection
|
|
|
|
.fromClass(stateClass, true)
|
|
|
|
.getField(FuzzyFieldContract
|
|
|
|
.newBuilder()
|
|
|
|
.typeExact(int.class)
|
|
|
|
.build());
|
|
|
|
gameStateIdField.setAccessible(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
return (Integer) gameStateIdField.get(generic);
|
|
|
|
} catch (ReflectiveOperationException ex) {
|
|
|
|
throw new RuntimeException(ex);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return (Integer) generic;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Class<Integer> getSpecificType() {
|
|
|
|
return Integer.class;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
2012-11-13 14:34:07 +01:00
|
|
|
}
|