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 }