    001    package cpw.mods.fml.common;
    002
    003    import java.lang.reflect.InvocationTargetException;
    004    import java.util.List;
    005    import java.util.Map;
    006    import java.util.Map.Entry;
    007    import java.util.logging.Level;
    008
    009    import com.google.common.base.Joiner;
    010    import com.google.common.collect.ArrayListMultimap;
    011    import com.google.common.collect.BiMap;
    012    import com.google.common.collect.ImmutableBiMap;
    013    import com.google.common.collect.ImmutableMap;
    014    import com.google.common.collect.ImmutableMap.Builder;
    015    import com.google.common.collect.Iterables;
    016    import com.google.common.collect.Lists;
    017    import com.google.common.collect.Multimap;
    018    import com.google.common.eventbus.EventBus;
    019    import com.google.common.eventbus.Subscribe;
    020
    021    import cpw.mods.fml.common.LoaderState.ModState;
    022    import cpw.mods.fml.common.event.FMLLoadEvent;
    023    import cpw.mods.fml.common.event.FMLPreInitializationEvent;
    024    import cpw.mods.fml.common.event.FMLStateEvent;
    025
    026    public class LoadController
    027    {
    028        private Loader loader;
    029        private EventBus masterChannel;
    030        private ImmutableMap<String,EventBus> eventChannels;
    031        private LoaderState state;
    032        private Multimap<String, ModState> modStates = ArrayListMultimap.create();
    033        private Multimap<String, Throwable> errors = ArrayListMultimap.create();
    034        private Map<String, ModContainer> modList;
    035        private List<ModContainer> activeModList = Lists.newArrayList();
    036        private ModContainer activeContainer;
    037        private BiMap<ModContainer, Object> modObjectList;
    038
    039        public LoadController(Loader loader)
    040        {
    041            this.loader = loader;
    042            this.masterChannel = new EventBus("FMLMainChannel");
    043            this.masterChannel.register(this);
    044
    045            state = LoaderState.NOINIT;
    046
    047
    048        }
    049
    050        @Subscribe
    051        public void buildModList(FMLLoadEvent event)
    052        {
    053            this.modList = loader.getIndexedModList();
    054            Builder<String, EventBus> eventBus = ImmutableMap.builder();
    055
    056            for (ModContainer mod : loader.getModList())
    057            {
    058                EventBus bus = new EventBus(mod.getModId());
    059                boolean isActive = mod.registerBus(bus, this);
    060                if (isActive)
    061                {
    062                    FMLLog.fine("Activating mod %s", mod.getModId());
    063                    activeModList.add(mod);
    064                    modStates.put(mod.getModId(), ModState.UNLOADED);
    065                    eventBus.put(mod.getModId(), bus);
    066                }
    067                else
    068                {
    069                    FMLLog.warning("Mod %s has been disabled through configuration", mod.getModId());
    070                    modStates.put(mod.getModId(), ModState.UNLOADED);
    071                    modStates.put(mod.getModId(), ModState.DISABLED);
    072                }
    073            }
    074
    075            eventChannels = eventBus.build();
    076        }
    077
    078        public void distributeStateMessage(LoaderState state, Object... eventData)
    079        {
    080            if (state.hasEvent())
    081            {
    082                masterChannel.post(state.getEvent(eventData));
    083            }
    084        }
    085
    086        public void transition(LoaderState desiredState)
    087        {
    088            LoaderState oldState = state;
    089            state = state.transition(!errors.isEmpty());
    090            if (state != desiredState)
    091            {
    092                Throwable toThrow = null;
    093                FMLLog.severe("Fatal errors were detected during the transition from %s to %s. Loading cannot continue", oldState, desiredState);
    094                StringBuilder sb = new StringBuilder();
    095                printModStates(sb);
    096                FMLLog.severe(sb.toString());
    097                FMLLog.severe("The following problems were captured during this phase");
    098                for (Entry<String, Throwable> error : errors.entries())
    099                {
    100                    FMLLog.log(Level.SEVERE, error.getValue(), "Caught exception from %s", error.getKey());
    101                    if (error.getValue() instanceof IFMLHandledException)
    102                    {
    103                        toThrow = error.getValue();
    104                    }
    105                    else if (toThrow == null)
    106                    {
    107                        toThrow = error.getValue();
    108                    }
    109                }
    110                if (toThrow != null && toThrow instanceof RuntimeException)
    111                {
    112                    throw (RuntimeException)toThrow;
    113                }
    114                else
    115                {
    116                    throw new LoaderException(toThrow);
    117                }
    118            }
    119        }
    120
    121        public ModContainer activeContainer()
    122        {
    123            return activeContainer;
    124        }
    125
    126        @Subscribe
    127        public void propogateStateMessage(FMLStateEvent stateEvent)
    128        {
    129            if (stateEvent instanceof FMLPreInitializationEvent)
    130            {
    131                modObjectList = buildModObjectList();
    132            }
    133            for (ModContainer mc : activeModList)
    134            {
    135                activeContainer = mc;
    136                String modId = mc.getModId();
    137                stateEvent.applyModContainer(activeContainer());
    138                FMLLog.finer("Posting state event %s to mod %s", stateEvent.getEventType(), modId);
    139                eventChannels.get(modId).post(stateEvent);
    140                FMLLog.finer("State event %s delivered to mod %s", stateEvent.getEventType(), modId);
    141                activeContainer = null;
    142                if (!errors.containsKey(modId))
    143                {
    144                    modStates.put(modId, stateEvent.getModState());
    145                }
    146                else
    147                {
    148                    modStates.put(modId, ModState.ERRORED);
    149                }
    150            }
    151        }
    152
    153        public ImmutableBiMap<ModContainer, Object> buildModObjectList()
    154        {
    155            ImmutableBiMap.Builder<ModContainer, Object> builder = ImmutableBiMap.<ModContainer, Object>builder();
    156            for (ModContainer mc : activeModList)
    157            {
    158                if (!mc.isImmutable() && mc.getMod()!=null)
    159                {
    160                    builder.put(mc, mc.getMod());
    161                }
    162                if (mc.getMod()==null && !mc.isImmutable() && state!=LoaderState.CONSTRUCTING)
    163                {
    164                    FMLLog.severe("There is a severe problem with %s - it appears not to have constructed correctly", mc.getModId());
    165                    if (state != LoaderState.CONSTRUCTING)
    166                    {
    167                        this.errorOccurred(mc, new RuntimeException());
    168                    }
    169                }
    170            }
    171            return builder.build();
    172        }
    173
    174        public void errorOccurred(ModContainer modContainer, Throwable exception)
    175        {
    176            if (exception instanceof InvocationTargetException)
    177            {
    178                errors.put(modContainer.getModId(), ((InvocationTargetException)exception).getCause());
    179            }
    180            else
    181            {
    182                errors.put(modContainer.getModId(), exception);
    183            }
    184        }
    185
    186        public void printModStates(StringBuilder ret)
    187        {
    188            for (ModContainer mc : loader.getModList())
    189            {
    190                ret.append("\n\t").append(mc.getModId()).append(" [").append(mc.getName()).append("] (").append(mc.getSource().getName()).append(") ");
    191                Joiner.on("->"). appendTo(ret, modStates.get(mc.getModId()));
    192            }
    193        }
    194
    195        public List<ModContainer> getActiveModList()
    196        {
    197            return activeModList;
    198        }
    199
    200        public ModState getModState(ModContainer selectedMod)
    201        {
    202            return Iterables.getLast(modStates.get(selectedMod.getModId()), ModState.AVAILABLE);
    203        }
    204
    205        public void distributeStateMessage(Class<?> customEvent)
    206        {
    207            try
    208            {
    209                masterChannel.post(customEvent.newInstance());
    210            }
    211            catch (Exception e)
    212            {
    213                FMLLog.log(Level.SEVERE, e, "An unexpected exception");
    214                throw new LoaderException(e);
    215            }
    216        }
    217
    218        public BiMap<ModContainer, Object> getModObjectList()
    219        {
    220            if (modObjectList == null)
    221            {
    222                FMLLog.severe("Detected an attempt by a mod %s to perform game activity during mod construction. This is a serious programming error.", activeContainer);
    223                return buildModObjectList();
    224            }
    225            return ImmutableBiMap.copyOf(modObjectList);
    226        }
    227
    228        public boolean isInState(LoaderState state)
    229        {
    230            return this.state == state;
    231        }
    232    }