package net.minecraftforkage.internal;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.logging.log4j.Level;
import net.minecraftforkage.PackerDataUtils;
import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;
import cpw.mods.fml.common.FMLLog;
import cpw.mods.fml.common.Loader;
import cpw.mods.fml.common.ModContainer;
import cpw.mods.fml.relauncher.FMLLaunchHandler;
@SuppressWarnings("serial")
public class FieldInjection {
private static class Entry {
String className;
String fieldName;
String type;
Map<String, String> data = new HashMap<String, String>();
Field locateField() throws ReflectiveOperationException {
Field f = Class.forName(className).getDeclaredField(fieldName);
f.setAccessible(true);
return f;
}
void inject(Object value) throws ReflectiveOperationException {
Field f = locateField();
Object instance = null;
if(!Modifier.isStatic(f.getModifiers())) {
try {
instance = findInstance(f.getDeclaringClass());
} catch(Exception e) {
throw new RuntimeException(className+"."+fieldName+" is not static, and couldn't find an appropriate instance (for "+type+" injection)", e);
}
}
if(value == null) {
FMLLog.log(Level.ERROR, new IllegalArgumentException("injecting null?"), "Injecting null into field %s of type %s?", f.toString(), f.getType().toString());
return;
}
if(!f.getType().isAssignableFrom(value.getClass()))
throw new RuntimeException(className+"."+fieldName+" has type "+f.getType()+", not compatible with actual value type "+value.getClass().getName());
f.set(instance, value);
}
}
private static final List<Entry> entries;
static {
entries = PackerDataUtils.read("mcforkage-fields-to-inject.json", new TypeToken<List<Entry>>(){});
for(Entry e : entries) {
if(!e.type.equals("sided-proxy") && !e.type.equals("mod-instance") && !e.type.equals("mod-metadata"))
throw new RuntimeException("Unknown field injection type: " + e.type+" on "+e.className+"."+e.fieldName+". Extra data: " + e.data);
}
}
/**
* Injects all sided proxies. Called as soon as possible, and only once.
*/
public static void injectSidedProxies() {
for(Entry e : entries) {
if(e.type.equals("sided-proxy")) {
String className;
if(FMLLaunchHandler.side().isClient())
className = e.data.get("clientSideClass");
else
className = e.data.get("serverSideClass");
try {
Object value = Class.forName(className).getConstructor().newInstance();
e.inject(value);
} catch(Throwable ex) {
throw new RuntimeException("Failed to inject sided proxy, to "+e.className+"."+e.fieldName+", of type "+className, ex);
}
}
}
}
private static Object findInstance(Class<?> ofClass) throws Exception {
// Mod?
for(ModContainer container : Loader.instance().getActiveModList()) {
Object instance = container.getMod();
if(ofClass.isInstance(instance))
return instance;
}
// Scala singleton?
Class<?> instanceSource = (ofClass.getName().endsWith("$") ? ofClass : Class.forName(ofClass.getName()+"$"));
return instanceSource.getField("MODULE$").get(null);
}
/**
* Injects all mod instances and metadata objects. Called after mod objects are constructed (and only once).
*/
public static void injectModInstancesAndMetadata() {
for(Entry e : entries) {
if(e.type.equals("mod-instance")) {
String mod = e.data.get("mod");
if(Loader.isModLoaded(mod))
try {
e.inject(Loader.instance().getIndexedModList().get(mod).getMod());
} catch(Throwable ex) {
throw new RuntimeException("Failed to inject mod instance, to "+e.className+"."+e.fieldName+", of mod "+mod, ex);
}
}
if(e.type.equals("mod-metadata")) {
String mod = e.data.get("mod");
if(Loader.isModLoaded(mod))
try {
e.inject(Loader.instance().getIndexedModList().get(mod).getMetadata());
} catch(Throwable ex) {
throw new RuntimeException("Failed to inject mod metadata, to "+e.className+"."+e.fieldName+", of mod "+mod, ex);
}
}
}
}
}