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(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 }