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