package openmods.config.game; import com.google.common.base.Preconditions; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; import cpw.mods.fml.common.Loader; import cpw.mods.fml.common.ModContainer; import cpw.mods.fml.common.event.FMLMissingMappingsEvent.MissingMapping; import cpw.mods.fml.common.registry.GameRegistry; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.Collection; import java.util.Map; import java.util.Set; import net.minecraft.block.Block; import net.minecraft.item.Item; import net.minecraft.item.ItemBlock; import net.minecraft.tileentity.TileEntity; import openmods.Log; import openmods.config.BlockInstances; import openmods.config.InstanceContainer; import openmods.config.ItemInstances; import openmods.config.game.RegisterBlock.RegisterTileEntity; public class GameConfigProvider { private interface IAnnotationProcessor<I, A extends Annotation> { public void process(I entry, A annotation); public String getEntryName(A annotation); public boolean isEnabled(String name); } private static final AbstractFeatureManager NULL_FEATURE_MANAGER = new AbstractFeatureManager() { @Override public boolean isEnabled(String category, String name) { return true; } @Override public Set<String> getCategories() { return ImmutableSet.of(); } @Override public Set<String> getFeaturesInCategory(String category) { return ImmutableSet.of(); } }; private AbstractFeatureManager features = NULL_FEATURE_MANAGER; private boolean remapFromLegacy = true; private final FactoryRegistry<Block> blockFactory = new FactoryRegistry<Block>(); private final FactoryRegistry<Item> itemFactory = new FactoryRegistry<Item>(); private final Map<String, Item> itemRemaps = Maps.newHashMap(); private final Map<String, Block> blockRemaps = Maps.newHashMap(); private static class IdDecorator { private String modId; private final String joiner; public IdDecorator(String joiner) { this.joiner = joiner; } public void setMod(String modId) { this.modId = modId; } public String decorate(String id) { return modId + joiner + id; } } private final IdDecorator langDecorator = new IdDecorator("."); private final IdDecorator textureDecorator = new IdDecorator(":"); private final IdDecorator teDecorator = new IdDecorator("_"); private final IdDecorator legacyItemDecorator = new IdDecorator("."); private final IdDecorator legacyBlockDecorator = new IdDecorator("_"); private final String modId; public GameConfigProvider(String modPrefix) { langDecorator.setMod(modPrefix); textureDecorator.setMod(modPrefix); teDecorator.setMod(modPrefix); legacyBlockDecorator.setMod(modPrefix); legacyItemDecorator.setMod(modPrefix); ModContainer mod = Loader.instance().activeModContainer(); Preconditions.checkNotNull(mod, "This class can only be initialized in mod init"); this.modId = mod.getModId(); } public void setLanguageModId(String modId) { langDecorator.setMod(modId); } public void setTextureModId(String modId) { textureDecorator.setMod(modId); } public void setTileEntityModId(String modId) { teDecorator.setMod(modId); } public void setFeatures(AbstractFeatureManager features) { this.features = features; } public void setRemapFromLegacy(boolean remapFromLegacy) { this.remapFromLegacy = remapFromLegacy; } public FactoryRegistry<Block> getBlockFactory() { return blockFactory; } public FactoryRegistry<Item> getItemFactory() { return itemFactory; } private static <I, A extends Annotation> void processAnnotations(Class<? extends InstanceContainer<?>> config, Class<I> fieldClass, Class<A> annotationClass, FactoryRegistry<I> factory, IAnnotationProcessor<I, A> processor) { for (Field f : config.getFields()) { if (Modifier.isStatic(f.getModifiers()) && fieldClass.isAssignableFrom(f.getType())) { if (f.isAnnotationPresent(IgnoreFeature.class)) continue; A annotation = f.getAnnotation(annotationClass); if (annotation == null) { Log.warn("Field %s has valid type %s for registration, but no annotation %s", f, fieldClass, annotationClass); continue; } String name = processor.getEntryName(annotation); if (!processor.isEnabled(name)) { Log.info("Item %s (from field %s) is disabled", name, f); continue; } @SuppressWarnings("unchecked") Class<? extends I> fieldType = (Class<? extends I>)f.getType(); I entry = factory.construct(name, fieldType); if (entry == null) continue; try { f.set(null, entry); } catch (Exception e) { throw Throwables.propagate(e); } processor.process(entry, annotation); } } } private interface IdSetter { public void setId(String id); } private static void setPrefixedId(String id, String objectName, IdDecorator decorator, IdSetter setter, String noneValue, String defaultValue) { if (!id.equals(RegisterBlock.NONE)) { if (id.equals(RegisterBlock.DEFAULT)) id = decorator.decorate(objectName); else id = decorator.decorate(id); setter.setId(id); } } private static void setItemPrefixedId(String id, String itemName, IdDecorator decorator, IdSetter setter) { setPrefixedId(id, itemName, decorator, setter, RegisterItem.NONE, RegisterItem.DEFAULT); } public void registerItems(Class<? extends ItemInstances> klazz) { processAnnotations(klazz, Item.class, RegisterItem.class, itemFactory, new IAnnotationProcessor<Item, RegisterItem>() { @Override public void process(final Item item, RegisterItem annotation) { final String name = annotation.name(); final String legacyName = legacyItemDecorator.decorate(name); if (remapFromLegacy) { GameRegistry.registerItem(item, name); itemRemaps.put(modId + ":" + legacyName, item); } else { GameRegistry.registerItem(item, legacyName); } setItemPrefixedId(annotation.unlocalizedName(), name, langDecorator, new IdSetter() { @Override public void setId(String unlocalizedName) { item.setUnlocalizedName(unlocalizedName); } }); setItemPrefixedId(annotation.textureName(), name, textureDecorator, new IdSetter() { @Override public void setId(String textureName) { item.setTextureName(textureName); } }); } @Override public String getEntryName(RegisterItem annotation) { return annotation.name(); } @Override public boolean isEnabled(String name) { return features.isItemEnabled(name); } }); } private static void setBlockPrefixedId(String id, String blockName, IdDecorator decorator, IdSetter setter) { setPrefixedId(id, blockName, decorator, setter, RegisterBlock.NONE, RegisterBlock.DEFAULT); } public void registerBlocks(Class<? extends BlockInstances> klazz) { processAnnotations(klazz, Block.class, RegisterBlock.class, blockFactory, new IAnnotationProcessor<Block, RegisterBlock>() { @Override public void process(final Block block, RegisterBlock annotation) { final String name = annotation.name(); final Class<? extends ItemBlock> itemBlockClass = annotation.itemBlock(); Class<? extends TileEntity> teClass = annotation.tileEntity(); if (teClass == TileEntity.class) teClass = null; final String legacyName = legacyBlockDecorator.decorate(name); if (remapFromLegacy) { GameRegistry.registerBlock(block, itemBlockClass, name); blockRemaps.put(modId + ":" + legacyName, block); } else { GameRegistry.registerBlock(block, itemBlockClass, legacyName); } setBlockPrefixedId(annotation.unlocalizedName(), name, langDecorator, new IdSetter() { @Override public void setId(String unlocalizedName) { block.setBlockName(unlocalizedName); } }); setBlockPrefixedId(annotation.textureName(), name, textureDecorator, new IdSetter() { @Override public void setId(String textureName) { block.setBlockTextureName(textureName); } }); if (teClass != null) { final String teName = teDecorator.decorate(name); GameRegistry.registerTileEntity(teClass, teName); } if (block instanceof IRegisterableBlock) ((IRegisterableBlock)block).setupBlock(modId, name, teClass, itemBlockClass); for (RegisterTileEntity te : annotation.tileEntities()) { final String teName = teDecorator.decorate(te.name()); GameRegistry.registerTileEntity(te.cls(), teName); } } @Override public String getEntryName(RegisterBlock annotation) { return annotation.name(); } @Override public boolean isEnabled(String name) { return features.isBlockEnabled(name); } }); } public void handleRemaps(Collection<MissingMapping> mappings) { for (MissingMapping mapping : mappings) { switch (mapping.type) { case BLOCK: { Block remap = blockRemaps.get(mapping.name); if (remap != null) mapping.remap(remap); break; } case ITEM: { Item remap = itemRemaps.get(mapping.name); if (remap == null) { Block blockRemap = blockRemaps.get(mapping.name); if (blockRemap != null) remap = Item.getItemFromBlock(blockRemap); } if (remap != null) mapping.remap(remap); } default: break; } } } }