mirror of
https://github.com/dmulloy2/ProtocolLib.git
synced 2024-09-27 14:52:52 +02:00
155 lines
5.6 KiB
Java
155 lines
5.6 KiB
Java
/**
|
|
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
|
* Copyright (C) 2012 Kristian S. Stangeland
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
|
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
|
* the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
* See the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with this program;
|
|
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
|
* 02111-1307 USA
|
|
*/
|
|
package com.comphenix.protocol.reflect.cloning;
|
|
|
|
import java.lang.reflect.Constructor;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Map.Entry;
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
import java.util.function.Function;
|
|
import java.util.function.Supplier;
|
|
|
|
import com.comphenix.protocol.reflect.EquivalentConverter;
|
|
import com.comphenix.protocol.reflect.StructureModifier;
|
|
import com.comphenix.protocol.utility.MinecraftReflection;
|
|
import com.comphenix.protocol.wrappers.*;
|
|
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
|
|
|
|
import net.md_5.bungee.api.chat.BaseComponent;
|
|
|
|
/**
|
|
* Represents an object that can clone a specific list of Bukkit- and Minecraft-related objects.
|
|
*
|
|
* @author Kristian
|
|
*/
|
|
public class BukkitCloner implements Cloner {
|
|
private static final Map<Class<?>, Function<Object, Object>> CLONERS = new ConcurrentHashMap<>();
|
|
|
|
private static void fromWrapper(Supplier<Class<?>> getClass, Function<Object, ClonableWrapper> fromHandle) {
|
|
try {
|
|
Class<?> nmsClass = getClass.get();
|
|
if (nmsClass != null) {
|
|
CLONERS.put(nmsClass, nmsObject -> fromHandle.apply(nmsObject).deepClone().getHandle());
|
|
}
|
|
} catch (Throwable ignored) { }
|
|
}
|
|
|
|
@SuppressWarnings({"unchecked", "rawtypes"})
|
|
private static void fromConverter(Supplier<Class<?>> getClass, EquivalentConverter converter) {
|
|
try {
|
|
Class<?> nmsClass = getClass.get();
|
|
if (nmsClass != null) {
|
|
CLONERS.put(nmsClass, nmsObject -> converter.getGeneric(converter.getSpecific(nmsObject)));
|
|
}
|
|
} catch (Throwable ignored) { }
|
|
}
|
|
|
|
private static void fromManual(Supplier<Class<?>> getClass, Function<Object, Object> cloner) {
|
|
try {
|
|
Class<?> nmsClass = getClass.get();
|
|
if (nmsClass != null) {
|
|
CLONERS.put(nmsClass, cloner);
|
|
}
|
|
} catch (Throwable ignored) { }
|
|
}
|
|
|
|
static {
|
|
fromManual(MinecraftReflection::getItemStackClass, source ->
|
|
MinecraftReflection.getMinecraftItemStack(MinecraftReflection.getBukkitItemStack(source).clone()));
|
|
fromWrapper(MinecraftReflection::getDataWatcherClass, WrappedDataWatcher::new);
|
|
fromConverter(MinecraftReflection::getBlockPositionClass, BlockPosition.getConverter());
|
|
fromWrapper(MinecraftReflection::getServerPingClass, WrappedServerPing::fromHandle);
|
|
fromConverter(MinecraftReflection::getMinecraftKeyClass, MinecraftKey.getConverter());
|
|
fromWrapper(MinecraftReflection::getIBlockDataClass, WrappedBlockData::fromHandle);
|
|
fromManual(MinecraftReflection::getNonNullListClass, source -> nonNullListCloner().clone(source));
|
|
fromWrapper(MinecraftReflection::getNBTBaseClass, NbtFactory::fromNMS);
|
|
fromWrapper(MinecraftReflection::getIChatBaseComponentClass, WrappedChatComponent::fromHandle);
|
|
fromWrapper(WrappedVillagerData::getNmsClass, WrappedVillagerData::fromHandle);
|
|
fromConverter(MinecraftReflection::getSectionPositionClass, BukkitConverters.getSectionPositionConverter());
|
|
|
|
try {
|
|
fromManual(ComponentConverter::getBaseComponentArrayClass, source ->
|
|
ComponentConverter.clone((BaseComponent[]) source));
|
|
} catch (Throwable ignored) { }
|
|
|
|
try {
|
|
fromManual(AdventureComponentConverter::getComponentClass, AdventureComponentConverter::clone);
|
|
} catch (Throwable ignored) { }
|
|
}
|
|
|
|
private Function<Object, Object> findCloner(Class<?> type) {
|
|
for (Entry<Class<?>, Function<Object, Object>> entry : CLONERS.entrySet()) {
|
|
if (entry.getKey().isAssignableFrom(type)) {
|
|
return entry.getValue();
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public boolean canClone(Object source) {
|
|
if (source == null)
|
|
return false;
|
|
|
|
return findCloner(source.getClass()) != null;
|
|
}
|
|
|
|
@Override
|
|
public Object clone(Object source) {
|
|
if (source == null)
|
|
throw new IllegalArgumentException("source cannot be NULL.");
|
|
|
|
return findCloner(source.getClass()).apply(source);
|
|
}
|
|
|
|
private static Constructor<?> nonNullList = null;
|
|
|
|
private static Cloner nonNullListCloner() {
|
|
return new Cloner() {
|
|
@Override
|
|
public boolean canClone(Object source) {
|
|
return MinecraftReflection.is(MinecraftReflection.getNonNullListClass(), source);
|
|
}
|
|
|
|
@Override
|
|
public Object clone(Object source) {
|
|
StructureModifier<Object> modifier = new StructureModifier<>(source.getClass()).withTarget(source);
|
|
List<?> list = (List<?>) modifier.read(0);
|
|
Object empty = modifier.read(1);
|
|
|
|
if (nonNullList == null) {
|
|
try {
|
|
nonNullList = source.getClass().getDeclaredConstructor(List.class, Object.class);
|
|
nonNullList.setAccessible(true);
|
|
} catch (ReflectiveOperationException ex) {
|
|
throw new RuntimeException("Could not find NonNullList constructor", ex);
|
|
}
|
|
}
|
|
|
|
try {
|
|
return nonNullList.newInstance(new ArrayList<>(list), empty);
|
|
} catch (ReflectiveOperationException ex) {
|
|
throw new RuntimeException("Could not create new NonNullList", ex);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
}
|