PageRenderTime 74ms CodeModel.GetById 29ms app.highlight 40ms RepoModel.GetById 0ms app.codeStats 0ms

/src/minecraft/com/mumfrey/liteloader/core/LiteLoader.java

https://bitbucket.org/codelore/xclient
Java | 1184 lines | 977 code | 207 blank | 0 comment | 193 complexity | 000888f7af5f40679a59e9784736fafa MD5 | raw file
   1 package com.mumfrey.liteloader.core;
   2 
   3 import com.mumfrey.liteloader.*;
   4import com.mumfrey.liteloader.gui.GuiControlsPaginated;
   5import com.mumfrey.liteloader.util.ModUtilities;
   6import com.mumfrey.liteloader.util.PrivateFields;
   7import net.minecraft.client.Minecraft;
   8import net.minecraft.client.gui.GuiControls;
   9import net.minecraft.client.gui.GuiNewChat;
  10import net.minecraft.client.gui.GuiScreen;
  11import net.minecraft.client.gui.ScaledResolution;
  12import net.minecraft.logging.ILogAgent;
  13import net.minecraft.network.packet.NetHandler;
  14import net.minecraft.network.packet.Packet1Login;
  15import net.minecraft.network.packet.Packet3Chat;
  16import net.minecraft.profiler.IPlayerUsage;
  17import net.minecraft.profiler.PlayerUsageSnooper;
  18import net.minecraft.profiler.Profiler;
  19import net.minecraft.util.Timer;
  20
  21import javax.activity.InvalidActivityException;
  22import java.io.*;
  23import java.lang.reflect.Method;
  24import java.net.URL;
  25import java.net.URLClassLoader;
  26import java.net.URLDecoder;
  27import java.nio.charset.Charset;
  28import java.util.*;
  29import java.util.logging.*;
  30import java.util.logging.Formatter;
  31import java.util.zip.ZipEntry;
  32import java.util.zip.ZipFile;
  33import java.util.zip.ZipInputStream;
  34 
  35 public final class LiteLoader
  36   implements FilenameFilter, IPlayerUsage
  37 {
  38   private static final String LOADER_VERSION = "1.5.2";
  39   private static final int LOADER_REVISION = 9;
  40   private static final String[] SUPPORTED_VERSIONS = { "1.5.2", "1.5.r1" };
  41   private static final int MAX_DISCOVERY_DEPTH = 16;
  42   private static LiteLoader instance;
  43   public static Logger logger = Logger.getLogger("liteloader");
  44 
  45   private static boolean useStdOut = false;
  46   private File modsFolder;
  47   private Minecraft minecraft = Minecraft.getMinecraft();
  48 
  49   private File propertiesFile = new File(Minecraft.getMinecraftDir(), "liteloader.properties");
  50 
  51   private Properties internalProperties = new Properties();
  52 
  53   private Properties localProperties = new Properties();
  54 
  55   private String branding = null;
  56 
  57   private boolean paginateControls = true;
  58   private Timer minecraftTimer;
  59   private String loadedModsList = "none";
  60 
  61   private Collection<LiteMod> mods = new LinkedList();
  62 
  63   private LinkedList tickListeners = new LinkedList();
  64 
  65   private LinkedList loopListeners = new LinkedList();
  66 
  67   private LinkedList initListeners = new LinkedList();
  68 
  69   private LinkedList renderListeners = new LinkedList();
  70 
  71   private LinkedList postRenderListeners = new LinkedList();
  72 
  73   private LinkedList chatRenderListeners = new LinkedList();
  74 
  75   private LinkedList chatListeners = new LinkedList();
  76 
  77   private LinkedList chatFilters = new LinkedList();
  78 
  79   private LinkedList loginListeners = new LinkedList();
  80 
  81   private LinkedList preLoginListeners = new LinkedList();
  82 
  83   private LinkedList pluginChannelListeners = new LinkedList();
  84 
  85   private HashMap pluginChannels = new HashMap();
  86   private Method mAddUrl;
  87   private boolean loaderStartupDone;
  88   private boolean loaderStartupComplete;
  89   private boolean lateInitDone;
  90   private boolean chatHooked;
  91   private boolean loginHooked;
  92   private boolean pluginChannelHooked;
  93   private boolean tickHooked;
  94   private HookProfiler profilerHook = new HookProfiler(this, logger);
  95   private ScaledResolution currentResolution;
  96 
  97   public static final LiteLoader getInstance()
  98   {
  99     if (instance == null)
 100     {
 101       instance = new LiteLoader();
 102       instance.initLoader();
 103     }
 104 
 105     return instance;
 106   }
 107 
 108   public static final Logger getLogger()
 109   {
 110     return logger;
 111   }
 112 
 113   public static final PrintStream getConsoleStream()
 114   {
 115     return useStdOut ? System.out : System.err;
 116   }
 117 
 118   public static final String getVersion()
 119   {
 120     return "1.5.2";
 121   }
 122 
 123   public static final int getRevision()
 124   {
 125     return 9;
 126   }
 127 
 128   private void initLoader()
 129   {
 130     if (this.loaderStartupDone) return;
 131     this.loaderStartupDone = true;
 132 
 133     prepareClassOverrides();
 134 
 135     if (prepareLoader())
 136     {
 137       logger.info(String.format("LiteLoader %s starting up...", new Object[] { "1.5.2" }));
 138 
 139       if (this.branding != null)
 140       {
 141         logger.info(String.format("Active Pack: %s", new Object[] { this.branding }));
 142       }
 143 
 144       logger.info(String.format("Java reports OS=\"%s\"", new Object[] { System.getProperty("os.name").toLowerCase() }));
 145 
 146       boolean searchMods = this.localProperties.getProperty("search.mods", "true").equalsIgnoreCase("true");
 147       boolean searchProtectionDomain = this.localProperties.getProperty("search.jar", "true").equalsIgnoreCase("true");
 148       boolean searchClassPath = this.localProperties.getProperty("search.classpath", "true").equalsIgnoreCase("true");
 149 
 150       if ((!searchMods) && (!searchProtectionDomain) && (!searchClassPath))
 151       {
 152         logger.warning("Invalid configuration, no search locations defined. Enabling all search locations.");
 153 
 154         this.localProperties.setProperty("search.mods", "true");
 155         this.localProperties.setProperty("search.jar", "true");
 156         this.localProperties.setProperty("search.classpath", "true");
 157 
 158         searchMods = true;
 159         searchProtectionDomain = true;
 160         searchClassPath = true;
 161       }
 162 
 163       prepareMods(searchMods, searchProtectionDomain, searchClassPath);
 164 
 165       initMods();
 166 
 167       initHooks();
 168 
 169       this.loaderStartupComplete = true;
 170 
 171       writeProperties();
 172     }
 173   }
 174 
 175   private void prepareClassOverrides()
 176   {
 177     registerBaseClassOverride(ModUtilities.getObfuscatedFieldName("net.minecraft.src.CallableJVMFlags", "h", "h"), "h");
 178   }
 179 
 180   private void registerBaseClassOverride(String binaryClassName, String fileName)
 181   {
 182     try
 183     {
 184       Method mDefineClass = ClassLoader.class.getDeclaredMethod("defineClass", new Class[] { String.class, byte[].class, Integer.TYPE, Integer.TYPE });
 185       mDefineClass.setAccessible(true);
 186 
 187       InputStream resourceInputStream = LiteLoader.class.getResourceAsStream("/classes/" + fileName + ".bin");
 188 
 189       if (resourceInputStream != null)
 190       {
 191         ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
 192 
 193         for (int readBytes = resourceInputStream.read(); readBytes >= 0; readBytes = resourceInputStream.read())
 194         {
 195           outputStream.write(readBytes);
 196         }
 197 
 198         byte[] data = outputStream.toByteArray();
 199 
 200         outputStream.close();
 201         resourceInputStream.close();
 202 
 203         logger.info("Defining class override for " + binaryClassName);
 204         mDefineClass.invoke(Minecraft.class.getClassLoader(), new Object[] { binaryClassName, data, Integer.valueOf(0), Integer.valueOf(data.length) });
 205       }
 206       else
 207       {
 208         logger.info("Error defining class override for " + binaryClassName + ", file not found");
 209       }
 210     }
 211     catch (Throwable th)
 212     {
 213       logger.log(Level.WARNING, "Error defining class override for " + binaryClassName, th);
 214     }
 215   }
 216 
 217   private boolean prepareLoader()
 218   {
 219     try
 220     {
 221       this.mAddUrl = URLClassLoader.class.getDeclaredMethod("addURL", new Class[] { URL.class });
 222       this.mAddUrl.setAccessible(true);
 223 
 224       prepareProperties();
 225 
 226       prepareLogger();
 227 
 228       this.paginateControls = this.localProperties.getProperty("controls.pages", "true").equalsIgnoreCase("true");
 229       this.localProperties.setProperty("controls.pages", String.valueOf(this.paginateControls));
 230 
 231       this.branding = this.internalProperties.getProperty("brand", null);
 232       if ((this.branding != null) && (this.branding.length() < 1)) this.branding = null;
 233 
 234       if (this.branding != null)
 235         this.localProperties.setProperty("brand", this.branding);
 236       else {
 237         this.localProperties.remove("brand");
 238       }
 239     }
 240     catch (Throwable th)
 241     {
 242       logger.log(Level.SEVERE, "Error initialising LiteLoader", th);
 243       return false;
 244     }
 245 
 246     return true;
 247   }
 248 
 249   private void prepareLogger()
 250     throws SecurityException, IOException
 251   {
 252     Formatter logFormatter = new LiteLoaderLogFormatter();
 253 
 254     logger.setUseParentHandlers(false);
 255     useStdOut = (System.getProperty("liteloader.log", "stderr").equalsIgnoreCase("stdout")) || (this.localProperties.getProperty("log", "stderr").equalsIgnoreCase("stdout"));
 256 
 257     StreamHandler consoleHandler = useStdOut ? new com.mumfrey.liteloader.util.log.ConsoleHandler() : new java.util.logging.ConsoleHandler();
 258     consoleHandler.setFormatter(logFormatter);
 259     logger.addHandler(consoleHandler);
 260 
 261     FileHandler logFileHandler = new FileHandler(new File(Minecraft.getMinecraftDir(), "LiteLoader.txt").getAbsolutePath());
 262     logFileHandler.setFormatter(logFormatter);
 263     logger.addHandler(logFileHandler);
 264   }
 265 
 266   private void prepareProperties()
 267   {
 268     try
 269     {
 270       InputStream propertiesStream = LiteLoader.class.getResourceAsStream("/liteloader.properties");
 271 
 272       if (propertiesStream != null)
 273       {
 274         this.internalProperties.load(propertiesStream);
 275         propertiesStream.close();
 276       }
 277     }
 278     catch (Throwable th)
 279     {
 280       this.internalProperties = new Properties();
 281     }
 282 
 283     try
 284     {
 285       this.localProperties = new Properties(this.internalProperties);
 286       InputStream localPropertiesStream = getLocalPropertiesStream();
 287 
 288       if (localPropertiesStream != null)
 289       {
 290         this.localProperties.load(localPropertiesStream);
 291         localPropertiesStream.close();
 292       }
 293     }
 294     catch (Throwable th)
 295     {
 296       this.localProperties = new Properties(this.internalProperties);
 297     }
 298   }
 299 
 300   private InputStream getLocalPropertiesStream()
 301     throws FileNotFoundException
 302   {
 303     if (this.propertiesFile.exists())
 304     {
 305       return new FileInputStream(this.propertiesFile);
 306     }
 307 
 308     return LiteLoader.class.getResourceAsStream("/liteloader.properties");
 309   }
 310 
 311   private void writeProperties()
 312   {
 313     try
 314     {
 315       this.localProperties.store(new FileWriter(this.propertiesFile), String.format("Properties for LiteLoader %s", new Object[] { "1.5.2" }));
 316     }
 317     catch (Throwable th)
 318     {
 319       logger.log(Level.WARNING, "Error writing liteloader properties", th);
 320     }
 321   }
 322 
 323   public File getModsFolder()
 324   {
 325     if (this.modsFolder == null)
 326     {
 327       this.modsFolder = new File(Minecraft.getMinecraftDir(), "mods");
 328 
 329       if ((!this.modsFolder.exists()) || (!this.modsFolder.isDirectory()))
 330       {
 331         try
 332         {
 333           this.modsFolder.mkdirs();
 334         }
 335         catch (Exception ex) {
 336         }
 337       }
 338     }
 339     return this.modsFolder;
 340   }
 341 
 342   public String getLoadedModsList()
 343   {
 344     return this.loadedModsList;
 345   }
 346 
 347   public String getBranding()
 348   {
 349     return this.branding;
 350   }
 351 
 352   public LiteMod getMod(String modName)
 353     throws InvalidActivityException, IllegalArgumentException
 354   {
 355     if (!this.loaderStartupComplete)
 356     {
 357       throw new InvalidActivityException("Attempted to get a reference to a mod before loader startup is complete");
 358     }
 359 
 360     if (modName == null)
 361     {
 362       throw new IllegalArgumentException("Attempted to get a reference to a mod without specifying a mod name");
 363     }
 364 
 365     for (LiteMod mod : this.mods)
 366     {
 367       if ((modName.equalsIgnoreCase(mod.getName())) || (modName.equalsIgnoreCase(mod.getClass().getSimpleName()))) return mod;
 368     }
 369 
 370     return null;
 371   }
 372 
 373   private void prepareMods(boolean searchMods, boolean searchProtectionDomain, boolean searchClassPath)
 374   {
 375     LinkedList modFiles = new LinkedList();
 376 
 377     if (searchMods)
 378     {
 379       File modFolder = getModsFolder();
 380       if ((modFolder.exists()) && (modFolder.isDirectory()))
 381       {
 382         logger.info("Mods folder found, searching " + modFolder.getPath());
 383         findModFiles(modFolder, modFiles);
 384         logger.info("Found " + modFiles.size() + " mod file(s)");
 385       }
 386 
 387     }
 388 
 389     HashMap modsToLoad = null;
 390     try
 391     {
 392       logger.info("Enumerating class path...");
 393 
 394       String classPath = System.getProperty("java.class.path");
 395       String classPathSeparator = System.getProperty("path.separator");
 396       String[] classPathEntries = classPath.split(classPathSeparator);
 397 
 398       logger.info(String.format("Class path separator=\"%s\"", new Object[] { classPathSeparator }));
 399       logger.info(String.format("Class path entries=(\n   classpathEntry=%s\n)", new Object[] { classPath.replace(classPathSeparator, "\n   classpathEntry=") }));
 400 
 401       if ((searchProtectionDomain) || (searchClassPath)) logger.info("Discovering mods on class path...");
 402       modsToLoad = findModClasses(classPathEntries, modFiles, searchProtectionDomain, searchClassPath);
 403 
 404       logger.info("Mod class discovery completed");
 405     }
 406     catch (Throwable th)
 407     {
 408       logger.log(Level.WARNING, "Mod class discovery failed", th);
 409       return;
 410     }
 411 
 412     loadMods(modsToLoad);
 413   }
 414 
 415   protected void findModFiles(File modFolder, LinkedList modFiles)
 416   {
 417     List supportedVerions = Arrays.asList(SUPPORTED_VERSIONS);
 418 
 419     for (File modFile : modFolder.listFiles(this))
 420     {
 421       try
 422       {
 423         ZipFile modZip = new ZipFile(modFile);
 424         ZipEntry version = modZip.getEntry("version.txt");
 425 
 426         if (version != null)
 427         {
 428           InputStream versionStream = modZip.getInputStream(version);
 429           BufferedReader versionReader = new BufferedReader(new InputStreamReader(versionStream));
 430           String strVersion = versionReader.readLine();
 431           versionReader.close();
 432 
 433           if ((supportedVerions.contains(strVersion)) && (addURLToClassPath(modFile.toURI().toURL())))
 434           {
 435             modFiles.add(modFile);
 436           }
 437           else
 438           {
 439             logger.info("Not adding invalid or outdated mod file: " + modFile.getAbsolutePath());
 440           }
 441         }
 442 
 443         modZip.close();
 444       }
 445       catch (Exception ex)
 446       {
 447         logger.warning("Error enumerating '" + modFile.getAbsolutePath() + "': Invalid zip file or error reading file");
 448       }
 449     }
 450   }
 451 
 452   public boolean accept(File dir, String fileName)
 453   {
 454     return fileName.toLowerCase().endsWith(".litemod");
 455   }
 456 
 457   private HashMap findModClasses(String[] classPathEntries, LinkedList modFiles, boolean searchProtectionDomain, boolean searchClassPath)
 458   {
 459     HashMap modsToLoad = new HashMap();
 460 
 461     if (searchProtectionDomain)
 462     {
 463       try
 464       {
 465         logger.info("Searching protection domain code source...");
 466 
 467         File packagePath = null;
 468 
 469         URL protectionDomainLocation = LiteLoader.class.getProtectionDomain().getCodeSource().getLocation();
 470         if (protectionDomainLocation != null)
 471         {
 472           if ((protectionDomainLocation.toString().indexOf('!') > -1) && (protectionDomainLocation.toString().startsWith("jar:")))
 473           {
 474             protectionDomainLocation = new URL(protectionDomainLocation.toString().substring(4, protectionDomainLocation.toString().indexOf('!')));
 475           }
 476 
 477           packagePath = new File(protectionDomainLocation.toURI());
 478         }
 479         else
 480         {
 481           String reflectionClassPath = LiteLoader.class.getResource("/com/mumfrey/liteloader/core/LiteLoader.class").getPath();
 482 
 483           if (reflectionClassPath.indexOf('!') > -1)
 484           {
 485             reflectionClassPath = URLDecoder.decode(reflectionClassPath, "UTF-8");
 486             packagePath = new File(reflectionClassPath.substring(5, reflectionClassPath.indexOf('!')));
 487           }
 488         }
 489 
 490         if (packagePath != null)
 491         {
 492           LinkedList modClasses = getSubclassesFor(packagePath, Minecraft.class.getClassLoader(), LiteMod.class, "LiteMod");
 493 
 494           for (Class mod : (Collection<Class>)modClasses)
 495           {
 496             if (modsToLoad.containsKey(mod.getSimpleName()))
 497             {
 498               logger.warning("Mod name collision for mod with class '" + mod.getSimpleName() + "', maybe you have more than one copy?");
 499             }
 500 
 501             modsToLoad.put(mod.getSimpleName(), mod);
 502           }
 503 
 504           if (modClasses.size() > 0) logger.info(String.format("Found %s potential matches", new Object[] { Integer.valueOf(modClasses.size()) }));
 505         }
 506       }
 507       catch (Throwable th)
 508       {
 509         logger.warning("Error loading from local class path: " + th.getMessage());
 510       }
 511     }
 512 
 513     if (searchClassPath)
 514     {
 515       for (String classPathPart : classPathEntries)
 516       {
 517         logger.info(String.format("Searching %s...", new Object[] { classPathPart }));
 518 
 519         File packagePath = new File(classPathPart);
 520         LinkedList modClasses = getSubclassesFor(packagePath, Minecraft.class.getClassLoader(), LiteMod.class, "LiteMod");
 521 
 522         for (Class mod : (Collection<Class>)modClasses)
 523         {
 524           if (modsToLoad.containsKey(mod.getSimpleName()))
 525           {
 526             logger.warning("Mod name collision for mod with class '" + mod.getSimpleName() + "', maybe you have more than one copy?");
 527           }
 528 
 529           modsToLoad.put(mod.getSimpleName(), mod);
 530         }
 531 
 532         if (modClasses.size() <= 0) continue; logger.info(String.format("Found %s potential matches", new Object[] { Integer.valueOf(modClasses.size()) }));
 533       }
 534 
 535     }
 536 
 537     for (File modFile : (Collection<File>)modFiles)
 538     {
 539       logger.info(String.format("Searching %s...", new Object[] { modFile.getAbsolutePath() }));
 540 
 541       LinkedList modClasses = getSubclassesFor(modFile, Minecraft.class.getClassLoader(), LiteMod.class, "LiteMod");
 542 
 543       for (Class mod : (Collection<Class>)modClasses)
 544       {
 545         if (modsToLoad.containsKey(mod.getSimpleName()))
 546         {
 547           logger.warning("Mod name collision for mod with class '" + mod.getSimpleName() + "', maybe you have more than one copy?");
 548         }
 549 
 550         modsToLoad.put(mod.getSimpleName(), mod);
 551       }
 552 
 553       if (modClasses.size() > 0) logger.info(String.format("Found %s potential matches", new Object[] { Integer.valueOf(modClasses.size()) }));
 554     }
 555 
 556     return modsToLoad;
 557   }
 558 
 559   private void loadMods(HashMap modsToLoad)
 560   {
 561     if (modsToLoad == null)
 562     {
 563       logger.info("Mod class discovery failed. Not loading any mods!");
 564       return;
 565     }
 566 
 567     logger.info("Discovered " + modsToLoad.size() + " total mod(s)");
 568 
 569     for (Class mod : (Collection<Class>)modsToLoad.values())
 570     {
 571       try
 572       {
 573         logger.info("Loading mod from " + mod.getName());
 574 
 575         LiteMod newMod = (LiteMod)mod.newInstance();
 576         this.mods.add(newMod);
 577 
 578         logger.info("Successfully added mod " + newMod.getName() + " version " + newMod.getVersion());
 579       }
 580       catch (Throwable th)
 581       {
 582         logger.warning(th.toString());
 583         th.printStackTrace();
 584       }
 585     }
 586   }
 587 
 588   private void initMods()
 589   {
 590     this.loadedModsList = "";
 591     int loadedModsCount = 0;
 592 
 593     for (Iterator iter = this.mods.iterator(); iter.hasNext(); )
 594     {
 595       LiteMod mod = (LiteMod)iter.next();
 596       try
 597       {
 598         logger.info("Initialising mod " + mod.getName() + " version " + mod.getVersion());
 599 
 600         mod.init();
 601 
 602         if ((mod instanceof Tickable))
 603         {
 604           addTickListener((Tickable)mod);
 605         }
 606 
 607         if ((mod instanceof GameLoopListener))
 608         {
 609           addLoopListener((GameLoopListener)mod);
 610         }
 611 
 612         if ((mod instanceof InitCompleteListener))
 613         {
 614           addInitListener((InitCompleteListener)mod);
 615         }
 616 
 617         if ((mod instanceof RenderListener))
 618         {
 619           addRenderListener((RenderListener)mod);
 620         }
 621 
 622         if ((mod instanceof PostRenderListener))
 623         {
 624           addPostRenderListener((PostRenderListener)mod);
 625         }
 626 
 627         if ((mod instanceof ChatFilter))
 628         {
 629           addChatFilter((ChatFilter)mod);
 630         }
 631 
 632         if ((mod instanceof ChatListener))
 633         {
 634           if ((mod instanceof ChatFilter))
 635           {
 636             logger.warning(String.format("Interface error initialising mod '%1s'. A mod implementing ChatFilter and ChatListener is not supported! Remove one of these interfaces", new Object[] { mod.getName() }));
 637           }
 638           else
 639           {
 640             addChatListener((ChatListener)mod);
 641           }
 642         }
 643 
 644         if ((mod instanceof ChatRenderListener))
 645         {
 646           addChatRenderListener((ChatRenderListener)mod);
 647         }
 648 
 649         if ((mod instanceof PreLoginListener))
 650         {
 651           addPreLoginListener((PreLoginListener)mod);
 652         }
 653 
 654         if ((mod instanceof LoginListener))
 655         {
 656           addLoginListener((LoginListener)mod);
 657         }
 658 
 659         if ((mod instanceof PluginChannelListener))
 660         {
 661           addPluginChannelListener((PluginChannelListener)mod);
 662         }
 663 
 664         this.loadedModsList += String.format("\n          - %s version %s", new Object[] { mod.getName(), mod.getVersion() });
 665         loadedModsCount++;
 666       }
 667       catch (Throwable th)
 668       {
 669         logger.log(Level.WARNING, "Error initialising mod '" + mod.getName(), th);
 670         iter.remove();
 671       }
 672     }
 673 
 674     this.loadedModsList = String.format("%s loaded mod(s)%s", new Object[] { Integer.valueOf(loadedModsCount), this.loadedModsList });
 675   }
 676 
 677   private void initHooks()
 678   {
 679     try
 680     {
 681       if (((this.chatListeners.size() > 0) || (this.chatFilters.size() > 0)) && (!this.chatHooked))
 682       {
 683         this.chatHooked = true;
 684         HookChat.register();
 685         HookChat.registerPacketHandler(this);
 686       }
 687 
 688       if (((this.preLoginListeners.size() > 0) || (this.loginListeners.size() > 0)) && (!this.loginHooked))
 689       {
 690         this.loginHooked = true;
 691         ModUtilities.registerPacketOverride(1, HookLogin.class);
 692         HookLogin.loader = this;
 693       }
 694 
 695       if ((this.pluginChannelListeners.size() > 0) && (!this.pluginChannelHooked))
 696       {
 697         this.pluginChannelHooked = true;
 698         HookPluginChannels.register();
 699         HookPluginChannels.registerPacketHandler(this);
 700       }
 701 
 702       if (!this.tickHooked)
 703       {
 704         this.tickHooked = true;
 705         PrivateFields.minecraftProfiler.setFinal(this.minecraft, this.profilerHook);
 706       }
 707 
 708       PlayerUsageSnooper snooper = this.minecraft.getPlayerUsageSnooper();
 709       PrivateFields.playerStatsCollector.setFinal(snooper, this);
 710     }
 711     catch (Exception ex)
 712     {
 713       logger.log(Level.WARNING, "Error creating hooks", ex);
 714       ex.printStackTrace();
 715     }
 716   }
 717 
 718   public void addTickListener(Tickable tickable)
 719   {
 720     if (!this.tickListeners.contains(tickable))
 721     {
 722       this.tickListeners.add(tickable);
 723       if (this.loaderStartupComplete) initHooks();
 724     }
 725   }
 726 
 727   public void addLoopListener(GameLoopListener loopListener)
 728   {
 729     if (!this.loopListeners.contains(loopListener))
 730     {
 731       this.loopListeners.add(loopListener);
 732       if (this.loaderStartupComplete) initHooks();
 733     }
 734   }
 735 
 736   public void addInitListener(InitCompleteListener initCompleteListener)
 737   {
 738     if (!this.initListeners.contains(initCompleteListener))
 739     {
 740       this.initListeners.add(initCompleteListener);
 741       if (this.loaderStartupComplete) initHooks();
 742     }
 743   }
 744 
 745   public void addRenderListener(RenderListener tickable)
 746   {
 747     if (!this.renderListeners.contains(tickable))
 748     {
 749       this.renderListeners.add(tickable);
 750       if (this.loaderStartupComplete) initHooks();
 751     }
 752   }
 753 
 754   public void addPostRenderListener(PostRenderListener tickable)
 755   {
 756     if (!this.postRenderListeners.contains(tickable))
 757     {
 758       this.postRenderListeners.add(tickable);
 759       if (this.loaderStartupComplete) initHooks();
 760     }
 761   }
 762 
 763   public void addChatFilter(ChatFilter chatFilter)
 764   {
 765     if (!this.chatFilters.contains(chatFilter))
 766     {
 767       this.chatFilters.add(chatFilter);
 768       if (this.loaderStartupComplete) initHooks();
 769     }
 770   }
 771 
 772   public void addChatListener(ChatListener chatListener)
 773   {
 774     if (!this.chatListeners.contains(chatListener))
 775     {
 776       this.chatListeners.add(chatListener);
 777       if (this.loaderStartupComplete) initHooks();
 778     }
 779   }
 780 
 781   public void addChatRenderListener(ChatRenderListener chatRenderListener)
 782   {
 783     if (!this.chatRenderListeners.contains(chatRenderListener))
 784     {
 785       this.chatRenderListeners.add(chatRenderListener);
 786       if (this.loaderStartupComplete) initHooks();
 787     }
 788   }
 789 
 790   public void addPreLoginListener(PreLoginListener loginListener)
 791   {
 792     if (!this.preLoginListeners.contains(loginListener))
 793     {
 794       this.preLoginListeners.add(loginListener);
 795       if (this.loaderStartupComplete) initHooks();
 796     }
 797   }
 798 
 799   public void addLoginListener(LoginListener loginListener)
 800   {
 801     if (!this.loginListeners.contains(loginListener))
 802     {
 803       this.loginListeners.add(loginListener);
 804       if (this.loaderStartupComplete) initHooks();
 805     }
 806   }
 807 
 808   public void addPluginChannelListener(PluginChannelListener pluginChannelListener)
 809   {
 810     if (!this.pluginChannelListeners.contains(pluginChannelListener))
 811     {
 812       this.pluginChannelListeners.add(pluginChannelListener);
 813       if (this.loaderStartupComplete) initHooks();
 814     }
 815   }
 816 
 817   private static LinkedList getSubclassesFor(File packagePath, ClassLoader classloader, Class superClass, String prefix)
 818   {
 819     LinkedList classes = new LinkedList();
 820     try
 821     {
 822       if (packagePath.isDirectory())
 823       {
 824         enumerateDirectory(prefix, superClass, classloader, classes, packagePath);
 825       }
 826       else if ((packagePath.isFile()) && ((packagePath.getName().endsWith(".jar")) || (packagePath.getName().endsWith(".zip")) || (packagePath.getName().endsWith(".litemod"))))
 827       {
 828         enumerateCompressedPackage(prefix, superClass, classloader, classes, packagePath);
 829       }
 830     }
 831     catch (Throwable th)
 832     {
 833       logger.log(Level.WARNING, "Enumeration error", th);
 834     }
 835 
 836     return classes;
 837   }
 838 
 839   private static void enumerateCompressedPackage(String prefix, Class superClass, ClassLoader classloader, LinkedList classes, File packagePath)
 840     throws IOException, FileNotFoundException
 841   {
 842     FileInputStream fileinputstream = new FileInputStream(packagePath);
 843     ZipInputStream zipinputstream = new ZipInputStream(fileinputstream);
 844 
 845     ZipEntry zipentry = null;
 846     do
 847     {
 848       zipentry = zipinputstream.getNextEntry();
 849 
 850       if ((zipentry == null) || (!zipentry.getName().endsWith(".class")))
 851         continue;
 852       String classFileName = zipentry.getName();
 853       String className = classFileName.lastIndexOf('/') > -1 ? classFileName.substring(classFileName.lastIndexOf('/') + 1) : classFileName;
 854 
 855       if ((prefix != null) && (!className.startsWith(prefix)))
 856         continue;
 857       try
 858       {
 859         String fullClassName = classFileName.substring(0, classFileName.length() - 6).replaceAll("/", ".");
 860         checkAndAddClass(classloader, superClass, classes, fullClassName);
 861       }
 862       catch (Exception ex)
 863       {
 864       }
 865     }
 866 
 867     while (zipentry != null);
 868 
 869     fileinputstream.close();
 870   }
 871 
 872   private static void enumerateDirectory(String prefix, Class superClass, ClassLoader classloader, LinkedList classes, File packagePath)
 873   {
 874     enumerateDirectory(prefix, superClass, classloader, classes, packagePath, "", 0);
 875   }
 876 
 877   private static void enumerateDirectory(String prefix, Class superClass, ClassLoader classloader, LinkedList classes, File packagePath, String packageName, int depth)
 878   {
 879     if (depth > 16) return;
 880 
 881     File[] classFiles = packagePath.listFiles();
 882 
 883     for (File classFile : classFiles)
 884     {
 885       if (classFile.isDirectory())
 886       {
 887         enumerateDirectory(prefix, superClass, classloader, classes, classFile, packageName + classFile.getName() + ".", depth + 1);
 888       }
 889       else
 890       {
 891         if ((!classFile.getName().endsWith(".class")) || ((prefix != null) && (!classFile.getName().startsWith(prefix))))
 892           continue;
 893         String classFileName = classFile.getName();
 894         String className = packageName + classFileName.substring(0, classFileName.length() - 6);
 895         checkAndAddClass(classloader, superClass, classes, className);
 896       }
 897     }
 898   }
 899 
 900   private static void checkAndAddClass(ClassLoader classloader, Class superClass, LinkedList classes, String className)
 901   {
 902     if (className.indexOf('$') > -1) {
 903       return;
 904     }
 905     try
 906     {
 907       Class subClass = classloader.loadClass(className);
 908 
 909       if ((subClass != null) && (!superClass.equals(subClass)) && (superClass.isAssignableFrom(subClass)) && (!subClass.isInterface()) && (!classes.contains(subClass)))
 910       {
 911         classes.add(subClass);
 912       }
 913     }
 914     catch (Throwable th)
 915     {
 916       logger.log(Level.WARNING, "checkAndAddClass error", th);
 917     }
 918   }
 919 
 920   private boolean addURLToClassPath(URL classUrl)
 921   {
 922     try
 923     {
 924       if (((Minecraft.class.getClassLoader() instanceof URLClassLoader)) && (this.mAddUrl != null) && (this.mAddUrl.isAccessible()))
 925       {
 926         URLClassLoader classLoader = (URLClassLoader)Minecraft.class.getClassLoader();
 927         this.mAddUrl.invoke(classLoader, new Object[] { classUrl });
 928         return true;
 929       }
 930     }
 931     catch (Throwable th)
 932     {
 933       logger.log(Level.WARNING, "Error adding class path entry", th);
 934     }
 935 
 936     return false;
 937   }
 938 
 939   public void onInit()
 940   {
 941     if (!this.lateInitDone)
 942     {
 943       this.lateInitDone = true;
 944 
 945       for (InitCompleteListener initMod : (Collection<InitCompleteListener>)this.initListeners)
 946       {
 947         try
 948         {
 949           logger.info("Calling late init for mod " + initMod.getName());
 950           initMod.onInitCompleted(this.minecraft, this);
 951         }
 952         catch (Throwable th)
 953         {
 954           logger.log(Level.WARNING, "Error initialising mod " + initMod.getName(), th);
 955         }
 956       }
 957     }
 958   }
 959 
 960   public void onRender()
 961   {
 962     if ((this.paginateControls) && (this.minecraft.currentScreen != null) && (this.minecraft.currentScreen.getClass().equals(GuiControls.class)))
 963     {
 964       try
 965       {
 966         GuiScreen parentScreen = (GuiScreen)PrivateFields.guiControlsParentScreen.get((GuiControls)this.minecraft.currentScreen);
 967         this.minecraft.displayGuiScreen(new GuiControlsPaginated(parentScreen, this.minecraft.gameSettings));
 968       }
 969       catch (Exception ex) {
 970       }
 971     }
 972     for (RenderListener renderListener : (Collection<RenderListener>)this.renderListeners)
 973       renderListener.onRender();
 974   }
 975 
 976   public void postRenderEntities()
 977   {
 978     float partialTicks = this.minecraftTimer != null ? this.minecraftTimer.elapsedPartialTicks : 0.0F;
 979 
 980     for (PostRenderListener renderListener : (Collection<PostRenderListener>)this.postRenderListeners)
 981       renderListener.onPostRenderEntities(partialTicks);
 982   }
 983 
 984   public void postRender()
 985   {
 986     float partialTicks = this.minecraftTimer != null ? this.minecraftTimer.elapsedPartialTicks : 0.0F;
 987 
 988     for (PostRenderListener renderListener : (Collection<PostRenderListener>)this.postRenderListeners)
 989       renderListener.onPostRender(partialTicks);
 990   }
 991 
 992   public void onBeforeGuiRender()
 993   {
 994     for (RenderListener renderListener : (Collection<RenderListener>)this.renderListeners)
 995       renderListener.onRenderGui(this.minecraft.currentScreen);
 996   }
 997 
 998   public void onSetupCameraTransform()
 999   {
1000     for (RenderListener renderListener : (Collection<RenderListener>)this.renderListeners)
1001       renderListener.onSetupCameraTransform();
1002   }
1003 
1004   public void onBeforeChatRender()
1005   {
1006     this.currentResolution = new ScaledResolution(this.minecraft.gameSettings, this.minecraft.displayWidth, this.minecraft.displayHeight);
1007     int screenWidth = this.currentResolution.getScaledWidth();
1008     int screenHeight = this.currentResolution.getScaledHeight();
1009 
1010     GuiNewChat chat = this.minecraft.ingameGUI.getChatGUI();
1011 
1012     for (ChatRenderListener chatRenderListener : (Collection<ChatRenderListener>)this.chatRenderListeners)
1013       chatRenderListener.onPreRenderChat(screenWidth, screenHeight, chat);
1014   }
1015 
1016   public void onAfterChatRender()
1017   {
1018     int screenWidth = this.currentResolution.getScaledWidth();
1019     int screenHeight = this.currentResolution.getScaledHeight();
1020 
1021     GuiNewChat chat = this.minecraft.ingameGUI.getChatGUI();
1022 
1023     for (ChatRenderListener chatRenderListener : (Collection<ChatRenderListener>)this.chatRenderListeners)
1024       chatRenderListener.onPostRenderChat(screenWidth, screenHeight, chat);
1025   }
1026 
1027   public void onTimerUpdate()
1028   {
1029     for (GameLoopListener loopListener : (Collection<GameLoopListener>)this.loopListeners)
1030       loopListener.onRunGameLoop(this.minecraft);
1031   }
1032 
1033   public void onTick(Profiler profiler, boolean tick)
1034   {
1035     float partialTicks = 0.0F;
1036 
1037     if ((tick) || (this.minecraftTimer == null))
1038     {
1039       this.minecraftTimer = ((Timer)PrivateFields.minecraftTimer.get(this.minecraft));
1040     }
1041 
1042     if (this.minecraftTimer != null)
1043     {
1044       partialTicks = this.minecraftTimer.renderPartialTicks;
1045       tick = this.minecraftTimer.elapsedTicks > 0;
1046     }
1047 
1048     boolean inGame = (this.minecraft.renderViewEntity != null) && (this.minecraft.renderViewEntity.worldObj != null);
1049 
1050     for (Tickable tickable : (Collection<Tickable>)this.tickListeners)
1051     {
1052       profiler.startSection(tickable.getClass().getSimpleName());
1053       tickable.onTick(this.minecraft, partialTicks, inGame, tick);
1054       profiler.endSection();
1055     }
1056   }
1057 
1058   public boolean onChat(Packet3Chat chatPacket)
1059   {
1060     for (ChatFilter chatFilter : (Collection<ChatFilter>)this.chatFilters) {
1061       if (!chatFilter.onChat(chatPacket)) {
1062         return false;
1063       }
1064     }
1065     for (ChatListener chatListener : (Collection<ChatListener>)this.chatListeners) {
1066       chatListener.onChat(chatPacket.message);
1067     }
1068     return true;
1069   }
1070 
1071   public boolean onPreLogin(NetHandler netHandler, Packet1Login loginPacket)
1072   {
1073     boolean cancelled = false;
1074 
1075     for (PreLoginListener loginListener : (Collection<PreLoginListener>)this.preLoginListeners)
1076     {
1077       cancelled |= !loginListener.onPreLogin(netHandler, loginPacket);
1078     }
1079 
1080     return !cancelled;
1081   }
1082 
1083   public void onConnectToServer(NetHandler netHandler, Packet1Login loginPacket)
1084   {
1085     for (LoginListener loginListener : (Collection<LoginListener>)this.loginListeners) {
1086       loginListener.onLogin(netHandler, loginPacket);
1087     }
1088     setupPluginChannels();
1089   }
1090 
1091   public void onPluginChannelMessage(HookPluginChannels hookPluginChannels)
1092   {
1093     if ((hookPluginChannels != null) && (hookPluginChannels.channel != null) && (this.pluginChannels.containsKey(hookPluginChannels.channel)))
1094     {
1095       for (PluginChannelListener pluginChannelListener : (Collection<PluginChannelListener>)this.pluginChannels.get(hookPluginChannels.channel))
1096       {
1097         try
1098         {
1099           pluginChannelListener.onCustomPayload(hookPluginChannels.channel, hookPluginChannels.length, hookPluginChannels.data);
1100         }
1101         catch (Exception ex)
1102         {
1103         }
1104       }
1105     }
1106   }
1107 
1108   public void sendPluginChannelMessage(String channel, byte[] data)
1109   {
1110     ModUtilities.sendPluginChannelMessage(channel, data);
1111   }
1112 
1113   protected void setupPluginChannels()
1114   {
1115     this.pluginChannels.clear();
1116 
1117     for (PluginChannelListener pluginChannelListener : (Collection<PluginChannelListener>)pluginChannelListeners) {
1118 
1119       List channels = pluginChannelListener.getChannels();
1120 
1121       if (channels != null)
1122       {
1123         for (String channel : (Collection<String>)channels)
1124         {
1125           if ((channel.length() > 16) || (channel.toUpperCase().equals("REGISTER")) || (channel.toUpperCase().equals("UNREGISTER"))) {
1126             continue;
1127           }
1128           if (!this.pluginChannels.containsKey(channel))
1129           {
1130             this.pluginChannels.put(channel, new LinkedList());
1131           }
1132 
1133           ((LinkedList)this.pluginChannels.get(channel)).add(pluginChannelListener);
1134         }
1135       }
1136     }
1137     PluginChannelListener pluginChannelListener;
1138     if (this.pluginChannels.keySet().size() > 0)
1139     {
1140       StringBuilder channelList = new StringBuilder();
1141       boolean separator = false;
1142 
1143       for (String channel : (Collection<String>)this.pluginChannels.keySet())
1144       {
1145         if (separator) channelList.append("");
1146         channelList.append(channel);
1147         separator = true;
1148       }
1149 
1150       byte[] registrationData = channelList.toString().getBytes(Charset.forName("UTF8"));
1151 
1152       sendPluginChannelMessage("REGISTER", registrationData);
1153     }
1154   }
1155 
1156   public void addServerStatsToSnooper(PlayerUsageSnooper var1)
1157   {
1158     this.minecraft.addServerStatsToSnooper(var1);
1159   }
1160 
1161   public void addServerTypeToSnooper(PlayerUsageSnooper var1)
1162   {
1163     sanityCheck();
1164     this.minecraft.addServerTypeToSnooper(var1);
1165   }
1166 
1167   public boolean isSnooperEnabled()
1168   {
1169     return this.minecraft.isSnooperEnabled();
1170   }
1171 
1172   public ILogAgent getLogAgent()
1173   {
1174     return this.minecraft.getLogAgent();
1175   }
1176 
1177   private void sanityCheck()
1178   {
1179     if ((this.tickHooked) && (this.minecraft.mcProfiler != this.profilerHook))
1180     {
1181       PrivateFields.minecraftProfiler.setFinal(this.minecraft, this.profilerHook);
1182     }
1183   }
1184 }