001    /*
002     * The FML Forge Mod Loader suite.
003     * Copyright (C) 2012 cpw
004     *
005     * This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or any later version.
007     *
008     * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
009     * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
010     *
011     * You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51
012     * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
013     */
014    package cpw.mods.fml.common;
015    
016    import java.util.EnumSet;
017    import java.util.List;
018    import java.util.Properties;
019    import java.util.logging.Level;
020    import java.util.logging.Logger;
021    
022    import net.minecraft.server.MinecraftServer;
023    import net.minecraft.src.CrashReport;
024    import net.minecraft.src.DedicatedServer;
025    import net.minecraft.src.Entity;
026    import net.minecraft.src.EntityPlayer;
027    import net.minecraft.src.EntityPlayerMP;
028    import net.minecraft.src.NetHandler;
029    import net.minecraft.src.Packet131MapData;
030    import net.minecraft.src.ServerListenThread;
031    import net.minecraft.src.ThreadServerApplication;
032    import net.minecraft.src.World;
033    
034    import com.google.common.base.Objects;
035    import com.google.common.base.Strings;
036    import com.google.common.base.Throwables;
037    import com.google.common.collect.ImmutableList;
038    import com.google.common.collect.ImmutableList.Builder;
039    import com.google.common.collect.Lists;
040    
041    import cpw.mods.fml.common.network.EntitySpawnAdjustmentPacket;
042    import cpw.mods.fml.common.network.EntitySpawnPacket;
043    import cpw.mods.fml.common.registry.EntityRegistry.EntityRegistration;
044    import cpw.mods.fml.common.registry.TickRegistry;
045    import cpw.mods.fml.server.FMLServerHandler;
046    
047    
048    /**
049     * The main class for non-obfuscated hook handling code
050     *
051     * Anything that doesn't require obfuscated or client/server specific code should
052     * go in this handler
053     *
054     * It also contains a reference to the sided handler instance that is valid
055     * allowing for common code to access specific properties from the obfuscated world
056     * without a direct dependency
057     *
058     * @author cpw
059     *
060     */
061    public class FMLCommonHandler
062    {
063        /**
064         * The singleton
065         */
066        private static final FMLCommonHandler INSTANCE = new FMLCommonHandler();
067        /**
068         * The delegate for side specific data and functions
069         */
070        private IFMLSidedHandler sidedDelegate;
071    
072        private List<IScheduledTickHandler> scheduledClientTicks = Lists.newArrayList();
073        private List<IScheduledTickHandler> scheduledServerTicks = Lists.newArrayList();
074        private Class<?> forge;
075        private boolean noForge;
076        private List<String> brandings;
077        private List<ICrashCallable> crashCallables = Lists.newArrayList(Loader.instance().getCallableCrashInformation());
078    
079    
080        public void beginLoading(IFMLSidedHandler handler)
081        {
082            sidedDelegate = handler;
083            FMLLog.info("Attempting early MinecraftForge initialization");
084            callForgeMethod("initialize");
085            callForgeMethod("registerCrashCallable");
086            FMLLog.info("Completed early MinecraftForge initialization");
087        }
088    
089        public void rescheduleTicks(Side side)
090        {
091            TickRegistry.updateTickQueue(side.isClient() ? scheduledClientTicks : scheduledServerTicks, side);
092        }
093        public void tickStart(EnumSet<TickType> ticks, Side side, Object ... data)
094        {
095            List<IScheduledTickHandler> scheduledTicks = side.isClient() ? scheduledClientTicks : scheduledServerTicks;
096    
097            if (scheduledTicks.size()==0)
098            {
099                return;
100            }
101            for (IScheduledTickHandler ticker : scheduledTicks)
102            {
103                EnumSet<TickType> ticksToRun = EnumSet.copyOf(Objects.firstNonNull(ticker.ticks(), EnumSet.noneOf(TickType.class)));
104                ticksToRun.removeAll(EnumSet.complementOf(ticks));
105                if (!ticksToRun.isEmpty())
106                {
107                    ticker.tickStart(ticksToRun, data);
108                }
109            }
110        }
111    
112        public void tickEnd(EnumSet<TickType> ticks, Side side, Object ... data)
113        {
114            List<IScheduledTickHandler> scheduledTicks = side.isClient() ? scheduledClientTicks : scheduledServerTicks;
115    
116            if (scheduledTicks.size()==0)
117            {
118                return;
119            }
120            for (IScheduledTickHandler ticker : scheduledTicks)
121            {
122                EnumSet<TickType> ticksToRun = EnumSet.copyOf(Objects.firstNonNull(ticker.ticks(), EnumSet.noneOf(TickType.class)));
123                ticksToRun.removeAll(EnumSet.complementOf(ticks));
124                if (!ticksToRun.isEmpty())
125                {
126                    ticker.tickEnd(ticksToRun, data);
127                }
128            }
129        }
130    
131        /**
132         * @return the instance
133         */
134        public static FMLCommonHandler instance()
135        {
136            return INSTANCE;
137        }
138        /**
139         * Find the container that associates with the supplied mod object
140         * @param mod
141         * @return
142         */
143        public ModContainer findContainerFor(Object mod)
144        {
145            return Loader.instance().getReversedModObjectList().get(mod);
146        }
147        /**
148         * Get the forge mod loader logging instance (goes to the forgemodloader log file)
149         * @return
150         */
151        public Logger getFMLLogger()
152        {
153            return FMLLog.getLogger();
154        }
155    
156        /**
157         * @param key
158         * @param lang
159         * @param value
160         */
161        /**
162         * @param languagePack
163         * @param lang
164         */
165    
166        public Side getSide()
167        {
168            return sidedDelegate.getSide();
169        }
170    
171        /**
172         * Return the effective side for the context in the game. This is dependent
173         * on thread analysis to try and determine whether the code is running in the
174         * server or not. Use at your own risk
175         *
176         * @return
177         */
178        public Side getEffectiveSide()
179        {
180            Thread thr = Thread.currentThread();
181            if ((thr instanceof ThreadServerApplication) || (thr instanceof ServerListenThread))
182            {
183                return Side.SERVER;
184            }
185    
186            return Side.CLIENT;
187        }
188        /**
189         * Raise an exception
190         *
191         * @param exception
192         * @param message
193         * @param stopGame
194         */
195        public void raiseException(Throwable exception, String message, boolean stopGame)
196        {
197            FMLCommonHandler.instance().getFMLLogger().throwing("FMLHandler", "raiseException", exception);
198            if (stopGame)
199            {
200                getSidedDelegate().haltGame(message,exception);
201            }
202        }
203    
204    
205        private Class<?> findMinecraftForge()
206        {
207            if (forge==null && !noForge)
208            {
209                try {
210                    forge = Class.forName("net.minecraftforge.common.MinecraftForge");
211                } catch (Exception ex) {
212                    noForge = true;
213                }
214            }
215            return forge;
216        }
217    
218        private Object callForgeMethod(String method)
219        {
220            if (noForge)
221                return null;
222            try
223            {
224                return findMinecraftForge().getMethod(method).invoke(null);
225            }
226            catch (Exception e)
227            {
228                // No Forge installation
229                return null;
230            }
231        }
232        /**
233         * @param string
234         * @return
235         */
236        public void computeBranding()
237        {
238            if (brandings == null)
239            {
240                Builder brd = ImmutableList.<String>builder();
241                brd.add(Loader.instance().getMCVersionString());
242                brd.add("FML v"+Loader.instance().getFMLVersionString());
243                String forgeBranding = (String) callForgeMethod("getBrandingVersion");
244                if (!Strings.isNullOrEmpty(forgeBranding))
245                {
246                    brd.add(forgeBranding);
247                }
248                brd.addAll(sidedDelegate.getAdditionalBrandingInformation());
249                try {
250                    Properties props=new Properties();
251                    props.load(getClass().getClassLoader().getResourceAsStream("fmlbranding.properties"));
252                    brd.add(props.getProperty("fmlbranding"));
253                } catch (Exception ex) {
254                    // Ignore - no branding file found
255                }
256                int tModCount = Loader.instance().getModList().size();
257                int aModCount = Loader.instance().getActiveModList().size();
258                brd.add(String.format("%d mod%s loaded, %d mod%s active", tModCount, tModCount!=1 ? "s" :"", aModCount, aModCount!=1 ? "s" :"" ));
259                brandings = brd.build();
260            }
261        }
262        public List<String> getBrandings()
263        {
264            if (brandings == null)
265            {
266                computeBranding();
267            }
268            return ImmutableList.copyOf(brandings);
269        }
270    
271        /**
272         * @return
273         */
274        public IFMLSidedHandler getSidedDelegate()
275        {
276            return sidedDelegate;
277        }
278    
279        public void onPostServerTick()
280        {
281            tickEnd(EnumSet.of(TickType.SERVER), Side.SERVER);
282        }
283    
284        /**
285         * Every tick just after world and other ticks occur
286         */
287        public void onPostWorldTick(Object world)
288        {
289            tickEnd(EnumSet.of(TickType.WORLD), Side.SERVER, world);
290        }
291    
292        public void onPreServerTick()
293        {
294            tickStart(EnumSet.of(TickType.SERVER), Side.SERVER);
295        }
296    
297        /**
298         * Every tick just before world and other ticks occur
299         */
300        public void onPreWorldTick(Object world)
301        {
302            tickStart(EnumSet.of(TickType.WORLD), Side.SERVER, world);
303        }
304    
305        public void onWorldLoadTick(World[] worlds)
306        {
307            rescheduleTicks(Side.SERVER);
308            for (World w : worlds)
309            {
310                tickStart(EnumSet.of(TickType.WORLDLOAD), Side.SERVER, w);
311            }
312        }
313    
314        public void handleServerStarting(MinecraftServer server)
315        {
316            Loader.instance().serverStarting(server);
317        }
318    
319        public void handleServerStarted()
320        {
321            Loader.instance().serverStarted();
322        }
323    
324        public void handleServerStopping()
325        {
326            Loader.instance().serverStopping();
327        }
328    
329        public MinecraftServer getMinecraftServerInstance()
330        {
331            return sidedDelegate.getServer();
332        }
333    
334        public void showGuiScreen(Object clientGuiElement)
335        {
336            sidedDelegate.showGuiScreen(clientGuiElement);
337        }
338    
339        public Entity spawnEntityIntoClientWorld(EntityRegistration registration, EntitySpawnPacket entitySpawnPacket)
340        {
341            return sidedDelegate.spawnEntityIntoClientWorld(registration, entitySpawnPacket);
342        }
343    
344        public void adjustEntityLocationOnClient(EntitySpawnAdjustmentPacket entitySpawnAdjustmentPacket)
345        {
346            sidedDelegate.adjustEntityLocationOnClient(entitySpawnAdjustmentPacket);
347        }
348    
349        public void onServerStart(DedicatedServer dedicatedServer)
350        {
351            FMLServerHandler.instance();
352            sidedDelegate.beginServerLoading(dedicatedServer);
353        }
354    
355        public void onServerStarted()
356        {
357            sidedDelegate.finishServerLoading();
358        }
359    
360    
361        public void onPreClientTick()
362        {
363            tickStart(EnumSet.of(TickType.CLIENT), Side.CLIENT);
364    
365        }
366    
367        public void onPostClientTick()
368        {
369            tickEnd(EnumSet.of(TickType.CLIENT), Side.CLIENT);
370        }
371    
372        public void onRenderTickStart(float timer)
373        {
374            tickStart(EnumSet.of(TickType.RENDER), Side.CLIENT, timer);
375        }
376    
377        public void onRenderTickEnd(float timer)
378        {
379            tickEnd(EnumSet.of(TickType.RENDER), Side.CLIENT, timer);
380        }
381    
382        public void onPlayerPreTick(EntityPlayer player)
383        {
384            Side side = player instanceof EntityPlayerMP ? Side.SERVER : Side.CLIENT;
385            tickStart(EnumSet.of(TickType.PLAYER), side, player);
386        }
387    
388        public void onPlayerPostTick(EntityPlayer player)
389        {
390            Side side = player instanceof EntityPlayerMP ? Side.SERVER : Side.CLIENT;
391            tickEnd(EnumSet.of(TickType.PLAYER), side, player);
392        }
393    
394        public void registerCrashCallable(ICrashCallable callable)
395        {
396            crashCallables.add(callable);
397        }
398    
399        public void enhanceCrashReport(CrashReport crashReport)
400        {
401            for (ICrashCallable call: crashCallables)
402            {
403                crashReport.addCrashSectionCallable(call.getLabel(), call);
404            }
405        }
406    
407        public void handleTinyPacket(NetHandler handler, Packet131MapData mapData)
408        {
409            sidedDelegate.handleTinyPacket(handler, mapData);
410        }
411    }