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