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 }