2016-06-23 03:49:47 +00:00
|
|
|
/*
|
|
|
|
* Minecraft Forge
|
2018-07-01 21:17:28 +00:00
|
|
|
* Copyright (c) 2016-2018.
|
2016-06-23 03:49:47 +00:00
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation version 2.1
|
|
|
|
* of the License.
|
|
|
|
*
|
|
|
|
* This library 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
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*/
|
|
|
|
|
2016-01-12 00:04:22 +00:00
|
|
|
package net.minecraftforge.common.capabilities;
|
|
|
|
|
|
|
|
import java.lang.reflect.Field;
|
|
|
|
import java.lang.reflect.Method;
|
|
|
|
import java.lang.reflect.Modifier;
|
2018-06-19 18:04:05 +00:00
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.Collection;
|
|
|
|
import java.util.Collections;
|
2016-01-12 00:04:22 +00:00
|
|
|
import java.util.IdentityHashMap;
|
|
|
|
import java.util.List;
|
2018-06-19 18:04:05 +00:00
|
|
|
import java.util.Objects;
|
2016-01-12 00:04:22 +00:00
|
|
|
import java.util.concurrent.Callable;
|
|
|
|
|
2018-06-19 18:04:05 +00:00
|
|
|
import net.minecraftforge.fml.language.ModFileScanData;
|
|
|
|
import org.apache.logging.log4j.LogManager;
|
|
|
|
import org.apache.logging.log4j.Logger;
|
2016-01-12 00:04:22 +00:00
|
|
|
import org.objectweb.asm.Type;
|
|
|
|
|
2017-06-25 01:08:20 +00:00
|
|
|
import java.util.function.Function;
|
2018-06-19 18:04:05 +00:00
|
|
|
import java.util.stream.Collectors;
|
2016-01-12 00:04:22 +00:00
|
|
|
|
|
|
|
import net.minecraftforge.common.util.EnumHelper;
|
2018-06-19 18:04:05 +00:00
|
|
|
|
|
|
|
import static net.minecraftforge.fml.Logging.CAPABILITIES;
|
2016-01-12 00:04:22 +00:00
|
|
|
|
|
|
|
public enum CapabilityManager
|
|
|
|
{
|
|
|
|
INSTANCE;
|
2018-08-27 17:10:07 +00:00
|
|
|
private static final Logger LOGGER = LogManager.getLogger();
|
2016-01-12 00:04:22 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Registers a capability to be consumed by others.
|
|
|
|
* APIs who define the capability should call this.
|
|
|
|
* To retrieve the Capability instance, use the @CapabilityInject annotation.
|
|
|
|
*
|
|
|
|
* @param type The Interface to be registered
|
|
|
|
* @param storage A default implementation of the storage handler.
|
2016-05-29 21:05:07 +00:00
|
|
|
* @param factory A Factory that will produce new instances of the default implementation.
|
2016-01-12 00:04:22 +00:00
|
|
|
*/
|
|
|
|
public <T> void register(Class<T> type, Capability.IStorage<T> storage, Callable<? extends T> factory)
|
|
|
|
{
|
2018-06-19 18:04:05 +00:00
|
|
|
Objects.requireNonNull(type,"Attempted to register a capability with invalid type");
|
|
|
|
Objects.requireNonNull(storage,"Attempted to register a capability with no storage implementation");
|
|
|
|
Objects.requireNonNull(factory,"Attempted to register a capability with no default implementation factory");
|
2016-01-12 00:04:22 +00:00
|
|
|
String realName = type.getName().intern();
|
2018-06-19 18:04:05 +00:00
|
|
|
if (providers.containsKey(realName)) {
|
|
|
|
LOGGER.error(CAPABILITIES, "Cannot register capability implementation multiple times : {}", realName);
|
|
|
|
throw new IllegalArgumentException("Cannot register a capability implementation multiple times : "+ realName);
|
|
|
|
}
|
2016-01-12 00:04:22 +00:00
|
|
|
|
2018-06-19 18:04:05 +00:00
|
|
|
Capability<T> cap = new Capability<>(realName, storage, factory);
|
2016-01-12 00:04:22 +00:00
|
|
|
providers.put(realName, cap);
|
|
|
|
|
2018-06-19 18:04:05 +00:00
|
|
|
callbacks.getOrDefault(realName, Collections.emptyList()).forEach(func -> func.apply(cap));
|
2016-01-12 00:04:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// INTERNAL
|
2018-06-19 18:04:05 +00:00
|
|
|
private IdentityHashMap<String, Capability<?>> providers = new IdentityHashMap<>();
|
|
|
|
private IdentityHashMap<String, List<Function<Capability<?>, Object>>> callbacks = new IdentityHashMap<>();
|
|
|
|
public void injectCapabilities(List<ModFileScanData> data)
|
|
|
|
{
|
|
|
|
final List<ModFileScanData.AnnotationData> capabilities = data.stream().map(ModFileScanData::getAnnotations).flatMap(Collection::stream).
|
|
|
|
filter(a -> Objects.equals(a.getClassType(), Type.getType(CapabilityInject.class))).
|
|
|
|
collect(Collectors.toList());
|
|
|
|
capabilities.forEach(this::attachCapabilityToMethod);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void attachCapabilityToMethod(ModFileScanData.AnnotationData entry)
|
2016-01-12 00:04:22 +00:00
|
|
|
{
|
2018-06-19 18:04:05 +00:00
|
|
|
final String targetClass = entry.getClassType().getClassName();
|
|
|
|
final String targetName = entry.getMemberName();
|
|
|
|
Type type = (Type)entry.getAnnotationData().get("value");
|
|
|
|
if (type == null)
|
2016-01-12 00:04:22 +00:00
|
|
|
{
|
2018-06-19 18:04:05 +00:00
|
|
|
LOGGER.warn(CAPABILITIES,"Unable to inject capability at {}.{} (Invalid Annotation)", targetClass, targetName);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
final String capabilityName = type.getInternalName().replace('/', '.').intern();
|
2016-01-12 00:04:22 +00:00
|
|
|
|
2018-06-19 18:04:05 +00:00
|
|
|
List<Function<Capability<?>, Object>> list = callbacks.computeIfAbsent(capabilityName, k -> new ArrayList<>());
|
2016-01-12 00:04:22 +00:00
|
|
|
|
2018-06-19 18:04:05 +00:00
|
|
|
if (entry.getMemberName().indexOf('(') > 0)
|
|
|
|
{
|
|
|
|
list.add(input -> {
|
|
|
|
try
|
2016-01-12 00:04:22 +00:00
|
|
|
{
|
2018-06-19 18:04:05 +00:00
|
|
|
for (Method mtd : Class.forName(targetClass).getDeclaredMethods())
|
2016-01-12 00:04:22 +00:00
|
|
|
{
|
2018-06-19 18:04:05 +00:00
|
|
|
if (targetName.equals(mtd.getName() + Type.getMethodDescriptor(mtd)))
|
2016-01-12 00:04:22 +00:00
|
|
|
{
|
2018-06-19 18:04:05 +00:00
|
|
|
if ((mtd.getModifiers() & Modifier.STATIC) != Modifier.STATIC)
|
2016-01-12 00:04:22 +00:00
|
|
|
{
|
2018-06-19 18:04:05 +00:00
|
|
|
LOGGER.warn(CAPABILITIES,"Unable to inject capability {} at {}.{} (Non-Static)", capabilityName, targetClass, targetName);
|
|
|
|
return null;
|
2016-01-12 00:04:22 +00:00
|
|
|
}
|
2018-06-19 18:04:05 +00:00
|
|
|
|
|
|
|
mtd.setAccessible(true);
|
|
|
|
mtd.invoke(null, input);
|
|
|
|
return null;
|
2016-01-12 00:04:22 +00:00
|
|
|
}
|
|
|
|
}
|
2018-06-19 18:04:05 +00:00
|
|
|
LOGGER.warn(CAPABILITIES,"Unable to inject capability {} at {}.{} (Method Not Found)", capabilityName, targetClass, targetName);
|
|
|
|
}
|
|
|
|
catch (Exception e)
|
2016-01-12 00:04:22 +00:00
|
|
|
{
|
2018-06-19 18:04:05 +00:00
|
|
|
LOGGER.warn(CAPABILITIES,"Unable to inject capability {} at {}.{}", capabilityName, targetClass, targetName, e);
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
list.add(input -> {
|
|
|
|
try
|
|
|
|
{
|
|
|
|
Field field = Class.forName(targetClass).getDeclaredField(targetName);
|
|
|
|
if ((field.getModifiers() & Modifier.STATIC) != Modifier.STATIC)
|
2016-01-12 00:04:22 +00:00
|
|
|
{
|
2018-06-19 18:04:05 +00:00
|
|
|
LOGGER.warn(CAPABILITIES,"Unable to inject capability {} at {}.{} (Non-Static)", capabilityName, targetClass, targetName);
|
2016-01-12 00:04:22 +00:00
|
|
|
return null;
|
|
|
|
}
|
2018-06-19 18:04:05 +00:00
|
|
|
// TODO Remove enumhelper here
|
|
|
|
EnumHelper.setFailsafeFieldValue(field, null, input);
|
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
LOGGER.warn(CAPABILITIES,"Unable to inject capability {} at {}.{}", capabilityName, targetClass, targetName, e);
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
});
|
2016-01-12 00:04:22 +00:00
|
|
|
}
|
|
|
|
}
|
2017-12-17 01:01:04 +00:00
|
|
|
}
|