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         */
142        public ModContainer findContainerFor(Object mod)
143        {
144            return Loader.instance().getReversedModObjectList().get(mod);
145        }
146        /**
147         * Get the forge mod loader logging instance (goes to the forgemodloader log file)
148         * @return
149         */
150        public Logger getFMLLogger()
151        {
152            return FMLLog.getLogger();
153        }
154    
155        public Side getSide()
156        {
157            return sidedDelegate.getSide();
158        }
159    
160        /**
161         * Return the effective side for the context in the game. This is dependent
162         * on thread analysis to try and determine whether the code is running in the
163         * server or not. Use at your own risk
164         */
165        public Side getEffectiveSide()
166        {
167            Thread thr = Thread.currentThread();
168            if ((thr instanceof ThreadServerApplication) || (thr instanceof ServerListenThread))
169            {
170                return Side.SERVER;
171            }
172    
173            return Side.CLIENT;
174        }
175        /**
176         * Raise an exception
177         */
178        public void raiseException(Throwable exception, String message, boolean stopGame)
179        {
180            FMLCommonHandler.instance().getFMLLogger().throwing("FMLHandler", "raiseException", exception);
181            if (stopGame)
182            {
183                getSidedDelegate().haltGame(message,exception);
184            }
185        }
186    
187    
188        private Class<?> findMinecraftForge()
189        {
190            if (forge==null && !noForge)
191            {
192                try {
193                    forge = Class.forName("net.minecraftforge.common.MinecraftForge");
194                } catch (Exception ex) {
195                    noForge = true;
196                }
197            }
198            return forge;
199        }
200    
201        private Object callForgeMethod(String method)
202        {
203            if (noForge)
204                return null;
205            try
206            {
207                return findMinecraftForge().getMethod(method).invoke(null);
208            }
209            catch (Exception e)
210            {
211                // No Forge installation
212                return null;
213            }
214        }
215    
216        public void computeBranding()
217        {
218            if (brandings == null)
219            {
220                Builder brd = ImmutableList.<String>builder();
221                brd.add(Loader.instance().getMCVersionString());
222                brd.add("FML v"+Loader.instance().getFMLVersionString());
223                String forgeBranding = (String) callForgeMethod("getBrandingVersion");
224                if (!Strings.isNullOrEmpty(forgeBranding))
225                {
226                    brd.add(forgeBranding);
227                }
228                brd.addAll(sidedDelegate.getAdditionalBrandingInformation());
229                try {
230                    Properties props=new Properties();
231                    props.load(getClass().getClassLoader().getResourceAsStream("fmlbranding.properties"));
232                    brd.add(props.getProperty("fmlbranding"));
233                } catch (Exception ex) {
234                    // Ignore - no branding file found
235                }
236                int tModCount = Loader.instance().getModList().size();
237                int aModCount = Loader.instance().getActiveModList().size();
238                brd.add(String.format("%d mod%s loaded, %d mod%s active", tModCount, tModCount!=1 ? "s" :"", aModCount, aModCount!=1 ? "s" :"" ));
239                brandings = brd.build();
240            }
241        }
242        public List<String> getBrandings()
243        {
244            if (brandings == null)
245            {
246                computeBranding();
247            }
248            return ImmutableList.copyOf(brandings);
249        }
250    
251        public IFMLSidedHandler getSidedDelegate()
252        {
253            return sidedDelegate;
254        }
255    
256        public void onPostServerTick()
257        {
258            tickEnd(EnumSet.of(TickType.SERVER), Side.SERVER);
259        }
260    
261        /**
262         * Every tick just after world and other ticks occur
263         */
264        public void onPostWorldTick(Object world)
265        {
266            tickEnd(EnumSet.of(TickType.WORLD), Side.SERVER, world);
267        }
268    
269        public void onPreServerTick()
270        {
271            tickStart(EnumSet.of(TickType.SERVER), Side.SERVER);
272        }
273    
274        /**
275         * Every tick just before world and other ticks occur
276         */
277        public void onPreWorldTick(Object world)
278        {
279            tickStart(EnumSet.of(TickType.WORLD), Side.SERVER, world);
280        }
281    
282        public void onWorldLoadTick(World[] worlds)
283        {
284            rescheduleTicks(Side.SERVER);
285            for (World w : worlds)
286            {
287                tickStart(EnumSet.of(TickType.WORLDLOAD), Side.SERVER, w);
288            }
289        }
290    
291        public void handleServerStarting(MinecraftServer server)
292        {
293            Loader.instance().serverStarting(server);
294        }
295    
296        public void handleServerStarted()
297        {
298            Loader.instance().serverStarted();
299        }
300    
301        public void handleServerStopping()
302        {
303            Loader.instance().serverStopping();
304        }
305    
306        public MinecraftServer getMinecraftServerInstance()
307        {
308            return sidedDelegate.getServer();
309        }
310    
311        public void showGuiScreen(Object clientGuiElement)
312        {
313            sidedDelegate.showGuiScreen(clientGuiElement);
314        }
315    
316        public Entity spawnEntityIntoClientWorld(EntityRegistration registration, EntitySpawnPacket entitySpawnPacket)
317        {
318            return sidedDelegate.spawnEntityIntoClientWorld(registration, entitySpawnPacket);
319        }
320    
321        public void adjustEntityLocationOnClient(EntitySpawnAdjustmentPacket entitySpawnAdjustmentPacket)
322        {
323            sidedDelegate.adjustEntityLocationOnClient(entitySpawnAdjustmentPacket);
324        }
325    
326        public void onServerStart(DedicatedServer dedicatedServer)
327        {
328            FMLServerHandler.instance();
329            sidedDelegate.beginServerLoading(dedicatedServer);
330        }
331    
332        public void onServerStarted()
333        {
334            sidedDelegate.finishServerLoading();
335        }
336    
337    
338        public void onPreClientTick()
339        {
340            tickStart(EnumSet.of(TickType.CLIENT), Side.CLIENT);
341    
342        }
343    
344        public void onPostClientTick()
345        {
346            tickEnd(EnumSet.of(TickType.CLIENT), Side.CLIENT);
347        }
348    
349        public void onRenderTickStart(float timer)
350        {
351            tickStart(EnumSet.of(TickType.RENDER), Side.CLIENT, timer);
352        }
353    
354        public void onRenderTickEnd(float timer)
355        {
356            tickEnd(EnumSet.of(TickType.RENDER), Side.CLIENT, timer);
357        }
358    
359        public void onPlayerPreTick(EntityPlayer player)
360        {
361            Side side = player instanceof EntityPlayerMP ? Side.SERVER : Side.CLIENT;
362            tickStart(EnumSet.of(TickType.PLAYER), side, player);
363        }
364    
365        public void onPlayerPostTick(EntityPlayer player)
366        {
367            Side side = player instanceof EntityPlayerMP ? Side.SERVER : Side.CLIENT;
368            tickEnd(EnumSet.of(TickType.PLAYER), side, player);
369        }
370    
371        public void registerCrashCallable(ICrashCallable callable)
372        {
373            crashCallables.add(callable);
374        }
375    
376        public void enhanceCrashReport(CrashReport crashReport)
377        {
378            for (ICrashCallable call: crashCallables)
379            {
380                crashReport.addCrashSectionCallable(call.getLabel(), call);
381            }
382        }
383    
384        public void handleTinyPacket(NetHandler handler, Packet131MapData mapData)
385        {
386            sidedDelegate.handleTinyPacket(handler, mapData);
387        }
388    }