diff --git a/src/minecraft/Start.java b/src/minecraft/Start.java
new file mode 100644
index 0000000..ac40620
--- /dev/null
+++ b/src/minecraft/Start.java
@@ -0,0 +1,88 @@
+import java.io.File;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Field;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLEncoder;
+
+import net.minecraft.client.Minecraft;
+
+public class Start
+{
+ public static void main(String[] args)
+ {
+ try
+ {
+ Field f = Minecraft.class.getDeclaredField("minecraftDir");
+ Field.setAccessible(new Field[] { f }, true);
+ f.set(null, new File("."));
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ return;
+ }
+
+ if (args.length != 2)
+ {
+ Minecraft.main(args);
+ }
+ else
+ {
+ try
+ {
+ String parameters = "http://login.minecraft.net/?user=" + URLEncoder.encode(args[0], "UTF-8") +
+ "&password=" + URLEncoder.encode(args[1], "UTF-8") +
+ "&version=" + 13;
+ String result = openUrl(parameters);
+
+ if (result == null)
+ {
+ System.out.println("Can't connect to minecraft.net");
+ return;
+ }
+
+ if (!result.contains(":"))
+ {
+ System.out.println("Login Failed: " + result);
+ return;
+ }
+
+ String[] values = result.split(":");
+ Minecraft.main(new String[] {values[2].trim(), values[3].trim()});
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private static String openUrl(String addr)
+ {
+ try
+ {
+ URL url = new URL(addr);
+ java.io.InputStream is;
+ is = url.openConnection().getInputStream();
+ java.io.BufferedReader reader = new java.io.BufferedReader(new java.io.InputStreamReader(is));
+ String buf = "";
+ String line = null;
+
+ while ((line = reader.readLine()) != null)
+ {
+ buf += "\n" + line;
+ }
+
+ reader.close();
+ return buf;
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/src/minecraft/cpw/mods/fml/client/CustomModLoadingErrorDisplayException.java b/src/minecraft/cpw/mods/fml/client/CustomModLoadingErrorDisplayException.java
new file mode 100644
index 0000000..a2ad27f
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/client/CustomModLoadingErrorDisplayException.java
@@ -0,0 +1,43 @@
+package cpw.mods.fml.client;
+
+import net.minecraft.client.gui.FontRenderer;
+import net.minecraft.client.gui.GuiErrorScreen;
+import cpw.mods.fml.common.IFMLHandledException;
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+
+/**
+ * If a mod throws this exception during loading, it will be called back to render
+ * the error screen through the methods below. This error will not be cleared, and will
+ * not allow the game to carry on, but might be useful if your mod wishes to report
+ * a fatal configuration error in a pretty way.
+ *
+ * Throw this through a proxy. It won't work on the dedicated server environment.
+ * @author cpw
+ *
+ */
+@SideOnly(Side.CLIENT)
+public abstract class CustomModLoadingErrorDisplayException extends RuntimeException implements IFMLHandledException
+{
+ /**
+ * Called after the GUI is inited by the parent code. You can do extra stuff here, maybe?
+ *
+ * @param errorScreen The error screen we're painting
+ * @param fontRenderer A font renderer for you
+ */
+ public abstract void initGui(GuiErrorScreen errorScreen, FontRenderer fontRenderer);
+
+ /**
+ * Draw your error to the screen.
+ *
+ * Warning: Minecraft is in a deep error state.All it can do is stop.
+ * Do not try and do anything involving complex user interaction here.
+ *
+ * @param errorScreen The error screen to draw to
+ * @param fontRenderer A font renderer for you
+ * @param mouseRelX Mouse X
+ * @param mouseRelY Mouse Y
+ * @param tickTime tick time
+ */
+ public abstract void drawScreen(GuiErrorScreen errorScreen, FontRenderer fontRenderer, int mouseRelX, int mouseRelY, float tickTime);
+}
diff --git a/src/minecraft/cpw/mods/fml/client/FMLClientHandler.java b/src/minecraft/cpw/mods/fml/client/FMLClientHandler.java
new file mode 100644
index 0000000..535b760
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/client/FMLClientHandler.java
@@ -0,0 +1,533 @@
+/*
+ * The FML Forge Mod Loader suite. Copyright (C) 2012 cpw
+ *
+ * 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
+ * Software Foundation; either version 2.1 of the License, or any later version.
+ *
+ * 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
+ * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * 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
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+package cpw.mods.fml.client;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.GuiScreen;
+import net.minecraft.client.multiplayer.GuiConnecting;
+import net.minecraft.client.multiplayer.NetClientHandler;
+import net.minecraft.client.multiplayer.WorldClient;
+import net.minecraft.client.renderer.entity.Render;
+import net.minecraft.client.renderer.entity.RenderManager;
+import net.minecraft.crash.CrashReport;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.EntityLiving;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.network.INetworkManager;
+import net.minecraft.network.packet.NetHandler;
+import net.minecraft.network.packet.Packet;
+import net.minecraft.network.packet.Packet131MapData;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.world.World;
+
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.MapDifference;
+import com.google.common.collect.MapDifference.ValueDifference;
+
+import cpw.mods.fml.client.modloader.ModLoaderClientHelper;
+import cpw.mods.fml.client.registry.KeyBindingRegistry;
+import cpw.mods.fml.client.registry.RenderingRegistry;
+import cpw.mods.fml.common.DummyModContainer;
+import cpw.mods.fml.common.DuplicateModsFoundException;
+import cpw.mods.fml.common.FMLCommonHandler;
+import cpw.mods.fml.common.FMLLog;
+import cpw.mods.fml.common.IFMLSidedHandler;
+import cpw.mods.fml.common.Loader;
+import cpw.mods.fml.common.LoaderException;
+import cpw.mods.fml.common.MetadataCollection;
+import cpw.mods.fml.common.MissingModsException;
+import cpw.mods.fml.common.ModContainer;
+import cpw.mods.fml.common.ModMetadata;
+import cpw.mods.fml.common.ObfuscationReflectionHelper;
+import cpw.mods.fml.common.WrongMinecraftVersionException;
+import cpw.mods.fml.common.network.EntitySpawnAdjustmentPacket;
+import cpw.mods.fml.common.network.EntitySpawnPacket;
+import cpw.mods.fml.common.network.ModMissingPacket;
+import cpw.mods.fml.common.registry.EntityRegistry.EntityRegistration;
+import cpw.mods.fml.common.registry.GameData;
+import cpw.mods.fml.common.registry.GameRegistry;
+import cpw.mods.fml.common.registry.IEntityAdditionalSpawnData;
+import cpw.mods.fml.common.registry.IThrowableEntity;
+import cpw.mods.fml.common.registry.ItemData;
+import cpw.mods.fml.common.registry.LanguageRegistry;
+import cpw.mods.fml.relauncher.Side;
+
+
+/**
+ * Handles primary communication from hooked code into the system
+ *
+ * The FML entry point is {@link #beginMinecraftLoading(Minecraft)} called from
+ * {@link Minecraft}
+ *
+ * Obfuscated code should focus on this class and other members of the "server"
+ * (or "client") code
+ *
+ * The actual mod loading is handled at arms length by {@link Loader}
+ *
+ * It is expected that a similar class will exist for each target environment:
+ * Bukkit and Client side.
+ *
+ * It should not be directly modified.
+ *
+ * @author cpw
+ *
+ */
+public class FMLClientHandler implements IFMLSidedHandler
+{
+ /**
+ * The singleton
+ */
+ private static final FMLClientHandler INSTANCE = new FMLClientHandler();
+
+ /**
+ * A reference to the server itself
+ */
+ private Minecraft client;
+
+ private DummyModContainer optifineContainer;
+
+ private boolean guiLoaded;
+
+ private boolean serverIsRunning;
+
+ private MissingModsException modsMissing;
+
+ private boolean loading;
+
+ private WrongMinecraftVersionException wrongMC;
+
+ private CustomModLoadingErrorDisplayException customError;
+
+ private DuplicateModsFoundException dupesFound;
+
+ private boolean serverShouldBeKilledQuietly;
+
+ /**
+ * Called to start the whole game off
+ *
+ * @param minecraft The minecraft instance being launched
+ */
+ public void beginMinecraftLoading(Minecraft minecraft)
+ {
+ if (minecraft.isDemo())
+ {
+ FMLLog.severe("DEMO MODE DETECTED, FML will not work. Finishing now.");
+ haltGame("FML will not run in demo mode", new RuntimeException());
+ return;
+ }
+
+ loading = true;
+ client = minecraft;
+ ObfuscationReflectionHelper.detectObfuscation(World.class);
+ TextureFXManager.instance().setClient(client);
+ FMLCommonHandler.instance().beginLoading(this);
+ new ModLoaderClientHelper(client);
+ try
+ {
+ Class> optifineConfig = Class.forName("Config", false, Loader.instance().getModClassLoader());
+ String optifineVersion = (String) optifineConfig.getField("VERSION").get(null);
+ Map dummyOptifineMeta = ImmutableMap.builder().put("name", "Optifine").put("version", optifineVersion).build();
+ ModMetadata optifineMetadata = MetadataCollection.from(getClass().getResourceAsStream("optifinemod.info"),"optifine").getMetadataForId("optifine", dummyOptifineMeta);
+ optifineContainer = new DummyModContainer(optifineMetadata);
+ FMLLog.info("Forge Mod Loader has detected optifine %s, enabling compatibility features",optifineContainer.getVersion());
+ }
+ catch (Exception e)
+ {
+ optifineContainer = null;
+ }
+ try
+ {
+ Loader.instance().loadMods();
+ }
+ catch (WrongMinecraftVersionException wrong)
+ {
+ wrongMC = wrong;
+ }
+ catch (DuplicateModsFoundException dupes)
+ {
+ dupesFound = dupes;
+ }
+ catch (MissingModsException missing)
+ {
+ modsMissing = missing;
+ }
+ catch (CustomModLoadingErrorDisplayException custom)
+ {
+ FMLLog.log(Level.SEVERE, custom, "A custom exception was thrown by a mod, the game will now halt");
+ customError = custom;
+ }
+ catch (LoaderException le)
+ {
+ haltGame("There was a severe problem during mod loading that has caused the game to fail", le);
+ return;
+ }
+ }
+
+ @Override
+ public void haltGame(String message, Throwable t)
+ {
+ client.displayCrashReport(new CrashReport(message, t));
+ throw Throwables.propagate(t);
+ }
+ /**
+ * Called a bit later on during initialization to finish loading mods
+ * Also initializes key bindings
+ *
+ */
+ @SuppressWarnings("deprecation")
+ public void finishMinecraftLoading()
+ {
+ if (modsMissing != null || wrongMC != null || customError!=null || dupesFound!=null)
+ {
+ return;
+ }
+ try
+ {
+ Loader.instance().initializeMods();
+ }
+ catch (CustomModLoadingErrorDisplayException custom)
+ {
+ FMLLog.log(Level.SEVERE, custom, "A custom exception was thrown by a mod, the game will now halt");
+ customError = custom;
+ return;
+ }
+ catch (LoaderException le)
+ {
+ haltGame("There was a severe problem during mod loading that has caused the game to fail", le);
+ return;
+ }
+ LanguageRegistry.reloadLanguageTable();
+ RenderingRegistry.instance().loadEntityRenderers((Map, Render>)RenderManager.instance.entityRenderMap);
+
+ loading = false;
+ KeyBindingRegistry.instance().uploadKeyBindingsToGame(client.gameSettings);
+ }
+
+ public void onInitializationComplete()
+ {
+ if (wrongMC != null)
+ {
+ client.displayGuiScreen(new GuiWrongMinecraft(wrongMC));
+ }
+ else if (modsMissing != null)
+ {
+ client.displayGuiScreen(new GuiModsMissing(modsMissing));
+ }
+ else if (dupesFound != null)
+ {
+ client.displayGuiScreen(new GuiDupesFound(dupesFound));
+ }
+ else if (customError != null)
+ {
+ client.displayGuiScreen(new GuiCustomModLoadingErrorScreen(customError));
+ }
+ else
+ {
+ TextureFXManager.instance().loadTextures(client.texturePackList.getSelectedTexturePack());
+ }
+ }
+ /**
+ * Get the server instance
+ */
+ public Minecraft getClient()
+ {
+ return client;
+ }
+
+ /**
+ * Get a handle to the client's logger instance
+ * The client actually doesn't have one- so we return null
+ */
+ public Logger getMinecraftLogger()
+ {
+ return null;
+ }
+
+ /**
+ * @return the instance
+ */
+ public static FMLClientHandler instance()
+ {
+ return INSTANCE;
+ }
+
+ /**
+ * @param player
+ * @param gui
+ */
+ public void displayGuiScreen(EntityPlayer player, GuiScreen gui)
+ {
+ if (client.thePlayer==player && gui != null) {
+ client.displayGuiScreen(gui);
+ }
+ }
+
+ /**
+ * @param mods
+ */
+ public void addSpecialModEntries(ArrayList mods)
+ {
+ if (optifineContainer!=null) {
+ mods.add(optifineContainer);
+ }
+ }
+
+ @Override
+ public List getAdditionalBrandingInformation()
+ {
+ if (optifineContainer!=null)
+ {
+ return Arrays.asList(String.format("Optifine %s",optifineContainer.getVersion()));
+ } else {
+ return ImmutableList.of();
+ }
+ }
+
+ @Override
+ public Side getSide()
+ {
+ return Side.CLIENT;
+ }
+
+ public boolean hasOptifine()
+ {
+ return optifineContainer!=null;
+ }
+
+ @Override
+ public void showGuiScreen(Object clientGuiElement)
+ {
+ GuiScreen gui = (GuiScreen) clientGuiElement;
+ client.displayGuiScreen(gui);
+ }
+
+ @Override
+ public Entity spawnEntityIntoClientWorld(EntityRegistration er, EntitySpawnPacket packet)
+ {
+ WorldClient wc = client.theWorld;
+
+ Class extends Entity> cls = er.getEntityClass();
+
+ try
+ {
+ Entity entity;
+ if (er.hasCustomSpawning())
+ {
+ entity = er.doCustomSpawning(packet);
+ }
+ else
+ {
+ entity = (Entity)(cls.getConstructor(World.class).newInstance(wc));
+ entity.entityId = packet.entityId;
+ entity.setLocationAndAngles(packet.scaledX, packet.scaledY, packet.scaledZ, packet.scaledYaw, packet.scaledPitch);
+ if (entity instanceof EntityLiving)
+ {
+ ((EntityLiving)entity).rotationYawHead = packet.scaledHeadYaw;
+ }
+
+ }
+
+ entity.serverPosX = packet.rawX;
+ entity.serverPosY = packet.rawY;
+ entity.serverPosZ = packet.rawZ;
+
+ if (entity instanceof IThrowableEntity)
+ {
+ Entity thrower = client.thePlayer.entityId == packet.throwerId ? client.thePlayer : wc.getEntityByID(packet.throwerId);
+ ((IThrowableEntity)entity).setThrower(thrower);
+ }
+
+
+ Entity parts[] = entity.getParts();
+ if (parts != null)
+ {
+ int i = packet.entityId - entity.entityId;
+ for (int j = 0; j < parts.length; j++)
+ {
+ parts[j].entityId += i;
+ }
+ }
+
+
+ if (packet.metadata != null)
+ {
+ entity.getDataWatcher().updateWatchedObjectsFromList((List)packet.metadata);
+ }
+
+ if (packet.throwerId > 0)
+ {
+ entity.setVelocity(packet.speedScaledX, packet.speedScaledY, packet.speedScaledZ);
+ }
+
+ if (entity instanceof IEntityAdditionalSpawnData)
+ {
+ ((IEntityAdditionalSpawnData)entity).readSpawnData(packet.dataStream);
+ }
+
+ wc.addEntityToWorld(packet.entityId, entity);
+ return entity;
+ }
+ catch (Exception e)
+ {
+ FMLLog.log(Level.SEVERE, e, "A severe problem occurred during the spawning of an entity");
+ throw Throwables.propagate(e);
+ }
+ }
+
+ @Override
+ public void adjustEntityLocationOnClient(EntitySpawnAdjustmentPacket packet)
+ {
+ Entity ent = client.theWorld.getEntityByID(packet.entityId);
+ if (ent != null)
+ {
+ ent.serverPosX = packet.serverX;
+ ent.serverPosY = packet.serverY;
+ ent.serverPosZ = packet.serverZ;
+ }
+ else
+ {
+ FMLLog.fine("Attempted to adjust the position of entity %d which is not present on the client", packet.entityId);
+ }
+ }
+
+ @Override
+ public void beginServerLoading(MinecraftServer server)
+ {
+ serverShouldBeKilledQuietly = false;
+ // NOOP
+ }
+
+ @Override
+ public void finishServerLoading()
+ {
+ // NOOP
+ }
+
+ @Override
+ public MinecraftServer getServer()
+ {
+ return client.getIntegratedServer();
+ }
+
+ @Override
+ public void sendPacket(Packet packet)
+ {
+ if(client.thePlayer != null)
+ {
+ client.thePlayer.sendQueue.addToSendQueue(packet);
+ }
+ }
+
+ @Override
+ public void displayMissingMods(ModMissingPacket modMissingPacket)
+ {
+ client.displayGuiScreen(new GuiModsMissingForServer(modMissingPacket));
+ }
+
+ /**
+ * If the client is in the midst of loading, we disable saving so that custom settings aren't wiped out
+ */
+ public boolean isLoading()
+ {
+ return loading;
+ }
+
+ @Override
+ public void handleTinyPacket(NetHandler handler, Packet131MapData mapData)
+ {
+ ((NetClientHandler)handler).fmlPacket131Callback(mapData);
+ }
+
+ @Override
+ public void setClientCompatibilityLevel(byte compatibilityLevel)
+ {
+ NetClientHandler.setConnectionCompatibilityLevel(compatibilityLevel);
+ }
+
+ @Override
+ public byte getClientCompatibilityLevel()
+ {
+ return NetClientHandler.getConnectionCompatibilityLevel();
+ }
+
+ public void warnIDMismatch(MapDifference idDifferences, boolean mayContinue)
+ {
+ GuiIdMismatchScreen mismatch = new GuiIdMismatchScreen(idDifferences, mayContinue);
+ client.displayGuiScreen(mismatch);
+ }
+
+ public void callbackIdDifferenceResponse(boolean response)
+ {
+ if (response)
+ {
+ serverShouldBeKilledQuietly = false;
+ GameData.releaseGate(true);
+ client.continueWorldLoading();
+ }
+ else
+ {
+ serverShouldBeKilledQuietly = true;
+ GameData.releaseGate(false);
+ // Reset and clear the client state
+ client.loadWorld((WorldClient)null);
+ client.displayGuiScreen(null);
+ }
+ }
+
+ @Override
+ public boolean shouldServerShouldBeKilledQuietly()
+ {
+ return serverShouldBeKilledQuietly;
+ }
+
+ @Override
+ public void disconnectIDMismatch(MapDifference s, NetHandler toKill, INetworkManager mgr)
+ {
+ boolean criticalMismatch = !s.entriesOnlyOnLeft().isEmpty();
+ for (Entry> mismatch : s.entriesDiffering().entrySet())
+ {
+ ValueDifference vd = mismatch.getValue();
+ if (!vd.leftValue().mayDifferByOrdinal(vd.rightValue()))
+ {
+ criticalMismatch = true;
+ }
+ }
+
+ if (!criticalMismatch)
+ {
+ // We'll carry on with this connection, and just log a message instead
+ return;
+ }
+ // Nuke the connection
+ ((NetClientHandler)toKill).disconnect();
+ // Stop GuiConnecting
+ GuiConnecting.forceTermination((GuiConnecting)client.currentScreen);
+ // pulse the network manager queue to clear cruft
+ mgr.processReadPackets();
+ // Nuke the world client
+ client.loadWorld((WorldClient)null);
+ // Show error screen
+ warnIDMismatch(s, false);
+ }
+}
diff --git a/src/minecraft/cpw/mods/fml/client/FMLTextureFX.java b/src/minecraft/cpw/mods/fml/client/FMLTextureFX.java
new file mode 100644
index 0000000..d3182dc
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/client/FMLTextureFX.java
@@ -0,0 +1,70 @@
+/*
+ * The FML Forge Mod Loader suite.
+ * Copyright (C) 2012 cpw
+ *
+ * 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
+ * Software Foundation; either version 2.1 of the License, or any later version.
+ *
+ * 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
+ * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * 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
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package cpw.mods.fml.client;
+
+import java.awt.Dimension;
+import java.util.List;
+import java.util.logging.Logger;
+
+import net.minecraft.client.renderer.RenderEngine;
+import net.minecraft.client.renderer.texturefx.TextureFX;
+import net.minecraft.client.texturepacks.ITexturePack;
+
+import cpw.mods.fml.common.FMLCommonHandler;
+import cpw.mods.fml.common.FMLLog;
+
+public class FMLTextureFX extends TextureFX implements ITextureFX
+{
+ public int tileSizeBase = 16;
+ public int tileSizeSquare = 256;
+ public int tileSizeMask = 15;
+ public int tileSizeSquareMask = 255;
+ public boolean errored = false;
+ protected Logger log = FMLLog.getLogger();
+
+ public FMLTextureFX(int icon)
+ {
+ super(icon);
+ }
+
+ @Override public void setErrored(boolean err){ errored = err; }
+ @Override public boolean getErrored(){ return errored; }
+ @Override
+ public void onTexturePackChanged(RenderEngine engine, ITexturePack texturepack, Dimension dimensions)
+ {
+ onTextureDimensionsUpdate(dimensions.width, dimensions.height);
+ }
+ @Override
+ public void onTextureDimensionsUpdate(int width, int height)
+ {
+ tileSizeBase = width >> 4;
+ tileSizeSquare = tileSizeBase * tileSizeBase;
+ tileSizeMask = tileSizeBase - 1;
+ tileSizeSquareMask = tileSizeSquare - 1;
+ setErrored(false);
+ setup();
+ }
+
+ protected void setup()
+ {
+ imageData = new byte[tileSizeSquare << 2];
+ }
+
+ public boolean unregister(RenderEngine engine, List effects)
+ {
+ effects.remove(this);
+ return true;
+ }
+}
diff --git a/src/minecraft/cpw/mods/fml/client/GuiCustomModLoadingErrorScreen.java b/src/minecraft/cpw/mods/fml/client/GuiCustomModLoadingErrorScreen.java
new file mode 100644
index 0000000..cdfd51b
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/client/GuiCustomModLoadingErrorScreen.java
@@ -0,0 +1,34 @@
+package cpw.mods.fml.client;
+
+import net.minecraft.client.gui.GuiErrorScreen;
+import cpw.mods.fml.common.Loader;
+import cpw.mods.fml.common.WrongMinecraftVersionException;
+
+public class GuiCustomModLoadingErrorScreen extends GuiErrorScreen
+{
+ private CustomModLoadingErrorDisplayException customException;
+ public GuiCustomModLoadingErrorScreen(CustomModLoadingErrorDisplayException customException)
+ {
+ this.customException = customException;
+ }
+ @Override
+
+ /**
+ * Adds the buttons (and other controls) to the screen in question.
+ */
+ public void initGui()
+ {
+ super.initGui();
+ this.customException.initGui(this, fontRenderer);
+ }
+ @Override
+
+ /**
+ * Draws the screen and all the components in it.
+ */
+ public void drawScreen(int par1, int par2, float par3)
+ {
+ this.drawDefaultBackground();
+ this.customException.drawScreen(this, fontRenderer, par1, par2, par3);
+ }
+}
diff --git a/src/minecraft/cpw/mods/fml/client/GuiDupesFound.java b/src/minecraft/cpw/mods/fml/client/GuiDupesFound.java
new file mode 100644
index 0000000..67f5d6a
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/client/GuiDupesFound.java
@@ -0,0 +1,53 @@
+package cpw.mods.fml.client;
+
+import java.io.File;
+import java.util.Map.Entry;
+
+import net.minecraft.client.gui.GuiErrorScreen;
+
+import cpw.mods.fml.common.DuplicateModsFoundException;
+import cpw.mods.fml.common.MissingModsException;
+import cpw.mods.fml.common.ModContainer;
+import cpw.mods.fml.common.versioning.ArtifactVersion;
+
+public class GuiDupesFound extends GuiErrorScreen
+{
+
+ private DuplicateModsFoundException dupes;
+
+ public GuiDupesFound(DuplicateModsFoundException dupes)
+ {
+ this.dupes = dupes;
+ }
+
+ @Override
+
+ /**
+ * Adds the buttons (and other controls) to the screen in question.
+ */
+ public void initGui()
+ {
+ super.initGui();
+ }
+ @Override
+
+ /**
+ * Draws the screen and all the components in it.
+ */
+ public void drawScreen(int par1, int par2, float par3)
+ {
+ this.drawDefaultBackground();
+ int offset = Math.max(85 - dupes.dupes.size() * 10, 10);
+ this.drawCenteredString(this.fontRenderer, "Forge Mod Loader has found a problem with your minecraft installation", this.width / 2, offset, 0xFFFFFF);
+ offset+=10;
+ this.drawCenteredString(this.fontRenderer, "You have mod sources that are duplicate within your system", this.width / 2, offset, 0xFFFFFF);
+ offset+=10;
+ this.drawCenteredString(this.fontRenderer, "Mod Id : File name", this.width / 2, offset, 0xFFFFFF);
+ offset+=5;
+ for (Entry mc : dupes.dupes.entries())
+ {
+ offset+=10;
+ this.drawCenteredString(this.fontRenderer, String.format("%s : %s", mc.getKey().getModId(), mc.getValue().getName()), this.width / 2, offset, 0xEEEEEE);
+ }
+ }
+}
diff --git a/src/minecraft/cpw/mods/fml/client/GuiIdMismatchScreen.java b/src/minecraft/cpw/mods/fml/client/GuiIdMismatchScreen.java
new file mode 100644
index 0000000..cf23567
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/client/GuiIdMismatchScreen.java
@@ -0,0 +1,92 @@
+package cpw.mods.fml.client;
+
+import java.util.List;
+import java.util.Map.Entry;
+
+import net.minecraft.client.gui.GuiButton;
+import net.minecraft.client.gui.GuiYesNo;
+import net.minecraft.util.StringTranslate;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.MapDifference;
+import com.google.common.collect.MapDifference.ValueDifference;
+
+import cpw.mods.fml.common.registry.ItemData;
+import cpw.mods.fml.common.versioning.ArtifactVersion;
+
+public class GuiIdMismatchScreen extends GuiYesNo {
+ private List missingIds = Lists.newArrayList();
+ private List mismatchedIds = Lists.newArrayList();
+ private boolean allowContinue;
+
+ public GuiIdMismatchScreen(MapDifference idDifferences, boolean allowContinue)
+ {
+ super(null,"ID mismatch", "Should I continue?", 1);
+ parentScreen = this;
+ for (Entry entry : idDifferences.entriesOnlyOnLeft().entrySet())
+ {
+ missingIds.add(String.format("ID %d (ModID: %s, type %s) is missing", entry.getValue().getItemId(), entry.getValue().getModId(), entry.getValue().getItemType()));
+ }
+ for (Entry> entry : idDifferences.entriesDiffering().entrySet())
+ {
+ ItemData world = entry.getValue().leftValue();
+ ItemData game = entry.getValue().rightValue();
+ mismatchedIds.add(String.format("ID %d is mismatched. World: (ModID: %s, type %s, ordinal %d) Game (ModID: %s, type %s, ordinal %d)", world.getItemId(), world.getModId(), world.getItemType(), world.getOrdinal(), game.getModId(), game.getItemType(), game.getOrdinal()));
+ }
+ this.allowContinue = allowContinue;
+ }
+
+ @Override
+ public void confirmClicked(boolean choice, int par2)
+ {
+ FMLClientHandler.instance().callbackIdDifferenceResponse(choice);
+ }
+
+ @Override
+
+ /**
+ * Draws the screen and all the components in it.
+ */
+ public void drawScreen(int par1, int par2, float par3)
+ {
+ this.drawDefaultBackground();
+ if (!allowContinue && controlList.size() == 2)
+ {
+ controlList.remove(0);
+ }
+ int offset = Math.max(85 - missingIds.size() * 10 + mismatchedIds.size() * 30, 10);
+ this.drawCenteredString(this.fontRenderer, "Forge Mod Loader has found world ID mismatches", this.width / 2, offset, 0xFFFFFF);
+ offset += 10;
+ for (String s: missingIds) {
+ this.drawCenteredString(this.fontRenderer, s, this.width / 2, offset, 0xEEEEEE);
+ offset += 10;
+ }
+ for (String s: mismatchedIds) {
+ this.drawCenteredString(this.fontRenderer, s, this.width / 2, offset, 0xEEEEEE);
+ offset += 10;
+ }
+ offset += 10;
+ if (allowContinue)
+ {
+ this.drawCenteredString(this.fontRenderer, "Do you wish to continue loading?", this.width / 2, offset, 0xFFFFFF);
+ offset += 10;
+ }
+ else
+ {
+ this.drawCenteredString(this.fontRenderer, "You cannot connect to this server", this.width / 2, offset, 0xFFFFFF);
+ offset += 10;
+ }
+ // super.super. Grrr
+ for (int var4 = 0; var4 < this.controlList.size(); ++var4)
+ {
+ GuiButton var5 = (GuiButton)this.controlList.get(var4);
+ var5.yPosition = Math.min(offset + 10, this.height - 20);
+ if (!allowContinue)
+ {
+ var5.xPosition = this.width / 2 - 75;
+ var5.displayString = StringTranslate.getInstance().translateKey("gui.done");
+ }
+ var5.drawButton(this.mc, par1, par2);
+ }
+ }
+}
diff --git a/src/minecraft/cpw/mods/fml/client/GuiModList.java b/src/minecraft/cpw/mods/fml/client/GuiModList.java
new file mode 100644
index 0000000..aacaa6e
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/client/GuiModList.java
@@ -0,0 +1,198 @@
+/*
+ * The FML Forge Mod Loader suite.
+ * Copyright (C) 2012 cpw
+ *
+ * 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
+ * Software Foundation; either version 2.1 of the License, or any later version.
+ *
+ * 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
+ * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * 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
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package cpw.mods.fml.client;
+
+import java.awt.Dimension;
+import java.util.ArrayList;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.FontRenderer;
+import net.minecraft.client.gui.GuiButton;
+import net.minecraft.client.gui.GuiScreen;
+import net.minecraft.client.gui.GuiSmallButton;
+import net.minecraft.client.renderer.Tessellator;
+import net.minecraft.util.StringTranslate;
+
+import org.lwjgl.opengl.GL11;
+
+import com.google.common.base.Strings;
+
+import cpw.mods.fml.common.Loader;
+import cpw.mods.fml.common.ModContainer;
+
+/**
+ * @author cpw
+ *
+ */
+public class GuiModList extends GuiScreen
+{
+ private GuiScreen mainMenu;
+ private GuiSlotModList modList;
+ private int selected = -1;
+ private ModContainer selectedMod;
+ private int listWidth;
+ private ArrayList mods;
+
+ /**
+ * @param mainMenu
+ */
+ public GuiModList(GuiScreen mainMenu)
+ {
+ this.mainMenu=mainMenu;
+ this.mods=new ArrayList();
+ FMLClientHandler.instance().addSpecialModEntries(mods);
+ for (ModContainer mod : Loader.instance().getModList()) {
+ if (mod.getMetadata()!=null && mod.getMetadata().parentMod==null && !Strings.isNullOrEmpty(mod.getMetadata().parent)) {
+ String parentMod = mod.getMetadata().parent;
+ ModContainer parentContainer = Loader.instance().getIndexedModList().get(parentMod);
+ if (parentContainer != null)
+ {
+ mod.getMetadata().parentMod = parentContainer;
+ parentContainer.getMetadata().childMods.add(mod);
+ continue;
+ }
+ }
+ else if (mod.getMetadata()!=null && mod.getMetadata().parentMod!=null)
+ {
+ continue;
+ }
+ mods.add(mod);
+ }
+ }
+
+ @Override
+
+ /**
+ * Adds the buttons (and other controls) to the screen in question.
+ */
+ public void initGui()
+ {
+ for (ModContainer mod : mods) {
+ listWidth=Math.max(listWidth,getFontRenderer().getStringWidth(mod.getName()) + 10);
+ listWidth=Math.max(listWidth,getFontRenderer().getStringWidth(mod.getVersion()) + 10);
+ }
+ listWidth=Math.min(listWidth, 150);
+ StringTranslate translations = StringTranslate.getInstance();
+ this.controlList.add(new GuiSmallButton(6, this.width / 2 - 75, this.height - 38, translations.translateKey("gui.done")));
+ this.modList=new GuiSlotModList(this, mods, listWidth);
+ this.modList.registerScrollButtons(this.controlList, 7, 8);
+ }
+
+ @Override
+
+ /**
+ * Fired when a control is clicked. This is the equivalent of ActionListener.actionPerformed(ActionEvent e).
+ */
+ protected void actionPerformed(GuiButton button) {
+ if (button.enabled)
+ {
+ switch (button.id)
+ {
+ case 6:
+ this.mc.displayGuiScreen(this.mainMenu);
+ return;
+ }
+ }
+ super.actionPerformed(button);
+ }
+
+ public int drawLine(String line, int offset, int shifty)
+ {
+ this.fontRenderer.drawString(line, offset, shifty, 0xd7edea);
+ return shifty + 10;
+ }
+
+ @Override
+
+ /**
+ * Draws the screen and all the components in it.
+ */
+ public void drawScreen(int p_571_1_, int p_571_2_, float p_571_3_)
+ {
+ this.modList.drawScreen(p_571_1_, p_571_2_, p_571_3_);
+ this.drawCenteredString(this.fontRenderer, "Mod List", this.width / 2, 16, 0xFFFFFF);
+ int offset = this.listWidth + 20;
+ if (selectedMod != null) {
+ GL11.glEnable(GL11.GL_BLEND);
+ if (!selectedMod.getMetadata().autogenerated) {
+ int shifty = 35;
+ if (!selectedMod.getMetadata().logoFile.isEmpty())
+ {
+ int texture = this.mc.renderEngine.getTexture(selectedMod.getMetadata().logoFile);
+ GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
+ this.mc.renderEngine.bindTexture(texture);
+ Dimension dim = TextureFXManager.instance().getTextureDimensions(texture);
+ int top = 32;
+ Tessellator tess = Tessellator.instance;
+ tess.startDrawingQuads();
+ tess.addVertexWithUV(offset, top + dim.height, zLevel, 0, 1);
+ tess.addVertexWithUV(offset + dim.width, top + dim.height, zLevel, 1, 1);
+ tess.addVertexWithUV(offset + dim.width, top, zLevel, 1, 0);
+ tess.addVertexWithUV(offset, top, zLevel, 0, 0);
+ tess.draw();
+
+ shifty += 65;
+ }
+ this.fontRenderer.drawStringWithShadow(selectedMod.getMetadata().name, offset, shifty, 0xFFFFFF);
+ shifty += 12;
+
+ shifty = drawLine(String.format("Version: %s (%s)", selectedMod.getDisplayVersion(), selectedMod.getVersion()), offset, shifty);
+ shifty = drawLine(String.format("Mod ID: '%s' Mod State: %s", selectedMod.getModId(), Loader.instance().getModState(selectedMod)), offset, shifty);
+ if (!selectedMod.getMetadata().credits.isEmpty()) {
+ shifty = drawLine(String.format("Credits: %s", selectedMod.getMetadata().credits), offset, shifty);
+ }
+ shifty = drawLine(String.format("Authors: %s", selectedMod.getMetadata().getAuthorList()), offset, shifty);
+ shifty = drawLine(String.format("URL: %s", selectedMod.getMetadata().url), offset, shifty);
+ shifty = drawLine(selectedMod.getMetadata().childMods.isEmpty() ? "No child mods for this mod" : String.format("Child mods: %s", selectedMod.getMetadata().getChildModList()), offset, shifty);
+ this.getFontRenderer().drawSplitString(selectedMod.getMetadata().description, offset, shifty + 10, this.width - offset - 20, 0xDDDDDD);
+ } else {
+ offset = ( this.listWidth + this.width ) / 2;
+ this.drawCenteredString(this.fontRenderer, selectedMod.getName(), offset, 35, 0xFFFFFF);
+ this.drawCenteredString(this.fontRenderer, String.format("Version: %s",selectedMod.getVersion()), offset, 45, 0xFFFFFF);
+ this.drawCenteredString(this.fontRenderer, String.format("Mod State: %s",Loader.instance().getModState(selectedMod)), offset, 55, 0xFFFFFF);
+ this.drawCenteredString(this.fontRenderer, "No mod information found", offset, 65, 0xDDDDDD);
+ this.drawCenteredString(this.fontRenderer, "Ask your mod author to provide a mod mcmod.info file", offset, 75, 0xDDDDDD);
+ }
+ GL11.glDisable(GL11.GL_BLEND);
+ }
+ super.drawScreen(p_571_1_, p_571_2_, p_571_3_);
+ }
+
+ Minecraft getMinecraftInstance() {
+ return mc;
+ }
+
+ FontRenderer getFontRenderer() {
+ return fontRenderer;
+ }
+
+ /**
+ * @param var1
+ */
+ public void selectModIndex(int var1)
+ {
+ this.selected=var1;
+ if (var1>=0 && var1<=mods.size()) {
+ this.selectedMod=mods.get(selected);
+ } else {
+ this.selectedMod=null;
+ }
+ }
+
+ public boolean modIndexSelected(int var1)
+ {
+ return var1==selected;
+ }
+}
diff --git a/src/minecraft/cpw/mods/fml/client/GuiModsMissing.java b/src/minecraft/cpw/mods/fml/client/GuiModsMissing.java
new file mode 100644
index 0000000..aa348e4
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/client/GuiModsMissing.java
@@ -0,0 +1,47 @@
+package cpw.mods.fml.client;
+
+import net.minecraft.client.gui.GuiErrorScreen;
+import cpw.mods.fml.common.MissingModsException;
+import cpw.mods.fml.common.versioning.ArtifactVersion;
+
+public class GuiModsMissing extends GuiErrorScreen
+{
+
+ private MissingModsException modsMissing;
+
+ public GuiModsMissing(MissingModsException modsMissing)
+ {
+ this.modsMissing = modsMissing;
+ }
+
+ @Override
+
+ /**
+ * Adds the buttons (and other controls) to the screen in question.
+ */
+ public void initGui()
+ {
+ super.initGui();
+ }
+ @Override
+
+ /**
+ * Draws the screen and all the components in it.
+ */
+ public void drawScreen(int par1, int par2, float par3)
+ {
+ this.drawDefaultBackground();
+ int offset = Math.max(85 - modsMissing.missingMods.size() * 10, 10);
+ this.drawCenteredString(this.fontRenderer, "Forge Mod Loader has found a problem with your minecraft installation", this.width / 2, offset, 0xFFFFFF);
+ offset+=10;
+ this.drawCenteredString(this.fontRenderer, "The mods and versions listed below could not be found", this.width / 2, offset, 0xFFFFFF);
+ offset+=5;
+ for (ArtifactVersion v : modsMissing.missingMods)
+ {
+ offset+=10;
+ this.drawCenteredString(this.fontRenderer, String.format("%s : %s", v.getLabel(), v.getRangeString()), this.width / 2, offset, 0xEEEEEE);
+ }
+ offset+=20;
+ this.drawCenteredString(this.fontRenderer, "The file 'ForgeModLoader-client-0.log' contains more information", this.width / 2, offset, 0xFFFFFF);
+ }
+}
diff --git a/src/minecraft/cpw/mods/fml/client/GuiModsMissingForServer.java b/src/minecraft/cpw/mods/fml/client/GuiModsMissingForServer.java
new file mode 100644
index 0000000..b340f8a
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/client/GuiModsMissingForServer.java
@@ -0,0 +1,64 @@
+package cpw.mods.fml.client;
+
+import net.minecraft.client.gui.GuiButton;
+import net.minecraft.client.gui.GuiScreen;
+import net.minecraft.client.gui.GuiSmallButton;
+import net.minecraft.util.StringTranslate;
+import cpw.mods.fml.common.network.ModMissingPacket;
+import cpw.mods.fml.common.versioning.ArtifactVersion;
+
+public class GuiModsMissingForServer extends GuiScreen
+{
+ private ModMissingPacket modsMissing;
+
+ public GuiModsMissingForServer(ModMissingPacket modsMissing)
+ {
+ this.modsMissing = modsMissing;
+ }
+
+ @Override
+
+ /**
+ * Adds the buttons (and other controls) to the screen in question.
+ */
+ public void initGui()
+ {
+ StringTranslate translations = StringTranslate.getInstance();
+ this.controlList.add(new GuiSmallButton(1, this.width / 2 - 75, this.height - 38, translations.translateKey("gui.done")));
+ }
+
+ @Override
+
+ /**
+ * Fired when a control is clicked. This is the equivalent of ActionListener.actionPerformed(ActionEvent e).
+ */
+ protected void actionPerformed(GuiButton par1GuiButton)
+ {
+ if (par1GuiButton.enabled && par1GuiButton.id == 1)
+ {
+ FMLClientHandler.instance().getClient().displayGuiScreen(null);
+ }
+ }
+ @Override
+
+ /**
+ * Draws the screen and all the components in it.
+ */
+ public void drawScreen(int par1, int par2, float par3)
+ {
+ this.drawDefaultBackground();
+ int offset = Math.max(85 - modsMissing.getModList().size() * 10, 10);
+ this.drawCenteredString(this.fontRenderer, "Forge Mod Loader could not connect to this server", this.width / 2, offset, 0xFFFFFF);
+ offset += 10;
+ this.drawCenteredString(this.fontRenderer, "The mods and versions listed below could not be found", this.width / 2, offset, 0xFFFFFF);
+ offset += 10;
+ this.drawCenteredString(this.fontRenderer, "They are required to play on this server", this.width / 2, offset, 0xFFFFFF);
+ offset += 5;
+ for (ArtifactVersion v : modsMissing.getModList())
+ {
+ offset += 10;
+ this.drawCenteredString(this.fontRenderer, String.format("%s : %s", v.getLabel(), v.getRangeString()), this.width / 2, offset, 0xEEEEEE);
+ }
+ super.drawScreen(par1, par2, par3);
+ }
+}
diff --git a/src/minecraft/cpw/mods/fml/client/GuiScrollingList.java b/src/minecraft/cpw/mods/fml/client/GuiScrollingList.java
new file mode 100644
index 0000000..bb2d611
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/client/GuiScrollingList.java
@@ -0,0 +1,400 @@
+package cpw.mods.fml.client;
+
+import java.util.List;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.GuiButton;
+import net.minecraft.client.renderer.Tessellator;
+
+import org.lwjgl.input.Mouse;
+import org.lwjgl.opengl.GL11;
+
+public abstract class GuiScrollingList
+{
+ private final Minecraft client;
+ protected final int listWidth;
+ protected final int listHeight;
+ protected final int top;
+ protected final int bottom;
+ private final int right;
+ protected final int left;
+ protected final int slotHeight;
+ private int scrollUpActionId;
+ private int scrollDownActionId;
+ protected int mouseX;
+ protected int mouseY;
+ private float initialMouseClickY = -2.0F;
+ private float scrollFactor;
+ private float scrollDistance;
+ private int selectedIndex = -1;
+ private long lastClickTime = 0L;
+ private boolean field_25123_p = true;
+ private boolean field_27262_q;
+ private int field_27261_r;
+
+ public GuiScrollingList(Minecraft client, int width, int height, int top, int bottom, int left, int entryHeight)
+ {
+ this.client = client;
+ this.listWidth = width;
+ this.listHeight = height;
+ this.top = top;
+ this.bottom = bottom;
+ this.slotHeight = entryHeight;
+ this.left = left;
+ this.right = width + this.left;
+ }
+
+ public void func_27258_a(boolean p_27258_1_)
+ {
+ this.field_25123_p = p_27258_1_;
+ }
+
+ protected void func_27259_a(boolean p_27259_1_, int p_27259_2_)
+ {
+ this.field_27262_q = p_27259_1_;
+ this.field_27261_r = p_27259_2_;
+
+ if (!p_27259_1_)
+ {
+ this.field_27261_r = 0;
+ }
+ }
+
+ protected abstract int getSize();
+
+ protected abstract void elementClicked(int index, boolean doubleClick);
+
+ protected abstract boolean isSelected(int index);
+
+ protected int getContentHeight()
+ {
+ return this.getSize() * this.slotHeight + this.field_27261_r;
+ }
+
+ protected abstract void drawBackground();
+
+ protected abstract void drawSlot(int var1, int var2, int var3, int var4, Tessellator var5);
+
+ protected void func_27260_a(int p_27260_1_, int p_27260_2_, Tessellator p_27260_3_) {}
+
+ protected void func_27255_a(int p_27255_1_, int p_27255_2_) {}
+
+ protected void func_27257_b(int p_27257_1_, int p_27257_2_) {}
+
+ public int func_27256_c(int p_27256_1_, int p_27256_2_)
+
+ {
+ int var3 = this.left + 1;
+ int var4 = this.left + this.listWidth - 7;
+ int var5 = p_27256_2_ - this.top - this.field_27261_r + (int)this.scrollDistance - 4;
+ int var6 = var5 / this.slotHeight;
+ return p_27256_1_ >= var3 && p_27256_1_ <= var4 && var6 >= 0 && var5 >= 0 && var6 < this.getSize() ? var6 : -1;
+ }
+
+ public void registerScrollButtons(List p_22240_1_, int p_22240_2_, int p_22240_3_)
+ {
+ this.scrollUpActionId = p_22240_2_;
+ this.scrollDownActionId = p_22240_3_;
+ }
+
+ private void applyScrollLimits()
+ {
+ int var1 = this.getContentHeight() - (this.bottom - this.top - 4);
+
+ if (var1 < 0)
+ {
+ var1 /= 2;
+ }
+
+ if (this.scrollDistance < 0.0F)
+ {
+ this.scrollDistance = 0.0F;
+ }
+
+ if (this.scrollDistance > (float)var1)
+ {
+ this.scrollDistance = (float)var1;
+ }
+ }
+
+ public void actionPerformed(GuiButton button)
+ {
+ if (button.enabled)
+ {
+ if (button.id == this.scrollUpActionId)
+ {
+ this.scrollDistance -= (float)(this.slotHeight * 2 / 3);
+ this.initialMouseClickY = -2.0F;
+ this.applyScrollLimits();
+ }
+ else if (button.id == this.scrollDownActionId)
+ {
+ this.scrollDistance += (float)(this.slotHeight * 2 / 3);
+ this.initialMouseClickY = -2.0F;
+ this.applyScrollLimits();
+ }
+ }
+ }
+
+ public void drawScreen(int mouseX, int mouseY, float p_22243_3_)
+ {
+ this.mouseX = mouseX;
+ this.mouseY = mouseY;
+ this.drawBackground();
+ int listLength = this.getSize();
+ int scrollBarXStart = this.left + this.listWidth - 6;
+ int scrollBarXEnd = scrollBarXStart + 6;
+ int boxLeft = this.left;
+ int boxRight = scrollBarXStart-1;
+ int var10;
+ int var11;
+ int var13;
+ int var19;
+
+ if (Mouse.isButtonDown(0))
+ {
+ if (this.initialMouseClickY == -1.0F)
+ {
+ boolean var7 = true;
+
+ if (mouseY >= this.top && mouseY <= this.bottom)
+ {
+ var10 = mouseY - this.top - this.field_27261_r + (int)this.scrollDistance - 4;
+ var11 = var10 / this.slotHeight;
+
+ if (mouseX >= boxLeft && mouseX <= boxRight && var11 >= 0 && var10 >= 0 && var11 < listLength)
+ {
+ boolean var12 = var11 == this.selectedIndex && System.currentTimeMillis() - this.lastClickTime < 250L;
+ this.elementClicked(var11, var12);
+ this.selectedIndex = var11;
+ this.lastClickTime = System.currentTimeMillis();
+ }
+ else if (mouseX >= boxLeft && mouseX <= boxRight && var10 < 0)
+ {
+ this.func_27255_a(mouseX - boxLeft, mouseY - this.top + (int)this.scrollDistance - 4);
+ var7 = false;
+ }
+
+ if (mouseX >= scrollBarXStart && mouseX <= scrollBarXEnd)
+ {
+ this.scrollFactor = -1.0F;
+ var19 = this.getContentHeight() - (this.bottom - this.top - 4);
+
+ if (var19 < 1)
+ {
+ var19 = 1;
+ }
+
+ var13 = (int)((float)((this.bottom - this.top) * (this.bottom - this.top)) / (float)this.getContentHeight());
+
+ if (var13 < 32)
+ {
+ var13 = 32;
+ }
+
+ if (var13 > this.bottom - this.top - 8)
+ {
+ var13 = this.bottom - this.top - 8;
+ }
+
+ this.scrollFactor /= (float)(this.bottom - this.top - var13) / (float)var19;
+ }
+ else
+ {
+ this.scrollFactor = 1.0F;
+ }
+
+ if (var7)
+ {
+ this.initialMouseClickY = (float)mouseY;
+ }
+ else
+ {
+ this.initialMouseClickY = -2.0F;
+ }
+ }
+ else
+ {
+ this.initialMouseClickY = -2.0F;
+ }
+ }
+ else if (this.initialMouseClickY >= 0.0F)
+ {
+ this.scrollDistance -= ((float)mouseY - this.initialMouseClickY) * this.scrollFactor;
+ this.initialMouseClickY = (float)mouseY;
+ }
+ }
+ else
+ {
+ while (Mouse.next())
+ {
+ int var16 = Mouse.getEventDWheel();
+
+ if (var16 != 0)
+ {
+ if (var16 > 0)
+ {
+ var16 = -1;
+ }
+ else if (var16 < 0)
+ {
+ var16 = 1;
+ }
+
+ this.scrollDistance += (float)(var16 * this.slotHeight / 2);
+ }
+ }
+
+ this.initialMouseClickY = -1.0F;
+ }
+
+ this.applyScrollLimits();
+ GL11.glDisable(GL11.GL_LIGHTING);
+ GL11.glDisable(GL11.GL_FOG);
+ Tessellator var18 = Tessellator.instance;
+ GL11.glBindTexture(GL11.GL_TEXTURE_2D, this.client.renderEngine.getTexture("/gui/background.png"));
+ GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
+ float var17 = 32.0F;
+ var18.startDrawingQuads();
+ var18.setColorOpaque_I(2105376);
+ var18.addVertexWithUV((double)this.left, (double)this.bottom, 0.0D, (double)((float)this.left / var17), (double)((float)(this.bottom + (int)this.scrollDistance) / var17));
+ var18.addVertexWithUV((double)this.right, (double)this.bottom, 0.0D, (double)((float)this.right / var17), (double)((float)(this.bottom + (int)this.scrollDistance) / var17));
+ var18.addVertexWithUV((double)this.right, (double)this.top, 0.0D, (double)((float)this.right / var17), (double)((float)(this.top + (int)this.scrollDistance) / var17));
+ var18.addVertexWithUV((double)this.left, (double)this.top, 0.0D, (double)((float)this.left / var17), (double)((float)(this.top + (int)this.scrollDistance) / var17));
+ var18.draw();
+// boxRight = this.listWidth / 2 - 92 - 16;
+ var10 = this.top + 4 - (int)this.scrollDistance;
+
+ if (this.field_27262_q)
+ {
+ this.func_27260_a(boxRight, var10, var18);
+ }
+
+ int var14;
+
+ for (var11 = 0; var11 < listLength; ++var11)
+ {
+ var19 = var10 + var11 * this.slotHeight + this.field_27261_r;
+ var13 = this.slotHeight - 4;
+
+ if (var19 <= this.bottom && var19 + var13 >= this.top)
+ {
+ if (this.field_25123_p && this.isSelected(var11))
+ {
+ var14 = boxLeft;
+ int var15 = boxRight;
+ GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
+ GL11.glDisable(GL11.GL_TEXTURE_2D);
+ var18.startDrawingQuads();
+ var18.setColorOpaque_I(8421504);
+ var18.addVertexWithUV((double)var14, (double)(var19 + var13 + 2), 0.0D, 0.0D, 1.0D);
+ var18.addVertexWithUV((double)var15, (double)(var19 + var13 + 2), 0.0D, 1.0D, 1.0D);
+ var18.addVertexWithUV((double)var15, (double)(var19 - 2), 0.0D, 1.0D, 0.0D);
+ var18.addVertexWithUV((double)var14, (double)(var19 - 2), 0.0D, 0.0D, 0.0D);
+ var18.setColorOpaque_I(0);
+ var18.addVertexWithUV((double)(var14 + 1), (double)(var19 + var13 + 1), 0.0D, 0.0D, 1.0D);
+ var18.addVertexWithUV((double)(var15 - 1), (double)(var19 + var13 + 1), 0.0D, 1.0D, 1.0D);
+ var18.addVertexWithUV((double)(var15 - 1), (double)(var19 - 1), 0.0D, 1.0D, 0.0D);
+ var18.addVertexWithUV((double)(var14 + 1), (double)(var19 - 1), 0.0D, 0.0D, 0.0D);
+ var18.draw();
+ GL11.glEnable(GL11.GL_TEXTURE_2D);
+ }
+
+ this.drawSlot(var11, boxRight, var19, var13, var18);
+ }
+ }
+
+ GL11.glDisable(GL11.GL_DEPTH_TEST);
+ byte var20 = 4;
+ this.overlayBackground(0, this.top, 255, 255);
+ this.overlayBackground(this.bottom, this.listHeight, 255, 255);
+ GL11.glEnable(GL11.GL_BLEND);
+ GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
+ GL11.glDisable(GL11.GL_ALPHA_TEST);
+ GL11.glShadeModel(GL11.GL_SMOOTH);
+ GL11.glDisable(GL11.GL_TEXTURE_2D);
+ var18.startDrawingQuads();
+ var18.setColorRGBA_I(0, 0);
+ var18.addVertexWithUV((double)this.left, (double)(this.top + var20), 0.0D, 0.0D, 1.0D);
+ var18.addVertexWithUV((double)this.right, (double)(this.top + var20), 0.0D, 1.0D, 1.0D);
+ var18.setColorRGBA_I(0, 255);
+ var18.addVertexWithUV((double)this.right, (double)this.top, 0.0D, 1.0D, 0.0D);
+ var18.addVertexWithUV((double)this.left, (double)this.top, 0.0D, 0.0D, 0.0D);
+ var18.draw();
+ var18.startDrawingQuads();
+ var18.setColorRGBA_I(0, 255);
+ var18.addVertexWithUV((double)this.left, (double)this.bottom, 0.0D, 0.0D, 1.0D);
+ var18.addVertexWithUV((double)this.right, (double)this.bottom, 0.0D, 1.0D, 1.0D);
+ var18.setColorRGBA_I(0, 0);
+ var18.addVertexWithUV((double)this.right, (double)(this.bottom - var20), 0.0D, 1.0D, 0.0D);
+ var18.addVertexWithUV((double)this.left, (double)(this.bottom - var20), 0.0D, 0.0D, 0.0D);
+ var18.draw();
+ var19 = this.getContentHeight() - (this.bottom - this.top - 4);
+
+ if (var19 > 0)
+ {
+ var13 = (this.bottom - this.top) * (this.bottom - this.top) / this.getContentHeight();
+
+ if (var13 < 32)
+ {
+ var13 = 32;
+ }
+
+ if (var13 > this.bottom - this.top - 8)
+ {
+ var13 = this.bottom - this.top - 8;
+ }
+
+ var14 = (int)this.scrollDistance * (this.bottom - this.top - var13) / var19 + this.top;
+
+ if (var14 < this.top)
+ {
+ var14 = this.top;
+ }
+
+ var18.startDrawingQuads();
+ var18.setColorRGBA_I(0, 255);
+ var18.addVertexWithUV((double)scrollBarXStart, (double)this.bottom, 0.0D, 0.0D, 1.0D);
+ var18.addVertexWithUV((double)scrollBarXEnd, (double)this.bottom, 0.0D, 1.0D, 1.0D);
+ var18.addVertexWithUV((double)scrollBarXEnd, (double)this.top, 0.0D, 1.0D, 0.0D);
+ var18.addVertexWithUV((double)scrollBarXStart, (double)this.top, 0.0D, 0.0D, 0.0D);
+ var18.draw();
+ var18.startDrawingQuads();
+ var18.setColorRGBA_I(8421504, 255);
+ var18.addVertexWithUV((double)scrollBarXStart, (double)(var14 + var13), 0.0D, 0.0D, 1.0D);
+ var18.addVertexWithUV((double)scrollBarXEnd, (double)(var14 + var13), 0.0D, 1.0D, 1.0D);
+ var18.addVertexWithUV((double)scrollBarXEnd, (double)var14, 0.0D, 1.0D, 0.0D);
+ var18.addVertexWithUV((double)scrollBarXStart, (double)var14, 0.0D, 0.0D, 0.0D);
+ var18.draw();
+ var18.startDrawingQuads();
+ var18.setColorRGBA_I(12632256, 255);
+ var18.addVertexWithUV((double)scrollBarXStart, (double)(var14 + var13 - 1), 0.0D, 0.0D, 1.0D);
+ var18.addVertexWithUV((double)(scrollBarXEnd - 1), (double)(var14 + var13 - 1), 0.0D, 1.0D, 1.0D);
+ var18.addVertexWithUV((double)(scrollBarXEnd - 1), (double)var14, 0.0D, 1.0D, 0.0D);
+ var18.addVertexWithUV((double)scrollBarXStart, (double)var14, 0.0D, 0.0D, 0.0D);
+ var18.draw();
+ }
+
+ this.func_27257_b(mouseX, mouseY);
+ GL11.glEnable(GL11.GL_TEXTURE_2D);
+ GL11.glShadeModel(GL11.GL_FLAT);
+ GL11.glEnable(GL11.GL_ALPHA_TEST);
+ GL11.glDisable(GL11.GL_BLEND);
+ }
+
+ private void overlayBackground(int p_22239_1_, int p_22239_2_, int p_22239_3_, int p_22239_4_)
+ {
+ Tessellator var5 = Tessellator.instance;
+ GL11.glBindTexture(GL11.GL_TEXTURE_2D, this.client.renderEngine.getTexture("/gui/background.png"));
+ GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
+ float var6 = 32.0F;
+ var5.startDrawingQuads();
+ var5.setColorRGBA_I(4210752, p_22239_4_);
+ var5.addVertexWithUV(0.0D, (double)p_22239_2_, 0.0D, 0.0D, (double)((float)p_22239_2_ / var6));
+ var5.addVertexWithUV((double)this.listWidth + 30, (double)p_22239_2_, 0.0D, (double)((float)(this.listWidth + 30) / var6), (double)((float)p_22239_2_ / var6));
+ var5.setColorRGBA_I(4210752, p_22239_3_);
+ var5.addVertexWithUV((double)this.listWidth + 30, (double)p_22239_1_, 0.0D, (double)((float)(this.listWidth + 30) / var6), (double)((float)p_22239_1_ / var6));
+ var5.addVertexWithUV(0.0D, (double)p_22239_1_, 0.0D, 0.0D, (double)((float)p_22239_1_ / var6));
+ var5.draw();
+ }
+}
diff --git a/src/minecraft/cpw/mods/fml/client/GuiSlotModList.java b/src/minecraft/cpw/mods/fml/client/GuiSlotModList.java
new file mode 100644
index 0000000..3b9dd0c
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/client/GuiSlotModList.java
@@ -0,0 +1,89 @@
+/*
+ * The FML Forge Mod Loader suite.
+ * Copyright (C) 2012 cpw
+ *
+ * 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
+ * Software Foundation; either version 2.1 of the License, or any later version.
+ *
+ * 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
+ * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * 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
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package cpw.mods.fml.client;
+
+import java.util.ArrayList;
+
+import net.minecraft.client.renderer.Tessellator;
+
+import cpw.mods.fml.common.Loader;
+import cpw.mods.fml.common.LoaderState.ModState;
+import cpw.mods.fml.common.ModContainer;
+
+/**
+ * @author cpw
+ *
+ */
+public class GuiSlotModList extends GuiScrollingList
+{
+ private GuiModList parent;
+ private ArrayList mods;
+
+ public GuiSlotModList(GuiModList parent, ArrayList mods, int listWidth)
+ {
+ super(parent.getMinecraftInstance(), listWidth, parent.height, 32, parent.height - 65 + 4, 10, 35);
+ this.parent=parent;
+ this.mods=mods;
+ }
+
+ @Override
+ protected int getSize()
+ {
+ return mods.size();
+ }
+
+ @Override
+ protected void elementClicked(int var1, boolean var2)
+ {
+ this.parent.selectModIndex(var1);
+ }
+
+ @Override
+ protected boolean isSelected(int var1)
+ {
+ return this.parent.modIndexSelected(var1);
+ }
+
+ @Override
+ protected void drawBackground()
+ {
+ this.parent.drawDefaultBackground();
+ }
+
+ @Override
+ protected int getContentHeight()
+ {
+ return (this.getSize()) * 35 + 1;
+ }
+
+ @Override
+ protected void drawSlot(int listIndex, int var2, int var3, int var4, Tessellator var5)
+ {
+ ModContainer mc=mods.get(listIndex);
+ if (Loader.instance().getModState(mc)==ModState.DISABLED)
+ {
+ this.parent.getFontRenderer().drawString(this.parent.getFontRenderer().trimStringToWidth(mc.getName(), listWidth - 10), this.left + 3 , var3 + 2, 0xFF2222);
+ this.parent.getFontRenderer().drawString(this.parent.getFontRenderer().trimStringToWidth(mc.getDisplayVersion(), listWidth - 10), this.left + 3 , var3 + 12, 0xFF2222);
+ this.parent.getFontRenderer().drawString(this.parent.getFontRenderer().trimStringToWidth("DISABLED", listWidth - 10), this.left + 3 , var3 + 22, 0xFF2222);
+ }
+ else
+ {
+ this.parent.getFontRenderer().drawString(this.parent.getFontRenderer().trimStringToWidth(mc.getName(), listWidth - 10), this.left + 3 , var3 + 2, 0xFFFFFF);
+ this.parent.getFontRenderer().drawString(this.parent.getFontRenderer().trimStringToWidth(mc.getDisplayVersion(), listWidth - 10), this.left + 3 , var3 + 12, 0xCCCCCC);
+ this.parent.getFontRenderer().drawString(this.parent.getFontRenderer().trimStringToWidth(mc.getMetadata() !=null ? mc.getMetadata().getChildModCountString() : "Metadata not found", listWidth - 10), this.left + 3 , var3 + 22, 0xCCCCCC);
+ }
+ }
+
+}
diff --git a/src/minecraft/cpw/mods/fml/client/GuiWrongMinecraft.java b/src/minecraft/cpw/mods/fml/client/GuiWrongMinecraft.java
new file mode 100644
index 0000000..ca55801
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/client/GuiWrongMinecraft.java
@@ -0,0 +1,42 @@
+package cpw.mods.fml.client;
+
+import net.minecraft.client.gui.GuiErrorScreen;
+import cpw.mods.fml.common.Loader;
+import cpw.mods.fml.common.WrongMinecraftVersionException;
+import cpw.mods.fml.common.versioning.ArtifactVersion;
+
+public class GuiWrongMinecraft extends GuiErrorScreen
+{
+ private WrongMinecraftVersionException wrongMC;
+ public GuiWrongMinecraft(WrongMinecraftVersionException wrongMC)
+ {
+ this.wrongMC = wrongMC;
+ }
+ @Override
+
+ /**
+ * Adds the buttons (and other controls) to the screen in question.
+ */
+ public void initGui()
+ {
+ super.initGui();
+ }
+ @Override
+
+ /**
+ * Draws the screen and all the components in it.
+ */
+ public void drawScreen(int par1, int par2, float par3)
+ {
+ this.drawDefaultBackground();
+ int offset = 75;
+ this.drawCenteredString(this.fontRenderer, "Forge Mod Loader has found a problem with your minecraft installation", this.width / 2, offset, 0xFFFFFF);
+ offset+=10;
+ this.drawCenteredString(this.fontRenderer, String.format("The mod listed below does not want to run in Minecraft version %s", Loader.instance().getMinecraftModContainer().getVersion()), this.width / 2, offset, 0xFFFFFF);
+ offset+=5;
+ offset+=10;
+ this.drawCenteredString(this.fontRenderer, String.format("%s (%s) wants Minecraft %s", wrongMC.mod.getName(), wrongMC.mod.getModId(), wrongMC.mod.acceptableMinecraftVersionRange()), this.width / 2, offset, 0xEEEEEE);
+ offset+=20;
+ this.drawCenteredString(this.fontRenderer, "The file 'ForgeModLoader-client-0.log' contains more information", this.width / 2, offset, 0xFFFFFF);
+ }
+}
diff --git a/src/minecraft/cpw/mods/fml/client/ITextureFX.java b/src/minecraft/cpw/mods/fml/client/ITextureFX.java
new file mode 100644
index 0000000..0dca285
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/client/ITextureFX.java
@@ -0,0 +1,31 @@
+/*
+ * The FML Forge Mod Loader suite.
+ * Copyright (C) 2012 cpw
+ *
+ * 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
+ * Software Foundation; either version 2.1 of the License, or any later version.
+ *
+ * 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
+ * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * 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
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package cpw.mods.fml.client;
+
+import java.awt.Dimension;
+
+import net.minecraft.client.renderer.RenderEngine;
+import net.minecraft.client.texturepacks.ITexturePack;
+
+public interface ITextureFX
+{
+ public void onTexturePackChanged(RenderEngine engine, ITexturePack texturepack, Dimension dimensions);
+
+ public void onTextureDimensionsUpdate(int width, int height);
+
+ public void setErrored(boolean errored);
+
+ public boolean getErrored();
+}
diff --git a/src/minecraft/cpw/mods/fml/client/OverrideInfo.java b/src/minecraft/cpw/mods/fml/client/OverrideInfo.java
new file mode 100644
index 0000000..f0d6fe2
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/client/OverrideInfo.java
@@ -0,0 +1,44 @@
+/*
+ * The FML Forge Mod Loader suite.
+ * Copyright (C) 2012 cpw
+ *
+ * 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
+ * Software Foundation; either version 2.1 of the License, or any later version.
+ *
+ * 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
+ * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * 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
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package cpw.mods.fml.client;
+
+import net.minecraft.client.renderer.texturefx.TextureFX;
+
+class OverrideInfo
+{
+ public String texture;
+ public String override;
+ public int index;
+ public int imageIndex;
+ public TextureFX textureFX;
+ public boolean added;
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ try {
+ OverrideInfo inf=(OverrideInfo) obj;
+ return index==inf.index && imageIndex==inf.imageIndex;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return index+imageIndex;
+ }
+}
\ No newline at end of file
diff --git a/src/minecraft/cpw/mods/fml/client/SpriteHelper.java b/src/minecraft/cpw/mods/fml/client/SpriteHelper.java
new file mode 100644
index 0000000..e65439e
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/client/SpriteHelper.java
@@ -0,0 +1,138 @@
+/*
+ * The FML Forge Mod Loader suite.
+ * Copyright (C) 2012 cpw
+ *
+ * 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
+ * Software Foundation; either version 2.1 of the License, or any later version.
+ *
+ * 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
+ * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * 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
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package cpw.mods.fml.client;
+
+import java.util.BitSet;
+import java.util.HashMap;
+import java.util.logging.Level;
+
+import cpw.mods.fml.common.FMLCommonHandler;
+import cpw.mods.fml.common.FMLLog;
+
+/**
+ * @author cpw
+ *
+ */
+public class SpriteHelper
+{
+ private static HashMap spriteInfo = new HashMap();
+
+ private static void initMCSpriteMaps() {
+ BitSet slots =
+ SpriteHelper.toBitSet(
+ "0000000000000000" +
+ "0000000000110000" +
+ "0000000000100000" +
+ "0000000001100000" +
+ "0000000000000000" +
+ "0000000000000000" +
+ "0000000000000000" +
+ "0000000000000000" +
+ "0000000000000000" +
+ "0000000000000000" +
+ "0000000000000000" +
+ "0000000000011111" +
+ "0000000000000000" +
+ "0000000001111100" +
+ "0000000001111000" +
+ "0000000000000000");
+ spriteInfo.put("/terrain.png", slots);
+
+ slots = SpriteHelper.toBitSet(
+ "0000000000000000" +
+ "0000000000000000" +
+ "0000000000000000" +
+ "0000000000000000" +
+ "0000000000000000" +
+ "0000000000000000" +
+ "0000000000000000" +
+ "0000000000000000" +
+ "0000000000000000" +
+ "0111110000000000" +
+ "1111111010000000" +
+ "0111111110000000" +
+ "0111111111111001" +
+ "1111111111111111" +
+ "0000011111111111" +
+ "0000000000000000");
+ spriteInfo.put("/gui/items.png", slots);
+ }
+ /**
+ * Register a sprite map for ModTextureStatic, to allow for other mods to override
+ * your sprite page.
+ *
+ *
+ */
+ public static void registerSpriteMapForFile(String file, String spriteMap) {
+ if (spriteInfo.size() == 0) {
+ initMCSpriteMaps();
+ }
+ if (spriteInfo.containsKey(file)) {
+ FMLCommonHandler.instance().getFMLLogger().finer(String.format("Duplicate attempt to register a sprite file %s for overriding -- ignoring",file));
+ return;
+ }
+ spriteInfo.put(file, toBitSet(spriteMap));
+ }
+
+ public static int getUniqueSpriteIndex(String path)
+ {
+ if (!spriteInfo.containsKey("/terrain.png"))
+ {
+ initMCSpriteMaps();
+ }
+
+ BitSet slots = spriteInfo.get(path);
+
+ if (slots == null)
+ {
+ Exception ex = new Exception(String.format("Invalid getUniqueSpriteIndex call for texture: %s", path));
+ FMLLog.log(Level.SEVERE, ex, "A critical error has been detected with sprite overrides");
+ FMLCommonHandler.instance().raiseException(ex,"Invalid request to getUniqueSpriteIndex",true);
+ }
+
+ int ret = getFreeSlot(slots);
+
+ if (ret == -1)
+ {
+ Exception ex = new Exception(String.format("No more sprite indicies left for: %s", path));
+ FMLLog.log(Level.SEVERE, ex, "There are no sprite indicies left for %s", path);
+ FMLCommonHandler.instance().raiseException(ex,"No more sprite indicies left", true);
+ }
+ return ret;
+ }
+
+ public static BitSet toBitSet(String data)
+ {
+ BitSet ret = new BitSet(data.length());
+ for (int x = 0; x < data.length(); x++)
+ {
+ ret.set(x, data.charAt(x) == '1');
+ }
+ return ret;
+ }
+
+ public static int getFreeSlot(BitSet slots)
+ {
+ int next=slots.nextSetBit(0);
+ slots.clear(next);
+ return next;
+ }
+
+ public static int freeSlotCount(String textureToOverride)
+ {
+ return spriteInfo.get(textureToOverride).cardinality();
+ }
+
+}
diff --git a/src/minecraft/cpw/mods/fml/client/TextureFXManager.java b/src/minecraft/cpw/mods/fml/client/TextureFXManager.java
new file mode 100644
index 0000000..57c9195
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/client/TextureFXManager.java
@@ -0,0 +1,319 @@
+package cpw.mods.fml.client;
+
+import static org.lwjgl.opengl.GL11.GL_TEXTURE_2D;
+import static org.lwjgl.opengl.GL11.GL_TEXTURE_BINDING_2D;
+
+import java.awt.Dimension;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+
+import javax.imageio.ImageIO;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.RenderEngine;
+import net.minecraft.client.renderer.texturefx.TextureFX;
+import net.minecraft.client.texturepacks.ITexturePack;
+import net.minecraft.src.ModTextureStatic;
+
+import org.lwjgl.opengl.GL11;
+
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
+
+import cpw.mods.fml.common.FMLCommonHandler;
+import cpw.mods.fml.common.FMLLog;
+import cpw.mods.fml.common.ModContainer;
+
+public class TextureFXManager
+{
+ private static final TextureFXManager INSTANCE = new TextureFXManager();
+
+ private class TextureProperties
+ {
+ private int textureId;
+ private Dimension dim;
+ }
+
+ private Map textureProperties = Maps.newHashMap();
+ private Multimap overrideInfo = ArrayListMultimap.create();
+ private HashSet animationSet = new HashSet();
+
+ private List addedTextureFX = new ArrayList();
+
+ private Minecraft client;
+
+ void setClient(Minecraft client)
+ {
+ this.client = client;
+ }
+
+ public boolean onUpdateTextureEffect(TextureFX effect)
+ {
+ ITextureFX ifx = (effect instanceof ITextureFX ? ((ITextureFX)effect) : null);
+
+ if (ifx != null && ifx.getErrored())
+ {
+ return false;
+ }
+
+ String name = effect.getClass().getSimpleName();
+ client.mcProfiler.startSection(name);
+ try
+ {
+ if (!FMLClientHandler.instance().hasOptifine())
+ {
+ effect.onTick();
+ }
+ }
+ catch (Exception e)
+ {
+ FMLLog.warning("Texture FX %s has failed to animate. Likely caused by a texture pack change that they did not respond correctly to", name);
+ if (ifx != null)
+ {
+ ifx.setErrored(true);
+ }
+ client.mcProfiler.endSection();
+ return false;
+ }
+ client.mcProfiler.endSection();
+
+ if (ifx != null)
+ {
+ Dimension dim = getTextureDimensions(effect);
+ int target = ((dim.width >> 4) * (dim.height >> 4)) << 2;
+ if (effect.imageData.length != target)
+ {
+ FMLLog.warning("Detected a texture FX sizing discrepancy in %s (%d, %d)", name, effect.imageData.length, target);
+ ifx.setErrored(true);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ //Quick and dirty image scaling, no smoothing or fanciness, meant for speed as it will be called every tick.
+ public void scaleTextureFXData(byte[] data, ByteBuffer buf, int target, int length)
+ {
+ int sWidth = (int)Math.sqrt(data.length / 4);
+ int factor = target / sWidth;
+ byte[] tmp = new byte[4];
+
+ buf.clear();
+
+ if (factor > 1)
+ {
+ for (int y = 0; y < sWidth; y++)
+ {
+ int sRowOff = sWidth * y;
+ int tRowOff = target * y * factor;
+ for (int x = 0; x < sWidth; x++)
+ {
+ int sPos = (x + sRowOff) * 4;
+ tmp[0] = data[sPos + 0];
+ tmp[1] = data[sPos + 1];
+ tmp[2] = data[sPos + 2];
+ tmp[3] = data[sPos + 3];
+
+ int tPosTop = (x * factor) + tRowOff;
+ for (int y2 = 0; y2 < factor; y2++)
+ {
+ buf.position((tPosTop + (y2 * target)) * 4);
+ for (int x2 = 0; x2 < factor; x2++)
+ {
+ buf.put(tmp);
+ }
+ }
+ }
+ }
+ }
+
+ buf.position(0).limit(length);
+ }
+
+ public void onPreRegisterEffect(TextureFX effect)
+ {
+ Dimension dim = getTextureDimensions(effect);
+ if (effect instanceof ITextureFX)
+ {
+ ((ITextureFX)effect).onTextureDimensionsUpdate(dim.width, dim.height);
+ }
+ }
+
+
+ public int getEffectTexture(TextureFX effect)
+ {
+ Integer id = effectTextures.get(effect);
+ if (id != null)
+ {
+ return id;
+ }
+
+ int old = GL11.glGetInteger(GL_TEXTURE_BINDING_2D);
+ effect.bindImage(client.renderEngine);
+ id = GL11.glGetInteger(GL_TEXTURE_BINDING_2D);
+ GL11.glBindTexture(GL_TEXTURE_2D, old);
+ effectTextures.put(effect, id);
+ effect.textureId = id;
+ return id;
+ }
+
+ public void onTexturePackChange(RenderEngine engine, ITexturePack texturepack, List effects)
+ {
+ pruneOldTextureFX(texturepack, effects);
+
+ for (TextureFX tex : effects)
+ {
+ if (tex instanceof ITextureFX)
+ {
+ ((ITextureFX)tex).onTexturePackChanged(engine, texturepack, getTextureDimensions(tex));
+ }
+ }
+
+ loadTextures(texturepack);
+ }
+
+ private HashMap textureDims = new HashMap();
+ private IdentityHashMap effectTextures = new IdentityHashMap();
+ private ITexturePack earlyTexturePack;
+ public void setTextureDimensions(int id, int width, int height, List effects)
+ {
+ Dimension dim = new Dimension(width, height);
+ textureDims.put(id, dim);
+
+ for (TextureFX tex : effects)
+ {
+ if (getEffectTexture(tex) == id && tex instanceof ITextureFX)
+ {
+ ((ITextureFX)tex).onTextureDimensionsUpdate(width, height);
+ }
+ }
+ }
+
+ public Dimension getTextureDimensions(TextureFX effect)
+ {
+ return getTextureDimensions(getEffectTexture(effect));
+ }
+
+ public Dimension getTextureDimensions(int id)
+ {
+ return textureDims.get(id);
+ }
+
+ public void addAnimation(TextureFX anim)
+ {
+ OverrideInfo info=new OverrideInfo();
+ info.index=anim.iconIndex;
+ info.imageIndex=anim.tileImage;
+ info.textureFX=anim;
+ if (animationSet.contains(info)) {
+ animationSet.remove(info);
+ }
+ animationSet.add(info);
+ }
+
+
+ public void loadTextures(ITexturePack texturePack)
+ {
+ registerTextureOverrides(client.renderEngine);
+ }
+
+
+ public void registerTextureOverrides(RenderEngine renderer) {
+ for (OverrideInfo animationOverride : animationSet) {
+ renderer.registerTextureFX(animationOverride.textureFX);
+ addedTextureFX.add(animationOverride.textureFX);
+ FMLCommonHandler.instance().getFMLLogger().finer(String.format("Registered texture override %d (%d) on %s (%d)", animationOverride.index, animationOverride.textureFX.iconIndex, animationOverride.textureFX.getClass().getSimpleName(), animationOverride.textureFX.tileImage));
+ }
+
+ for (String fileToOverride : overrideInfo.keySet()) {
+ for (OverrideInfo override : overrideInfo.get(fileToOverride)) {
+ try
+ {
+ BufferedImage image=loadImageFromTexturePack(renderer, override.override);
+ ModTextureStatic mts=new ModTextureStatic(override.index, 1, override.texture, image);
+ renderer.registerTextureFX(mts);
+ addedTextureFX.add(mts);
+ FMLCommonHandler.instance().getFMLLogger().finer(String.format("Registered texture override %d (%d) on %s (%d)", override.index, mts.iconIndex, override.texture, mts.tileImage));
+ }
+ catch (IOException e)
+ {
+ FMLCommonHandler.instance().getFMLLogger().throwing("FMLClientHandler", "registerTextureOverrides", e);
+ }
+ }
+ }
+ }
+
+ protected void registerAnimatedTexturesFor(ModContainer mod)
+ {
+ }
+
+ public void onEarlyTexturePackLoad(ITexturePack fallback)
+ {
+ if (client==null) {
+ // We're far too early- let's wait
+ this.earlyTexturePack = fallback;
+ } else {
+ loadTextures(fallback);
+ }
+ }
+
+
+ public void pruneOldTextureFX(ITexturePack var1, List effects)
+ {
+ ListIterator li = addedTextureFX.listIterator();
+ while (li.hasNext())
+ {
+ TextureFX tex = li.next();
+ if (tex instanceof FMLTextureFX)
+ {
+ if (((FMLTextureFX)tex).unregister(client.renderEngine, effects))
+ {
+ li.remove();
+ }
+ }
+ else
+ {
+ effects.remove(tex);
+ li.remove();
+ }
+ }
+ }
+ public void addNewTextureOverride(String textureToOverride, String overridingTexturePath, int location) {
+ OverrideInfo info = new OverrideInfo();
+ info.index = location;
+ info.override = overridingTexturePath;
+ info.texture = textureToOverride;
+ overrideInfo.put(textureToOverride, info);
+ FMLLog.fine("Overriding %s @ %d with %s. %d slots remaining",textureToOverride, location, overridingTexturePath, SpriteHelper.freeSlotCount(textureToOverride));
+ }
+
+ public BufferedImage loadImageFromTexturePack(RenderEngine renderEngine, String path) throws IOException
+ {
+ InputStream image=client.texturePackList.getSelectedTexturePack().getResourceAsStream(path);
+ if (image==null) {
+ throw new RuntimeException(String.format("The requested image path %s is not found",path));
+ }
+ BufferedImage result=ImageIO.read(image);
+ if (result==null)
+ {
+ throw new RuntimeException(String.format("The requested image path %s appears to be corrupted",path));
+ }
+ return result;
+ }
+
+ public static TextureFXManager instance()
+ {
+ return INSTANCE;
+ }
+
+}
diff --git a/src/minecraft/cpw/mods/fml/client/modloader/ModLoaderBlockRendererHandler.java b/src/minecraft/cpw/mods/fml/client/modloader/ModLoaderBlockRendererHandler.java
new file mode 100644
index 0000000..83313a4
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/client/modloader/ModLoaderBlockRendererHandler.java
@@ -0,0 +1,83 @@
+/*
+ * The FML Forge Mod Loader suite.
+ * Copyright (C) 2012 cpw
+ *
+ * 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
+ * Software Foundation; either version 2.1 of the License, or any later version.
+ *
+ * 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
+ * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * 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
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package cpw.mods.fml.client.modloader;
+
+import net.minecraft.block.Block;
+import net.minecraft.client.renderer.RenderBlocks;
+import net.minecraft.src.BaseMod;
+import net.minecraft.world.IBlockAccess;
+import cpw.mods.fml.client.registry.ISimpleBlockRenderingHandler;
+
+/**
+ * @author cpw
+ *
+ */
+public class ModLoaderBlockRendererHandler implements ISimpleBlockRenderingHandler
+{
+ private int renderId;
+ private boolean render3dInInventory;
+ private BaseMod mod;
+
+ /**
+ * @param mod
+ *
+ */
+ public ModLoaderBlockRendererHandler(int renderId, boolean render3dInInventory, BaseMod mod)
+ {
+ this.renderId=renderId;
+ this.render3dInInventory=render3dInInventory;
+ this.mod=mod;
+ }
+
+ @Override
+ public int getRenderId()
+ {
+ return renderId;
+ }
+
+ @Override
+ public boolean shouldRender3DInInventory()
+ {
+ return render3dInInventory;
+ }
+
+ /**
+ * @param world
+ * @param x
+ * @param y
+ * @param z
+ * @param block
+ * @param modelId
+ * @param renderer
+ */
+ @Override
+ public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks renderer)
+ {
+ return mod.renderWorldBlock(renderer, world, x, y, z, block, modelId);
+ }
+
+ /**
+ * @param block
+ * @param metadata
+ * @param modelID
+ * @param renderer
+ */
+ @Override
+ public void renderInventoryBlock(Block block, int metadata, int modelID, RenderBlocks renderer)
+ {
+ mod.renderInvBlock(renderer, block, metadata, modelID);
+ }
+
+}
diff --git a/src/minecraft/cpw/mods/fml/client/modloader/ModLoaderClientHelper.java b/src/minecraft/cpw/mods/fml/client/modloader/ModLoaderClientHelper.java
new file mode 100644
index 0000000..52dc590
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/client/modloader/ModLoaderClientHelper.java
@@ -0,0 +1,178 @@
+package cpw.mods.fml.client.modloader;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.logging.Level;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.network.INetworkManager;
+import net.minecraft.network.packet.NetHandler;
+import net.minecraft.network.packet.Packet250CustomPayload;
+import net.minecraft.src.BaseMod;
+import net.minecraft.client.*;
+import net.minecraft.client.entity.EntityClientPlayerMP;
+import net.minecraft.client.multiplayer.NetClientHandler;
+import net.minecraft.client.renderer.entity.Render;
+import net.minecraft.client.renderer.entity.RenderManager;
+import net.minecraft.client.settings.KeyBinding;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.player.EntityPlayer;
+
+import com.google.common.base.Equivalences;
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Iterators;
+import com.google.common.collect.MapDifference;
+import com.google.common.collect.MapDifference.ValueDifference;
+import com.google.common.collect.MapMaker;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Multimaps;
+
+import cpw.mods.fml.client.FMLClientHandler;
+import cpw.mods.fml.client.registry.KeyBindingRegistry;
+import cpw.mods.fml.client.registry.RenderingRegistry;
+import cpw.mods.fml.common.FMLLog;
+import cpw.mods.fml.common.Loader;
+import cpw.mods.fml.common.modloader.BaseModProxy;
+import cpw.mods.fml.common.modloader.IModLoaderSidedHelper;
+import cpw.mods.fml.common.modloader.ModLoaderHelper;
+import cpw.mods.fml.common.modloader.ModLoaderModContainer;
+import cpw.mods.fml.common.network.EntitySpawnPacket;
+import cpw.mods.fml.common.registry.EntityRegistry.EntityRegistration;
+
+public class ModLoaderClientHelper implements IModLoaderSidedHelper
+{
+ public static int obtainBlockModelIdFor(BaseMod mod, boolean inventoryRenderer)
+ {
+ int renderId=RenderingRegistry.getNextAvailableRenderId();
+ ModLoaderBlockRendererHandler bri=new ModLoaderBlockRendererHandler(renderId, inventoryRenderer, mod);
+ RenderingRegistry.registerBlockHandler(bri);
+ return renderId;
+ }
+
+
+ public static void handleFinishLoadingFor(ModLoaderModContainer mc, Minecraft game)
+ {
+ FMLLog.finer("Handling post startup activities for ModLoader mod %s", mc.getModId());
+ BaseMod mod = (BaseMod) mc.getMod();
+
+ Map, Render> renderers = Maps.newHashMap(RenderManager.instance.entityRenderMap);
+
+ try
+ {
+ FMLLog.finest("Requesting renderers from basemod %s", mc.getModId());
+ mod.addRenderer(renderers);
+ FMLLog.finest("Received %d renderers from basemod %s", renderers.size(), mc.getModId());
+ }
+ catch (Exception e)
+ {
+ FMLLog.log(Level.SEVERE, e, "A severe problem was detected with the mod %s during the addRenderer call. Continuing, but expect odd results", mc.getModId());
+ }
+
+ MapDifference, Render> difference = Maps.difference(RenderManager.instance.entityRenderMap, renderers, Equivalences.identity());
+
+ for ( Entry, Render> e : difference.entriesOnlyOnLeft().entrySet())
+ {
+ FMLLog.warning("The mod %s attempted to remove an entity renderer %s from the entity map. This will be ignored.", mc.getModId(), e.getKey().getName());
+ }
+
+ for (Entry, Render> e : difference.entriesOnlyOnRight().entrySet())
+ {
+ FMLLog.finest("Registering ModLoader entity renderer %s as instance of %s", e.getKey().getName(), e.getValue().getClass().getName());
+ RenderingRegistry.registerEntityRenderingHandler(e.getKey(), e.getValue());
+ }
+
+ for (Entry, ValueDifference> e : difference.entriesDiffering().entrySet())
+ {
+ FMLLog.finest("Registering ModLoader entity rendering override for %s as instance of %s", e.getKey().getName(), e.getValue().rightValue().getClass().getName());
+ RenderingRegistry.registerEntityRenderingHandler(e.getKey(), e.getValue().rightValue());
+ }
+
+ try
+ {
+ mod.registerAnimation(game);
+ }
+ catch (Exception e)
+ {
+ FMLLog.log(Level.SEVERE, e, "A severe problem was detected with the mod %s during the registerAnimation call. Continuing, but expect odd results", mc.getModId());
+ }
+ }
+
+ public ModLoaderClientHelper(Minecraft client)
+ {
+ this.client = client;
+ ModLoaderHelper.sidedHelper = this;
+ keyBindingContainers = Multimaps.newMultimap(Maps.>newHashMap(), new Supplier>()
+ {
+ @Override
+ public Collection get()
+ {
+ return Collections.singleton(new ModLoaderKeyBindingHandler());
+ }
+ });
+ }
+
+ private Minecraft client;
+ private static Multimap keyBindingContainers;
+
+ @Override
+ public void finishModLoading(ModLoaderModContainer mc)
+ {
+ handleFinishLoadingFor(mc, client);
+ }
+
+
+ public static void registerKeyBinding(BaseModProxy mod, KeyBinding keyHandler, boolean allowRepeat)
+ {
+ ModLoaderModContainer mlmc = (ModLoaderModContainer) Loader.instance().activeModContainer();
+ ModLoaderKeyBindingHandler handler = Iterables.getOnlyElement(keyBindingContainers.get(mlmc));
+ handler.setModContainer(mlmc);
+ handler.addKeyBinding(keyHandler, allowRepeat);
+ KeyBindingRegistry.registerKeyBinding(handler);
+ }
+
+
+ @Override
+ public Object getClientGui(BaseModProxy mod, EntityPlayer player, int ID, int x, int y, int z)
+ {
+ return ((net.minecraft.src.BaseMod)mod).getContainerGUI((EntityClientPlayerMP) player, ID, x, y, z);
+ }
+
+
+ @Override
+ public Entity spawnEntity(BaseModProxy mod, EntitySpawnPacket input, EntityRegistration er)
+ {
+ return ((net.minecraft.src.BaseMod)mod).spawnEntity(er.getModEntityId(), client.theWorld, input.scaledX, input.scaledY, input.scaledZ);
+ }
+
+
+ @Override
+ public void sendClientPacket(BaseModProxy mod, Packet250CustomPayload packet)
+ {
+ ((net.minecraft.src.BaseMod)mod).clientCustomPayload(client.thePlayer.sendQueue, packet);
+ }
+
+ private Map managerLookups = new MapMaker().weakKeys().weakValues().makeMap();
+ @Override
+ public void clientConnectionOpened(NetHandler netClientHandler, INetworkManager manager, BaseModProxy mod)
+ {
+ managerLookups.put(manager, netClientHandler);
+ ((BaseMod)mod).clientConnect((NetClientHandler)netClientHandler);
+ }
+
+
+ @Override
+ public boolean clientConnectionClosed(INetworkManager manager, BaseModProxy mod)
+ {
+ if (managerLookups.containsKey(manager))
+ {
+ ((BaseMod)mod).clientDisconnect((NetClientHandler) managerLookups.get(manager));
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/src/minecraft/cpw/mods/fml/client/modloader/ModLoaderKeyBindingHandler.java b/src/minecraft/cpw/mods/fml/client/modloader/ModLoaderKeyBindingHandler.java
new file mode 100644
index 0000000..c4093db
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/client/modloader/ModLoaderKeyBindingHandler.java
@@ -0,0 +1,112 @@
+/*
+ * The FML Forge Mod Loader suite.
+ * Copyright (C) 2012 cpw
+ *
+ * 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
+ * Software Foundation; either version 2.1 of the License, or any later version.
+ *
+ * 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
+ * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * 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
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package cpw.mods.fml.client.modloader;
+
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.List;
+
+import org.lwjgl.input.Mouse;
+
+import com.google.common.collect.ObjectArrays;
+import com.google.common.primitives.Booleans;
+
+import net.minecraft.client.settings.KeyBinding;
+import cpw.mods.fml.client.registry.KeyBindingRegistry;
+import cpw.mods.fml.common.TickType;
+import cpw.mods.fml.common.modloader.ModLoaderModContainer;
+
+/**
+ * @author cpw
+ *
+ */
+public class ModLoaderKeyBindingHandler extends KeyBindingRegistry.KeyHandler
+{
+ private ModLoaderModContainer modContainer;
+ private List helper;
+ private boolean[] active = new boolean[0];
+ private boolean[] mlRepeats = new boolean[0];
+ private boolean[] armed = new boolean[0];
+
+ public ModLoaderKeyBindingHandler()
+ {
+ super(new KeyBinding[0], new boolean[0]);
+ }
+
+ void setModContainer(ModLoaderModContainer modContainer)
+ {
+ this.modContainer = modContainer;
+ }
+
+ public void fireKeyEvent(KeyBinding kb)
+ {
+ ((net.minecraft.src.BaseMod)modContainer.getMod()).keyboardEvent(kb);
+ }
+
+ @Override
+ public void keyDown(EnumSet type, KeyBinding kb, boolean end, boolean repeats)
+ {
+ if (!end)
+ {
+ return;
+ }
+ int idx = helper.indexOf(kb);
+ if (type.contains(TickType.CLIENT))
+ {
+ armed[idx] = true;
+ }
+ if (armed[idx] && type.contains(TickType.RENDER) && (!active[idx] || mlRepeats[idx]))
+ {
+ fireKeyEvent(kb);
+ active[idx] = true;
+ armed[idx] = false;
+ }
+ }
+
+ @Override
+ public void keyUp(EnumSet type, KeyBinding kb, boolean end)
+ {
+ if (!end)
+ {
+ return;
+ }
+ int idx = helper.indexOf(kb);
+ active[idx] = false;
+ }
+
+ @Override
+ public EnumSet ticks()
+ {
+ return EnumSet.of(TickType.CLIENT, TickType.RENDER);
+ }
+
+ @Override
+ public String getLabel()
+ {
+ return modContainer.getModId() +" KB "+keyBindings[0].keyCode;
+ }
+
+ void addKeyBinding(KeyBinding binding, boolean repeats)
+ {
+ this.keyBindings = ObjectArrays.concat(this.keyBindings, binding);
+ this.repeatings = new boolean[this.keyBindings.length];
+ Arrays.fill(this.repeatings, true);
+ this.active = new boolean[this.keyBindings.length];
+ this.armed = new boolean[this.keyBindings.length];
+ this.mlRepeats = Booleans.concat(this.mlRepeats, new boolean[] { repeats });
+ this.keyDown = new boolean[this.keyBindings.length];
+ this.helper = Arrays.asList(this.keyBindings);
+ }
+}
diff --git a/src/minecraft/cpw/mods/fml/client/registry/ClientRegistry.java b/src/minecraft/cpw/mods/fml/client/registry/ClientRegistry.java
new file mode 100644
index 0000000..36e8def
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/client/registry/ClientRegistry.java
@@ -0,0 +1,29 @@
+package cpw.mods.fml.client.registry;
+
+import cpw.mods.fml.common.registry.GameRegistry;
+import net.minecraft.client.renderer.tileentity.TileEntityRenderer;
+import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer;
+import net.minecraft.tileentity.TileEntity;
+
+public class ClientRegistry
+{
+ /**
+ *
+ * Utility method for registering a tile entity and it's renderer at once - generally you should register them separately
+ *
+ * @param tileEntityClass
+ * @param id
+ * @param specialRenderer
+ */
+ public static void registerTileEntity(Class extends TileEntity > tileEntityClass, String id, TileEntitySpecialRenderer specialRenderer)
+ {
+ GameRegistry.registerTileEntity(tileEntityClass, id);
+ bindTileEntitySpecialRenderer(tileEntityClass, specialRenderer);
+ }
+
+ public static void bindTileEntitySpecialRenderer(Class extends TileEntity> tileEntityClass, TileEntitySpecialRenderer specialRenderer)
+ {
+ TileEntityRenderer.instance.specialRendererMap.put(tileEntityClass, specialRenderer);
+ specialRenderer.setTileEntityRenderer(TileEntityRenderer.instance);
+ }
+}
diff --git a/src/minecraft/cpw/mods/fml/client/registry/ISimpleBlockRenderingHandler.java b/src/minecraft/cpw/mods/fml/client/registry/ISimpleBlockRenderingHandler.java
new file mode 100644
index 0000000..d8e76d2
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/client/registry/ISimpleBlockRenderingHandler.java
@@ -0,0 +1,16 @@
+package cpw.mods.fml.client.registry;
+
+import net.minecraft.block.Block;
+import net.minecraft.client.renderer.RenderBlocks;
+import net.minecraft.world.IBlockAccess;
+
+public interface ISimpleBlockRenderingHandler
+{
+ public abstract void renderInventoryBlock(Block block, int metadata, int modelID, RenderBlocks renderer);
+
+ public abstract boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks renderer);
+
+ public abstract boolean shouldRender3DInInventory();
+
+ public abstract int getRenderId();
+}
diff --git a/src/minecraft/cpw/mods/fml/client/registry/KeyBindingRegistry.java b/src/minecraft/cpw/mods/fml/client/registry/KeyBindingRegistry.java
new file mode 100644
index 0000000..f3c55bf
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/client/registry/KeyBindingRegistry.java
@@ -0,0 +1,187 @@
+package cpw.mods.fml.client.registry;
+
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.Set;
+
+import net.minecraft.client.settings.GameSettings;
+import net.minecraft.client.settings.KeyBinding;
+
+import org.lwjgl.input.Keyboard;
+import org.lwjgl.input.Mouse;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
+import cpw.mods.fml.common.ITickHandler;
+import cpw.mods.fml.common.TickType;
+import cpw.mods.fml.common.registry.TickRegistry;
+import cpw.mods.fml.relauncher.Side;
+
+public class KeyBindingRegistry
+{
+ /**
+ * Register a KeyHandler to the game. This handler will be called on certain tick events
+ * if any of its key is inactive or has recently changed state
+ *
+ * @param handler
+ */
+ public static void registerKeyBinding(KeyHandler handler) {
+ instance().keyHandlers.add(handler);
+ if (!handler.isDummy)
+ {
+ TickRegistry.registerTickHandler(handler, Side.CLIENT);
+ }
+ }
+
+
+ /**
+ * Extend this class to register a KeyBinding and recieve callback
+ * when the key binding is triggered
+ *
+ * @author cpw
+ *
+ */
+ public static abstract class KeyHandler implements ITickHandler
+ {
+ protected KeyBinding[] keyBindings;
+ protected boolean[] keyDown;
+ protected boolean[] repeatings;
+ private boolean isDummy;
+
+ /**
+ * Pass an array of keybindings and a repeat flag for each one
+ *
+ * @param keyBindings
+ * @param repeatings
+ */
+ public KeyHandler(KeyBinding[] keyBindings, boolean[] repeatings)
+ {
+ assert keyBindings.length == repeatings.length : "You need to pass two arrays of identical length";
+ this.keyBindings = keyBindings;
+ this.repeatings = repeatings;
+ this.keyDown = new boolean[keyBindings.length];
+ }
+
+
+ /**
+ * Register the keys into the system. You will do your own keyboard management elsewhere. No events will fire
+ * if you use this method
+ *
+ * @param keyBindings
+ */
+ public KeyHandler(KeyBinding[] keyBindings)
+ {
+ this.keyBindings = keyBindings;
+ this.isDummy = true;
+ }
+
+ public KeyBinding[] getKeyBindings()
+ {
+ return this.keyBindings;
+ }
+
+ /**
+ * Not to be overridden - KeyBindings are tickhandlers under the covers
+ */
+ @Override
+ public final void tickStart(EnumSet type, Object... tickData)
+ {
+ keyTick(type, false);
+ }
+
+ /**
+ * Not to be overridden - KeyBindings are tickhandlers under the covers
+ */
+ @Override
+ public final void tickEnd(EnumSet type, Object... tickData)
+ {
+ keyTick(type, true);
+ }
+
+ private void keyTick(EnumSet type, boolean tickEnd)
+ {
+ for (int i = 0; i < keyBindings.length; i++)
+ {
+ KeyBinding keyBinding = keyBindings[i];
+ int keyCode = keyBinding.keyCode;
+ boolean state = (keyCode < 0 ? Mouse.isButtonDown(keyCode + 100) : Keyboard.isKeyDown(keyCode));
+ if (state != keyDown[i] || (state && repeatings[i]))
+ {
+ if (state)
+ {
+ keyDown(type, keyBinding, tickEnd, state!=keyDown[i]);
+ }
+ else
+ {
+ keyUp(type, keyBinding, tickEnd);
+ }
+ if (tickEnd)
+ {
+ keyDown[i] = state;
+ }
+ }
+
+ }
+ }
+
+ /**
+ * Called when the key is first in the down position on any tick from the {@link #ticks()}
+ * set. Will be called subsequently with isRepeat set to true
+ *
+ * @see #keyUp(EnumSet, KeyBinding, boolean)
+ *
+ * @param types the type(s) of tick that fired when this key was first down
+ * @param tickEnd was it an end or start tick which fired the key
+ * @param isRepeat is it a repeat key event
+ */
+ public abstract void keyDown(EnumSet types, KeyBinding kb, boolean tickEnd, boolean isRepeat);
+ /**
+ * Fired once when the key changes state from down to up
+ *
+ * @see #keyDown(EnumSet, KeyBinding, boolean, boolean)
+ *
+ * @param types the type(s) of tick that fired when this key was first down
+ * @param tickEnd was it an end or start tick which fired the key
+ */
+ public abstract void keyUp(EnumSet types, KeyBinding kb, boolean tickEnd);
+
+
+ /**
+ * This is the list of ticks for which the key binding should trigger. The only
+ * valid ticks are client side ticks, obviously.
+ *
+ * @see cpw.mods.fml.common.ITickHandler#ticks()
+ */
+ public abstract EnumSet ticks();
+ }
+
+ private static final KeyBindingRegistry INSTANCE = new KeyBindingRegistry();
+
+ private Set keyHandlers = Sets.newLinkedHashSet();
+
+ @Deprecated
+ public static KeyBindingRegistry instance()
+ {
+ return INSTANCE;
+ }
+
+
+ public void uploadKeyBindingsToGame(GameSettings settings)
+ {
+ ArrayList harvestedBindings = Lists.newArrayList();
+ for (KeyHandler key : keyHandlers)
+ {
+ for (KeyBinding kb : key.keyBindings)
+ {
+ harvestedBindings.add(kb);
+ }
+ }
+ KeyBinding[] modKeyBindings = harvestedBindings.toArray(new KeyBinding[harvestedBindings.size()]);
+ KeyBinding[] allKeys = new KeyBinding[settings.keyBindings.length + modKeyBindings.length];
+ System.arraycopy(settings.keyBindings, 0, allKeys, 0, settings.keyBindings.length);
+ System.arraycopy(modKeyBindings, 0, allKeys, settings.keyBindings.length, modKeyBindings.length);
+ settings.keyBindings = allKeys;
+ settings.loadOptions();
+ }
+}
diff --git a/src/minecraft/cpw/mods/fml/client/registry/RenderingRegistry.java b/src/minecraft/cpw/mods/fml/client/registry/RenderingRegistry.java
new file mode 100644
index 0000000..5406f60
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/client/registry/RenderingRegistry.java
@@ -0,0 +1,166 @@
+package cpw.mods.fml.client.registry;
+
+import java.util.List;
+import java.util.Map;
+
+import net.minecraft.block.Block;
+import net.minecraft.client.renderer.RenderBlocks;
+import net.minecraft.client.renderer.entity.*;
+import net.minecraft.entity.Entity;
+import net.minecraft.world.IBlockAccess;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.ObjectArrays;
+
+import cpw.mods.fml.client.SpriteHelper;
+import cpw.mods.fml.client.TextureFXManager;
+
+/**
+ * @author cpw
+ *
+ */
+public class RenderingRegistry
+{
+ private static final RenderingRegistry INSTANCE = new RenderingRegistry();
+
+ private int nextRenderId = 36;
+
+ private Map blockRenderers = Maps.newHashMap();
+
+ private List entityRenderers = Lists.newArrayList();
+
+ /**
+ * Add a new armour prefix to the RenderPlayer
+ *
+ * @param armor
+ */
+ public static int addNewArmourRendererPrefix(String armor)
+ {
+ RenderPlayer.armorFilenamePrefix = ObjectArrays.concat(RenderPlayer.armorFilenamePrefix, armor);
+ RenderBiped.bipedArmorFilenamePrefix = RenderPlayer.armorFilenamePrefix;
+ return RenderPlayer.armorFilenamePrefix.length - 1;
+ }
+
+ /**
+ * Register an entity rendering handler. This will, after mod initialization, be inserted into the main
+ * render map for entities
+ *
+ * @param entityClass
+ * @param renderer
+ */
+ public static void registerEntityRenderingHandler(Class extends Entity> entityClass, Render renderer)
+ {
+ instance().entityRenderers.add(new EntityRendererInfo(entityClass, renderer));
+ }
+
+ /**
+ * Register a simple block rendering handler
+ *
+ * @param handler
+ */
+ public static void registerBlockHandler(ISimpleBlockRenderingHandler handler)
+ {
+ instance().blockRenderers.put(handler.getRenderId(), handler);
+ }
+
+ /**
+ * Register the simple block rendering handler
+ * This version will not call getRenderId on the passed in handler, instead using the supplied ID, so you
+ * can easily re-use the same rendering handler for multiple IDs
+ *
+ * @param renderId
+ * @param handler
+ */
+ public static void registerBlockHandler(int renderId, ISimpleBlockRenderingHandler handler)
+ {
+ instance().blockRenderers.put(renderId, handler);
+ }
+ /**
+ * Get the next available renderId from the block render ID list
+ */
+ public static int getNextAvailableRenderId()
+ {
+ return instance().nextRenderId++;
+ }
+
+ /**
+ * Add a texture override for the given path and return the used index
+ *
+ * @param fileToOverride
+ * @param fileToAdd
+ */
+ public static int addTextureOverride(String fileToOverride, String fileToAdd)
+ {
+ int idx = SpriteHelper.getUniqueSpriteIndex(fileToOverride);
+ addTextureOverride(fileToOverride, fileToAdd, idx);
+ return idx;
+ }
+
+ /**
+ * Add a texture override for the given path and index
+ *
+ * @param path
+ * @param overlayPath
+ * @param index
+ */
+ public static void addTextureOverride(String path, String overlayPath, int index)
+ {
+ TextureFXManager.instance().addNewTextureOverride(path, overlayPath, index);
+ }
+
+ /**
+ * Get and reserve a unique texture index for the supplied path
+ *
+ * @param path
+ */
+ public static int getUniqueTextureIndex(String path)
+ {
+ return SpriteHelper.getUniqueSpriteIndex(path);
+ }
+
+ @Deprecated public static RenderingRegistry instance()
+ {
+ return INSTANCE;
+ }
+
+ private static class EntityRendererInfo
+ {
+ public EntityRendererInfo(Class extends Entity> target, Render renderer)
+ {
+ this.target = target;
+ this.renderer = renderer;
+ }
+ private Class extends Entity> target;
+ private Render renderer;
+ }
+
+ public boolean renderWorldBlock(RenderBlocks renderer, IBlockAccess world, int x, int y, int z, Block block, int modelId)
+ {
+ if (!blockRenderers.containsKey(modelId)) { return false; }
+ ISimpleBlockRenderingHandler bri = blockRenderers.get(modelId);
+ return bri.renderWorldBlock(world, x, y, z, block, modelId, renderer);
+ }
+
+ public void renderInventoryBlock(RenderBlocks renderer, Block block, int metadata, int modelID)
+ {
+ if (!blockRenderers.containsKey(modelID)) { return; }
+ ISimpleBlockRenderingHandler bri = blockRenderers.get(modelID);
+ bri.renderInventoryBlock(block, metadata, modelID, renderer);
+ }
+
+ public boolean renderItemAsFull3DBlock(int modelId)
+ {
+ ISimpleBlockRenderingHandler bri = blockRenderers.get(modelId);
+ return bri != null && bri.shouldRender3DInInventory();
+ }
+
+ public void loadEntityRenderers(Map, Render> rendererMap)
+ {
+ for (EntityRendererInfo info : entityRenderers)
+ {
+ rendererMap.put(info.target, info.renderer);
+ info.renderer.setRenderManager(RenderManager.instance);
+ }
+ }
+}
diff --git a/src/minecraft/cpw/mods/fml/common/BukkitPluginRef.java b/src/minecraft/cpw/mods/fml/common/BukkitPluginRef.java
new file mode 100644
index 0000000..6d97f7b
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/common/BukkitPluginRef.java
@@ -0,0 +1,29 @@
+package cpw.mods.fml.common;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Declare a variable to be populated by a Bukkit Plugin proxy instance if the bukkit coremod
+ * is available. It can only be applied to field typed as {@link BukkitProxy}
+ * Generally it should be used in conjunction with {@link Mod#bukkitPlugin()} specifying the
+ * plugin to load.
+ *
+ * @author cpw
+ *
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface BukkitPluginRef
+{
+ /**
+ * A reference (possibly version specific) to a Bukkit Plugin by name, using the name@versionbound
+ * specification. If this is a bukkit enabled environment the field annotated by this
+ * will be populated with a {@link BukkitProxy} instance if possible. This proxy will be gotten by
+ * reflectively calling the "getModProxy" method on the bukkit plugin instance.
+ * @return The name of the plugin which we will inject into this field
+ */
+ String value();
+}
diff --git a/src/minecraft/cpw/mods/fml/common/BukkitProxy.java b/src/minecraft/cpw/mods/fml/common/BukkitProxy.java
new file mode 100644
index 0000000..d0dcfb3
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/common/BukkitProxy.java
@@ -0,0 +1,12 @@
+package cpw.mods.fml.common;
+
+/**
+ * A marker interface for retrieving a proxy to a bukkit plugin.
+ * Fields associated with {@link BukkitPluginRef} annotations should be should probably
+ * declare this type and cast down if the target is available (not null)
+ * @author cpw
+ *
+ */
+public interface BukkitProxy
+{
+}
diff --git a/src/minecraft/cpw/mods/fml/common/CertificateHelper.java b/src/minecraft/cpw/mods/fml/common/CertificateHelper.java
new file mode 100644
index 0000000..886346c
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/common/CertificateHelper.java
@@ -0,0 +1,56 @@
+package cpw.mods.fml.common;
+
+import java.nio.ByteBuffer;
+import java.security.MessageDigest;
+import java.security.cert.Certificate;
+
+public class CertificateHelper {
+
+ private static final String HEXES = "0123456789abcdef";
+
+ public static String getFingerprint(Certificate certificate)
+ {
+ if (certificate == null)
+ {
+ return "NO VALID CERTIFICATE FOUND";
+ }
+ try
+ {
+ MessageDigest md = MessageDigest.getInstance("SHA-1");
+ byte[] der = certificate.getEncoded();
+ md.update(der);
+ byte[] digest = md.digest();
+ return hexify(digest);
+ }
+ catch (Exception e)
+ {
+ return null;
+ }
+ }
+
+ public static String getFingerprint(ByteBuffer buffer)
+ {
+ try
+ {
+ MessageDigest digest = MessageDigest.getInstance("SHA-1");
+ digest.update(buffer);
+ byte[] chksum = digest.digest();
+ return hexify(chksum);
+ }
+ catch (Exception e)
+ {
+ return null;
+ }
+ }
+
+ private static String hexify(byte[] chksum)
+ {
+ final StringBuilder hex = new StringBuilder( 2 * chksum.length );
+ for ( final byte b : chksum ) {
+ hex.append(HEXES.charAt((b & 0xF0) >> 4))
+ .append(HEXES.charAt((b & 0x0F)));
+ }
+ return hex.toString();
+ }
+
+}
diff --git a/src/minecraft/cpw/mods/fml/common/DummyModContainer.java b/src/minecraft/cpw/mods/fml/common/DummyModContainer.java
new file mode 100644
index 0000000..61fca24
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/common/DummyModContainer.java
@@ -0,0 +1,148 @@
+package cpw.mods.fml.common;
+
+import java.io.File;
+import java.security.cert.Certificate;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import com.google.common.eventbus.EventBus;
+
+import cpw.mods.fml.common.versioning.ArtifactVersion;
+import cpw.mods.fml.common.versioning.DefaultArtifactVersion;
+import cpw.mods.fml.common.versioning.VersionRange;
+
+public class DummyModContainer implements ModContainer
+{
+ private ModMetadata md;
+ private ArtifactVersion processedVersion;
+
+ public DummyModContainer(ModMetadata md)
+ {
+ this.md = md;
+ }
+
+ public DummyModContainer()
+ {
+ }
+
+ @Override
+ public void bindMetadata(MetadataCollection mc)
+ {
+ }
+
+ @Override
+ public List getDependants()
+ {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public List getDependencies()
+ {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public Set getRequirements()
+ {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public ModMetadata getMetadata()
+ {
+ return md;
+ }
+
+ @Override
+ public Object getMod()
+ {
+ return null;
+ }
+
+ @Override
+ public String getModId()
+ {
+ return md.modId;
+ }
+
+ @Override
+ public String getName()
+ {
+ return md.name;
+ }
+
+ @Override
+ public String getSortingRules()
+ {
+ return "";
+ }
+
+ @Override
+ public File getSource()
+ {
+ return null;
+ }
+
+ @Override
+ public String getVersion()
+ {
+ return md.version;
+ }
+
+ public boolean matches(Object mod)
+ {
+ return false;
+ }
+
+ @Override
+ public void setEnabledState(boolean enabled)
+ {
+ }
+
+ @Override
+ public boolean registerBus(EventBus bus, LoadController controller)
+ {
+ return false;
+ }
+
+ @Override
+ public ArtifactVersion getProcessedVersion()
+ {
+ if (processedVersion == null)
+ {
+ processedVersion = new DefaultArtifactVersion(getModId(), getVersion());
+ }
+ return processedVersion;
+ }
+
+ @Override
+ public boolean isImmutable()
+ {
+ return false;
+ }
+
+ @Override
+ public boolean isNetworkMod()
+ {
+ return false;
+ }
+
+ @Override
+ public String getDisplayVersion()
+ {
+ return md.version;
+ }
+ @Override
+ public VersionRange acceptableMinecraftVersionRange()
+ {
+ return Loader.instance().getMinecraftModContainer().getStaticVersionRange();
+ }
+
+ @Override
+ public Certificate getSigningCertificate()
+ {
+ return null;
+ }
+}
diff --git a/src/minecraft/cpw/mods/fml/common/DuplicateModsFoundException.java b/src/minecraft/cpw/mods/fml/common/DuplicateModsFoundException.java
new file mode 100644
index 0000000..a6105cd
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/common/DuplicateModsFoundException.java
@@ -0,0 +1,16 @@
+package cpw.mods.fml.common;
+
+import java.io.File;
+import java.util.List;
+
+import com.google.common.collect.SetMultimap;
+
+public class DuplicateModsFoundException extends LoaderException {
+
+ public SetMultimap dupes;
+
+ public DuplicateModsFoundException(SetMultimap dupes) {
+ this.dupes = dupes;
+ }
+
+}
diff --git a/src/minecraft/cpw/mods/fml/common/FMLCommonHandler.java b/src/minecraft/cpw/mods/fml/common/FMLCommonHandler.java
new file mode 100644
index 0000000..7f7e8ca
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/common/FMLCommonHandler.java
@@ -0,0 +1,461 @@
+/*
+ * The FML Forge Mod Loader suite.
+ * Copyright (C) 2012 cpw
+ *
+ * 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
+ * Software Foundation; either version 2.1 of the License, or any later version.
+ *
+ * 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
+ * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * 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
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+package cpw.mods.fml.common;
+
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.logging.Logger;
+
+import net.minecraft.crash.CrashReport;
+import net.minecraft.crash.CrashReportCategory;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.nbt.NBTBase;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.network.INetworkManager;
+import net.minecraft.network.packet.NetHandler;
+import net.minecraft.network.packet.Packet131MapData;
+import net.minecraft.server.*;
+import net.minecraft.server.dedicated.DedicatedServer;
+import net.minecraft.world.World;
+import net.minecraft.world.storage.SaveHandler;
+import net.minecraft.world.storage.WorldInfo;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+import com.google.common.collect.Lists;
+import com.google.common.collect.MapDifference;
+import com.google.common.collect.MapMaker;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+import cpw.mods.fml.common.network.EntitySpawnAdjustmentPacket;
+import cpw.mods.fml.common.network.EntitySpawnPacket;
+import cpw.mods.fml.common.registry.EntityRegistry.EntityRegistration;
+import cpw.mods.fml.common.registry.ItemData;
+import cpw.mods.fml.common.registry.TickRegistry;
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.server.FMLServerHandler;
+
+
+/**
+ * The main class for non-obfuscated hook handling code
+ *
+ * Anything that doesn't require obfuscated or client/server specific code should
+ * go in this handler
+ *
+ * It also contains a reference to the sided handler instance that is valid
+ * allowing for common code to access specific properties from the obfuscated world
+ * without a direct dependency
+ *
+ * @author cpw
+ *
+ */
+public class FMLCommonHandler
+{
+ /**
+ * The singleton
+ */
+ private static final FMLCommonHandler INSTANCE = new FMLCommonHandler();
+ /**
+ * The delegate for side specific data and functions
+ */
+ private IFMLSidedHandler sidedDelegate;
+
+ private List scheduledClientTicks = Lists.newArrayList();
+ private List scheduledServerTicks = Lists.newArrayList();
+ private Class> forge;
+ private boolean noForge;
+ private List brandings;
+ private List crashCallables = Lists.newArrayList(Loader.instance().getCallableCrashInformation());
+ private Set handlerSet = Sets.newSetFromMap(new MapMaker().weakKeys().makeMap());
+
+
+
+ public void beginLoading(IFMLSidedHandler handler)
+ {
+ sidedDelegate = handler;
+ FMLLog.info("Attempting early MinecraftForge initialization");
+ callForgeMethod("initialize");
+ callForgeMethod("registerCrashCallable");
+ FMLLog.info("Completed early MinecraftForge initialization");
+ }
+
+ public void rescheduleTicks(Side side)
+ {
+ TickRegistry.updateTickQueue(side.isClient() ? scheduledClientTicks : scheduledServerTicks, side);
+ }
+ public void tickStart(EnumSet ticks, Side side, Object ... data)
+ {
+ List scheduledTicks = side.isClient() ? scheduledClientTicks : scheduledServerTicks;
+
+ if (scheduledTicks.size()==0)
+ {
+ return;
+ }
+ for (IScheduledTickHandler ticker : scheduledTicks)
+ {
+ EnumSet ticksToRun = EnumSet.copyOf(Objects.firstNonNull(ticker.ticks(), EnumSet.noneOf(TickType.class)));
+ ticksToRun.removeAll(EnumSet.complementOf(ticks));
+ if (!ticksToRun.isEmpty())
+ {
+ ticker.tickStart(ticksToRun, data);
+ }
+ }
+ }
+
+ public void tickEnd(EnumSet ticks, Side side, Object ... data)
+ {
+ List scheduledTicks = side.isClient() ? scheduledClientTicks : scheduledServerTicks;
+
+ if (scheduledTicks.size()==0)
+ {
+ return;
+ }
+ for (IScheduledTickHandler ticker : scheduledTicks)
+ {
+ EnumSet ticksToRun = EnumSet.copyOf(Objects.firstNonNull(ticker.ticks(), EnumSet.noneOf(TickType.class)));
+ ticksToRun.removeAll(EnumSet.complementOf(ticks));
+ if (!ticksToRun.isEmpty())
+ {
+ ticker.tickEnd(ticksToRun, data);
+ }
+ }
+ }
+
+ /**
+ * @return the instance
+ */
+ public static FMLCommonHandler instance()
+ {
+ return INSTANCE;
+ }
+ /**
+ * Find the container that associates with the supplied mod object
+ * @param mod
+ */
+ public ModContainer findContainerFor(Object mod)
+ {
+ return Loader.instance().getReversedModObjectList().get(mod);
+ }
+ /**
+ * Get the forge mod loader logging instance (goes to the forgemodloader log file)
+ * @return The log instance for the FML log file
+ */
+ public Logger getFMLLogger()
+ {
+ return FMLLog.getLogger();
+ }
+
+ public Side getSide()
+ {
+ return sidedDelegate.getSide();
+ }
+
+ /**
+ * Return the effective side for the context in the game. This is dependent
+ * on thread analysis to try and determine whether the code is running in the
+ * server or not. Use at your own risk
+ */
+ public Side getEffectiveSide()
+ {
+ Thread thr = Thread.currentThread();
+ if ((thr instanceof ThreadMinecraftServer) || (thr instanceof ServerListenThread))
+ {
+ return Side.SERVER;
+ }
+
+ return Side.CLIENT;
+ }
+ /**
+ * Raise an exception
+ */
+ public void raiseException(Throwable exception, String message, boolean stopGame)
+ {
+ FMLCommonHandler.instance().getFMLLogger().throwing("FMLHandler", "raiseException", exception);
+ if (stopGame)
+ {
+ getSidedDelegate().haltGame(message,exception);
+ }
+ }
+
+
+ private Class> findMinecraftForge()
+ {
+ if (forge==null && !noForge)
+ {
+ try {
+ forge = Class.forName("net.minecraftforge.common.MinecraftForge");
+ } catch (Exception ex) {
+ noForge = true;
+ }
+ }
+ return forge;
+ }
+
+ private Object callForgeMethod(String method)
+ {
+ if (noForge)
+ return null;
+ try
+ {
+ return findMinecraftForge().getMethod(method).invoke(null);
+ }
+ catch (Exception e)
+ {
+ // No Forge installation
+ return null;
+ }
+ }
+
+ public void computeBranding()
+ {
+ if (brandings == null)
+ {
+ Builder brd = ImmutableList.builder();
+ brd.add(Loader.instance().getMCVersionString());
+ brd.add(Loader.instance().getMCPVersionString());
+ brd.add("FML v"+Loader.instance().getFMLVersionString());
+ String forgeBranding = (String) callForgeMethod("getBrandingVersion");
+ if (!Strings.isNullOrEmpty(forgeBranding))
+ {
+ brd.add(forgeBranding);
+ }
+ if (sidedDelegate!=null)
+ {
+ brd.addAll(sidedDelegate.getAdditionalBrandingInformation());
+ }
+ try {
+ Properties props=new Properties();
+ props.load(getClass().getClassLoader().getResourceAsStream("fmlbranding.properties"));
+ brd.add(props.getProperty("fmlbranding"));
+ } catch (Exception ex) {
+ // Ignore - no branding file found
+ }
+ int tModCount = Loader.instance().getModList().size();
+ int aModCount = Loader.instance().getActiveModList().size();
+ brd.add(String.format("%d mod%s loaded, %d mod%s active", tModCount, tModCount!=1 ? "s" :"", aModCount, aModCount!=1 ? "s" :"" ));
+ brandings = brd.build();
+ }
+ }
+ public List getBrandings()
+ {
+ if (brandings == null)
+ {
+ computeBranding();
+ }
+ return ImmutableList.copyOf(brandings);
+ }
+
+ public IFMLSidedHandler getSidedDelegate()
+ {
+ return sidedDelegate;
+ }
+
+ public void onPostServerTick()
+ {
+ tickEnd(EnumSet.of(TickType.SERVER), Side.SERVER);
+ }
+
+ /**
+ * Every tick just after world and other ticks occur
+ */
+ public void onPostWorldTick(Object world)
+ {
+ tickEnd(EnumSet.of(TickType.WORLD), Side.SERVER, world);
+ }
+
+ public void onPreServerTick()
+ {
+ tickStart(EnumSet.of(TickType.SERVER), Side.SERVER);
+ }
+
+ /**
+ * Every tick just before world and other ticks occur
+ */
+ public void onPreWorldTick(Object world)
+ {
+ tickStart(EnumSet.of(TickType.WORLD), Side.SERVER, world);
+ }
+
+ public void onWorldLoadTick(World[] worlds)
+ {
+ rescheduleTicks(Side.SERVER);
+ for (World w : worlds)
+ {
+ tickStart(EnumSet.of(TickType.WORLDLOAD), Side.SERVER, w);
+ }
+ }
+
+ public void handleServerStarting(MinecraftServer server)
+ {
+ Loader.instance().serverStarting(server);
+ }
+
+ public void handleServerStarted()
+ {
+ Loader.instance().serverStarted();
+ }
+
+ public void handleServerStopping()
+ {
+ Loader.instance().serverStopping();
+ }
+
+ public MinecraftServer getMinecraftServerInstance()
+ {
+ return sidedDelegate.getServer();
+ }
+
+ public void showGuiScreen(Object clientGuiElement)
+ {
+ sidedDelegate.showGuiScreen(clientGuiElement);
+ }
+
+ public Entity spawnEntityIntoClientWorld(EntityRegistration registration, EntitySpawnPacket entitySpawnPacket)
+ {
+ return sidedDelegate.spawnEntityIntoClientWorld(registration, entitySpawnPacket);
+ }
+
+ public void adjustEntityLocationOnClient(EntitySpawnAdjustmentPacket entitySpawnAdjustmentPacket)
+ {
+ sidedDelegate.adjustEntityLocationOnClient(entitySpawnAdjustmentPacket);
+ }
+
+ public void onServerStart(DedicatedServer dedicatedServer)
+ {
+ FMLServerHandler.instance();
+ sidedDelegate.beginServerLoading(dedicatedServer);
+ }
+
+ public void onServerStarted()
+ {
+ sidedDelegate.finishServerLoading();
+ }
+
+
+ public void onPreClientTick()
+ {
+ tickStart(EnumSet.of(TickType.CLIENT), Side.CLIENT);
+
+ }
+
+ public void onPostClientTick()
+ {
+ tickEnd(EnumSet.of(TickType.CLIENT), Side.CLIENT);
+ }
+
+ public void onRenderTickStart(float timer)
+ {
+ tickStart(EnumSet.of(TickType.RENDER), Side.CLIENT, timer);
+ }
+
+ public void onRenderTickEnd(float timer)
+ {
+ tickEnd(EnumSet.of(TickType.RENDER), Side.CLIENT, timer);
+ }
+
+ public void onPlayerPreTick(EntityPlayer player)
+ {
+ Side side = player instanceof EntityPlayerMP ? Side.SERVER : Side.CLIENT;
+ tickStart(EnumSet.of(TickType.PLAYER), side, player);
+ }
+
+ public void onPlayerPostTick(EntityPlayer player)
+ {
+ Side side = player instanceof EntityPlayerMP ? Side.SERVER : Side.CLIENT;
+ tickEnd(EnumSet.of(TickType.PLAYER), side, player);
+ }
+
+ public void registerCrashCallable(ICrashCallable callable)
+ {
+ crashCallables.add(callable);
+ }
+
+ public void enhanceCrashReport(CrashReport crashReport, CrashReportCategory category)
+ {
+ for (ICrashCallable call: crashCallables)
+ {
+ category.addCrashSectionCallable(call.getLabel(), call);
+ }
+ }
+
+ public void handleTinyPacket(NetHandler handler, Packet131MapData mapData)
+ {
+ sidedDelegate.handleTinyPacket(handler, mapData);
+ }
+
+ public void handleWorldDataSave(SaveHandler handler, WorldInfo worldInfo, NBTTagCompound tagCompound)
+ {
+ for (ModContainer mc : Loader.instance().getModList())
+ {
+ if (mc instanceof InjectedModContainer)
+ {
+ WorldAccessContainer wac = ((InjectedModContainer)mc).getWrappedWorldAccessContainer();
+ if (wac != null)
+ {
+ NBTTagCompound dataForWriting = wac.getDataForWriting(handler, worldInfo);
+ tagCompound.setCompoundTag(mc.getModId(), dataForWriting);
+ }
+ }
+ }
+ }
+
+ public void handleWorldDataLoad(SaveHandler handler, WorldInfo worldInfo, NBTTagCompound tagCompound)
+ {
+ if (getEffectiveSide()!=Side.SERVER)
+ {
+ return;
+ }
+ if (handlerSet.contains(handler))
+ {
+ return;
+ }
+ handlerSet.add(handler);
+ Map additionalProperties = Maps.newHashMap();
+ worldInfo.setAdditionalProperties(additionalProperties);
+ for (ModContainer mc : Loader.instance().getModList())
+ {
+ if (mc instanceof InjectedModContainer)
+ {
+ WorldAccessContainer wac = ((InjectedModContainer)mc).getWrappedWorldAccessContainer();
+ if (wac != null)
+ {
+ wac.readData(handler, worldInfo, additionalProperties, tagCompound.getCompoundTag(mc.getModId()));
+ }
+ }
+ }
+ }
+
+ public boolean shouldServerBeKilledQuietly()
+ {
+ return sidedDelegate.shouldServerShouldBeKilledQuietly();
+ }
+
+ public void disconnectIDMismatch(MapDifference serverDifference, NetHandler toKill, INetworkManager network)
+ {
+ sidedDelegate.disconnectIDMismatch(serverDifference, toKill, network);
+ }
+
+ public void handleServerStopped()
+ {
+ Loader.instance().serverStopped();
+ }
+}
diff --git a/src/minecraft/cpw/mods/fml/common/FMLDummyContainer.java b/src/minecraft/cpw/mods/fml/common/FMLDummyContainer.java
new file mode 100644
index 0000000..6be7080
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/common/FMLDummyContainer.java
@@ -0,0 +1,127 @@
+/*
+ * The FML Forge Mod Loader suite.
+ * Copyright (C) 2012 cpw
+ *
+ * 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
+ * Software Foundation; either version 2.1 of the License, or any later version.
+ *
+ * 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
+ * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * 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
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package cpw.mods.fml.common;
+
+import java.security.cert.Certificate;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Set;
+
+import net.minecraft.nbt.NBTBase;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.nbt.NBTTagList;
+import net.minecraft.world.storage.SaveHandler;
+import net.minecraft.world.storage.WorldInfo;
+
+import com.google.common.eventbus.EventBus;
+
+import cpw.mods.fml.common.registry.GameData;
+import cpw.mods.fml.common.registry.GameRegistry;
+import cpw.mods.fml.common.registry.ItemData;
+
+/**
+ * @author cpw
+ *
+ */
+public class FMLDummyContainer extends DummyModContainer implements WorldAccessContainer
+{
+ public FMLDummyContainer()
+ {
+ super(new ModMetadata());
+ ModMetadata meta = getMetadata();
+ meta.modId="FML";
+ meta.name="Forge Mod Loader";
+ meta.version=Loader.instance().getFMLVersionString();
+ meta.credits="Made possible with help from many people";
+ meta.authorList=Arrays.asList("cpw, LexManos");
+ meta.description="The Forge Mod Loader provides the ability for systems to load mods " +
+ "from the file system. It also provides key capabilities for mods to be able " +
+ "to cooperate and provide a good modding environment. " +
+ "The mod loading system is compatible with ModLoader, all your ModLoader " +
+ "mods should work.";
+ meta.url="https://github.com/cpw/FML/wiki";
+ meta.updateUrl="https://github.com/cpw/FML/wiki";
+ meta.screenshots=new String[0];
+ meta.logoFile="";
+ }
+
+ @Override
+ public boolean registerBus(EventBus bus, LoadController controller)
+ {
+ return true;
+ }
+
+ @Override
+ public NBTTagCompound getDataForWriting(SaveHandler handler, WorldInfo info)
+ {
+ NBTTagCompound fmlData = new NBTTagCompound();
+ NBTTagList list = new NBTTagList();
+ for (ModContainer mc : Loader.instance().getActiveModList())
+ {
+ NBTTagCompound mod = new NBTTagCompound();
+ mod.setString("ModId", mc.getModId());
+ mod.setString("ModVersion", mc.getVersion());
+ list.appendTag(mod);
+ }
+ fmlData.setTag("ModList", list);
+ NBTTagList itemList = new NBTTagList();
+ GameData.writeItemData(itemList);
+ fmlData.setTag("ModItemData", itemList);
+ return fmlData;
+ }
+
+ @Override
+ public void readData(SaveHandler handler, WorldInfo info, Map propertyMap, NBTTagCompound tag)
+ {
+ if (tag.hasKey("ModList"))
+ {
+ NBTTagList modList = tag.getTagList("ModList");
+ for (int i = 0; i < modList.tagCount(); i++)
+ {
+ NBTTagCompound mod = (NBTTagCompound) modList.tagAt(i);
+ String modId = mod.getString("ModId");
+ String modVersion = mod.getString("ModVersion");
+ ModContainer container = Loader.instance().getIndexedModList().get(modId);
+ if (container == null)
+ {
+ FMLLog.severe("This world was saved with mod %s which appears to be missing, things may not work well", modId);
+ continue;
+ }
+ if (!modVersion.equals(container.getVersion()))
+ {
+ FMLLog.info("This world was saved with mod %s version %s and it is now at version %s, things may not work well", modId, modVersion, container.getVersion());
+ }
+ }
+ }
+ if (tag.hasKey("ModItemData"))
+ {
+ NBTTagList modList = tag.getTagList("ModItemData");
+ Set worldSaveItems = GameData.buildWorldItemData(modList);
+ GameData.validateWorldSave(worldSaveItems);
+ }
+ else
+ {
+ GameData.validateWorldSave(null);
+ }
+ }
+
+
+ @Override
+ public Certificate getSigningCertificate()
+ {
+ Certificate[] certificates = getClass().getProtectionDomain().getCodeSource().getCertificates();
+ return certificates != null ? certificates[0] : null;
+ }
+}
diff --git a/src/minecraft/cpw/mods/fml/common/FMLLog.java b/src/minecraft/cpw/mods/fml/common/FMLLog.java
new file mode 100644
index 0000000..f9e6d67
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/common/FMLLog.java
@@ -0,0 +1,53 @@
+package cpw.mods.fml.common;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class FMLLog
+{
+ private static cpw.mods.fml.relauncher.FMLRelaunchLog coreLog = cpw.mods.fml.relauncher.FMLRelaunchLog.log;
+
+ public static void log(Level level, String format, Object... data)
+ {
+ coreLog.log(level, format, data);
+ }
+
+ public static void log(Level level, Throwable ex, String format, Object... data)
+ {
+ coreLog.log(level, ex, format, data);
+ }
+
+ public static void severe(String format, Object... data)
+ {
+ log(Level.SEVERE, format, data);
+ }
+
+ public static void warning(String format, Object... data)
+ {
+ log(Level.WARNING, format, data);
+ }
+
+ public static void info(String format, Object... data)
+ {
+ log(Level.INFO, format, data);
+ }
+
+ public static void fine(String format, Object... data)
+ {
+ log(Level.FINE, format, data);
+ }
+
+ public static void finer(String format, Object... data)
+ {
+ log(Level.FINER, format, data);
+ }
+
+ public static void finest(String format, Object... data)
+ {
+ log(Level.FINEST, format, data);
+ }
+ public static Logger getLogger()
+ {
+ return coreLog.getLogger();
+ }
+}
diff --git a/src/minecraft/cpw/mods/fml/common/FMLModContainer.java b/src/minecraft/cpw/mods/fml/common/FMLModContainer.java
new file mode 100644
index 0000000..7ab6449
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/common/FMLModContainer.java
@@ -0,0 +1,533 @@
+/*
+ * The FML Forge Mod Loader suite. Copyright (C) 2012 cpw
+ *
+ * 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
+ * Software Foundation; either version 2.1 of the License, or any later version.
+ *
+ * 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
+ * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * 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
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+package cpw.mods.fml.common;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.security.cert.Certificate;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipInputStream;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicates;
+import com.google.common.base.Strings;
+import com.google.common.base.Throwables;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterators;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.SetMultimap;
+import com.google.common.collect.Sets;
+import com.google.common.eventbus.EventBus;
+import com.google.common.eventbus.Subscribe;
+
+import cpw.mods.fml.common.Mod.Instance;
+import cpw.mods.fml.common.Mod.Metadata;
+import cpw.mods.fml.common.discovery.ASMDataTable;
+import cpw.mods.fml.common.discovery.ASMDataTable.ASMData;
+import cpw.mods.fml.common.event.FMLConstructionEvent;
+import cpw.mods.fml.common.event.FMLEvent;
+import cpw.mods.fml.common.event.FMLInitializationEvent;
+import cpw.mods.fml.common.event.FMLInterModComms.IMCEvent;
+import cpw.mods.fml.common.event.FMLFingerprintViolationEvent;
+import cpw.mods.fml.common.event.FMLPostInitializationEvent;
+import cpw.mods.fml.common.event.FMLPreInitializationEvent;
+import cpw.mods.fml.common.event.FMLServerStartedEvent;
+import cpw.mods.fml.common.event.FMLServerStartingEvent;
+import cpw.mods.fml.common.event.FMLServerStoppedEvent;
+import cpw.mods.fml.common.event.FMLServerStoppingEvent;
+import cpw.mods.fml.common.event.FMLStateEvent;
+import cpw.mods.fml.common.network.FMLNetworkHandler;
+import cpw.mods.fml.common.versioning.ArtifactVersion;
+import cpw.mods.fml.common.versioning.DefaultArtifactVersion;
+import cpw.mods.fml.common.versioning.VersionParser;
+import cpw.mods.fml.common.versioning.VersionRange;
+
+public class FMLModContainer implements ModContainer
+{
+ private Mod modDescriptor;
+ private Object modInstance;
+ private File source;
+ private ModMetadata modMetadata;
+ private String className;
+ private Map descriptor;
+ private boolean enabled = true;
+ private String internalVersion;
+ private boolean overridesMetadata;
+ private EventBus eventBus;
+ private LoadController controller;
+ private Multimap, Object> annotations;
+ private DefaultArtifactVersion processedVersion;
+ private boolean isNetworkMod;
+
+ private static final BiMap, Class extends Annotation>> modAnnotationTypes = ImmutableBiMap., Class extends Annotation>>builder()
+ .put(FMLPreInitializationEvent.class, Mod.PreInit.class)
+ .put(FMLInitializationEvent.class, Mod.Init.class)
+ .put(FMLPostInitializationEvent.class, Mod.PostInit.class)
+ .put(FMLServerStartingEvent.class, Mod.ServerStarting.class)
+ .put(FMLServerStartedEvent.class, Mod.ServerStarted.class)
+ .put(FMLServerStoppingEvent.class, Mod.ServerStopping.class)
+ .put(FMLServerStoppedEvent.class, Mod.ServerStopped.class)
+ .put(IMCEvent.class,Mod.IMCCallback.class)
+ .put(FMLFingerprintViolationEvent.class, Mod.FingerprintWarning.class)
+ .build();
+ private static final BiMap, Class extends FMLEvent>> modTypeAnnotations = modAnnotationTypes.inverse();
+ private String annotationDependencies;
+ private VersionRange minecraftAccepted;
+ private boolean fingerprintNotPresent;
+ private Set sourceFingerprints;
+ private Certificate certificate;
+
+
+ public FMLModContainer(String className, File modSource, Map modDescriptor)
+ {
+ this.className = className;
+ this.source = modSource;
+ this.descriptor = modDescriptor;
+ }
+
+ @Override
+ public String getModId()
+ {
+ return (String) descriptor.get("modid");
+ }
+
+ @Override
+ public String getName()
+ {
+ return modMetadata.name;
+ }
+
+ @Override
+ public String getVersion()
+ {
+ return internalVersion;
+ }
+
+ @Override
+ public File getSource()
+ {
+ return source;
+ }
+
+ @Override
+ public ModMetadata getMetadata()
+ {
+ return modMetadata;
+ }
+
+ @Override
+ public void bindMetadata(MetadataCollection mc)
+ {
+ modMetadata = mc.getMetadataForId(getModId(), descriptor);
+
+ if (descriptor.containsKey("useMetadata"))
+ {
+ overridesMetadata = !((Boolean)descriptor.get("useMetadata")).booleanValue();
+ }
+
+ if (overridesMetadata || !modMetadata.useDependencyInformation)
+ {
+ Set requirements = Sets.newHashSet();
+ List dependencies = Lists.newArrayList();
+ List dependants = Lists.newArrayList();
+ annotationDependencies = (String) descriptor.get("dependencies");
+ Loader.instance().computeDependencies(annotationDependencies, requirements, dependencies, dependants);
+ modMetadata.requiredMods = requirements;
+ modMetadata.dependencies = dependencies;
+ modMetadata.dependants = dependants;
+ FMLLog.finest("Parsed dependency info : %s %s %s", requirements, dependencies, dependants);
+ }
+ else
+ {
+ FMLLog.finest("Using mcmod dependency info : %s %s %s", modMetadata.requiredMods, modMetadata.dependencies, modMetadata.dependants);
+ }
+ if (Strings.isNullOrEmpty(modMetadata.name))
+ {
+ FMLLog.info("Mod %s is missing the required element 'name'. Substituting %s", getModId(), getModId());
+ modMetadata.name = getModId();
+ }
+ internalVersion = (String) descriptor.get("version");
+ if (Strings.isNullOrEmpty(internalVersion))
+ {
+ Properties versionProps = searchForVersionProperties();
+ if (versionProps != null)
+ {
+ internalVersion = versionProps.getProperty(getModId()+".version");
+ FMLLog.fine("Found version %s for mod %s in version.properties, using", internalVersion, getModId());
+ }
+
+ }
+ if (Strings.isNullOrEmpty(internalVersion) && !Strings.isNullOrEmpty(modMetadata.version))
+ {
+ FMLLog.warning("Mod %s is missing the required element 'version' and a version.properties file could not be found. Falling back to metadata version %s", getModId(), modMetadata.version);
+ internalVersion = modMetadata.version;
+ }
+ if (Strings.isNullOrEmpty(internalVersion))
+ {
+ FMLLog.warning("Mod %s is missing the required element 'version' and no fallback can be found. Substituting '1.0'.", getModId());
+ modMetadata.version = internalVersion = "1.0";
+ }
+
+ String mcVersionString = (String) descriptor.get("acceptedMinecraftVersions");
+ if (!Strings.isNullOrEmpty(mcVersionString))
+ {
+ minecraftAccepted = VersionParser.parseRange(mcVersionString);
+ }
+ else
+ {
+ minecraftAccepted = Loader.instance().getMinecraftModContainer().getStaticVersionRange();
+ }
+ }
+
+ public Properties searchForVersionProperties()
+ {
+ try
+ {
+ FMLLog.fine("Attempting to load the file version.properties from %s to locate a version number for %s", getSource().getName(), getModId());
+ Properties version = null;
+ if (getSource().isFile())
+ {
+ ZipFile source = new ZipFile(getSource());
+ ZipEntry versionFile = source.getEntry("version.properties");
+ if (versionFile!=null)
+ {
+ version = new Properties();
+ version.load(source.getInputStream(versionFile));
+ }
+ source.close();
+ }
+ else if (getSource().isDirectory())
+ {
+ File propsFile = new File(getSource(),"version.properties");
+ if (propsFile.exists() && propsFile.isFile())
+ {
+ version = new Properties();
+ FileInputStream fis = new FileInputStream(propsFile);
+ version.load(fis);
+ fis.close();
+ }
+ }
+ return version;
+ }
+ catch (Exception e)
+ {
+ Throwables.propagateIfPossible(e);
+ FMLLog.fine("Failed to find a usable version.properties file");
+ return null;
+ }
+ }
+
+ @Override
+ public void setEnabledState(boolean enabled)
+ {
+ this.enabled = enabled;
+ }
+
+ @Override
+ public Set getRequirements()
+ {
+ return modMetadata.requiredMods;
+ }
+
+ @Override
+ public List getDependencies()
+ {
+ return modMetadata.dependencies;
+ }
+
+ @Override
+ public List getDependants()
+ {
+ return modMetadata.dependants;
+ }
+
+ @Override
+ public String getSortingRules()
+ {
+ return ((overridesMetadata || !modMetadata.useDependencyInformation) ? Strings.nullToEmpty(annotationDependencies) : modMetadata.printableSortingRules());
+ }
+
+ @Override
+ public boolean matches(Object mod)
+ {
+ return mod == modInstance;
+ }
+
+ @Override
+ public Object getMod()
+ {
+ return modInstance;
+ }
+
+ @Override
+ public boolean registerBus(EventBus bus, LoadController controller)
+ {
+ if (this.enabled)
+ {
+ FMLLog.fine("Enabling mod %s", getModId());
+ this.eventBus = bus;
+ this.controller = controller;
+ eventBus.register(this);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ private Multimap, Object> gatherAnnotations(Class> clazz) throws Exception
+ {
+ Multimap,Object> anns = ArrayListMultimap.create();
+
+ for (Method m : clazz.getDeclaredMethods())
+ {
+ for (Annotation a : m.getAnnotations())
+ {
+ if (modTypeAnnotations.containsKey(a.annotationType()))
+ {
+ Class>[] paramTypes = new Class[] { modTypeAnnotations.get(a.annotationType()) };
+
+ if (Arrays.equals(m.getParameterTypes(), paramTypes))
+ {
+ m.setAccessible(true);
+ anns.put(a.annotationType(), m);
+ }
+ else
+ {
+ FMLLog.severe("The mod %s appears to have an invalid method annotation %s. This annotation can only apply to methods with argument types %s -it will not be called", getModId(), a.annotationType().getSimpleName(), Arrays.toString(paramTypes));
+ }
+ }
+ }
+ }
+ return anns;
+ }
+
+ private void processFieldAnnotations(ASMDataTable asmDataTable) throws Exception
+ {
+ SetMultimap annotations = asmDataTable.getAnnotationsFor(this);
+
+ parseSimpleFieldAnnotation(annotations, Instance.class.getName(), new Function()
+ {
+ public Object apply(ModContainer mc)
+ {
+ return mc.getMod();
+ }
+ });
+ parseSimpleFieldAnnotation(annotations, Metadata.class.getName(), new Function()
+ {
+ public Object apply(ModContainer mc)
+ {
+ return mc.getMetadata();
+ }
+ });
+ }
+
+ private void parseSimpleFieldAnnotation(SetMultimap annotations, String annotationClassName, Function retreiver) throws IllegalAccessException
+ {
+ String[] annName = annotationClassName.split("\\.");
+ String annotationName = annName[annName.length - 1];
+ for (ASMData targets : annotations.get(annotationClassName))
+ {
+ String targetMod = (String) targets.getAnnotationInfo().get("value");
+ Field f = null;
+ Object injectedMod = null;
+ ModContainer mc = this;
+ boolean isStatic = false;
+ Class> clz = modInstance.getClass();
+ if (!Strings.isNullOrEmpty(targetMod))
+ {
+ if (Loader.isModLoaded(targetMod))
+ {
+ mc = Loader.instance().getIndexedModList().get(targetMod);
+ }
+ else
+ {
+ mc = null;
+ }
+ }
+ if (mc != null)
+ {
+ try
+ {
+ clz = Class.forName(targets.getClassName(), true, Loader.instance().getModClassLoader());
+ f = clz.getDeclaredField(targets.getObjectName());
+ f.setAccessible(true);
+ isStatic = Modifier.isStatic(f.getModifiers());
+ injectedMod = retreiver.apply(mc);
+ }
+ catch (Exception e)
+ {
+ Throwables.propagateIfPossible(e);
+ FMLLog.log(Level.WARNING, e, "Attempting to load @%s in class %s for %s and failing", annotationName, targets.getClassName(), mc.getModId());
+ }
+ }
+ if (f != null)
+ {
+ Object target = null;
+ if (!isStatic)
+ {
+ target = modInstance;
+ if (!modInstance.getClass().equals(clz))
+ {
+ FMLLog.warning("Unable to inject @%s in non-static field %s.%s for %s as it is NOT the primary mod instance", annotationName, targets.getClassName(), targets.getObjectName(), mc.getModId());
+ continue;
+ }
+ }
+ f.set(target, injectedMod);
+ }
+ }
+ }
+
+ @Subscribe
+ public void constructMod(FMLConstructionEvent event)
+ {
+ try
+ {
+ ModClassLoader modClassLoader = event.getModClassLoader();
+ modClassLoader.addFile(source);
+ Class> clazz = Class.forName(className, true, modClassLoader);
+
+ Certificate[] certificates = clazz.getProtectionDomain().getCodeSource().getCertificates();
+ int len = 0;
+ if (certificates != null)
+ {
+ len = certificates.length;
+ }
+ Builder certBuilder = ImmutableList.builder();
+ for (int i = 0; i < len; i++)
+ {
+ certBuilder.add(CertificateHelper.getFingerprint(certificates[i]));
+ }
+
+ ImmutableList certList = certBuilder.build();
+ sourceFingerprints = ImmutableSet.copyOf(certList);
+
+ String expectedFingerprint = (String) descriptor.get("certificateFingerprint");
+
+ fingerprintNotPresent = true;
+
+ if (expectedFingerprint != null && !expectedFingerprint.isEmpty())
+ {
+ if (!sourceFingerprints.contains(expectedFingerprint))
+ {
+ Level warnLevel = Level.SEVERE;
+ if (source.isDirectory())
+ {
+ warnLevel = Level.FINER;
+ }
+ FMLLog.log(warnLevel, "The mod %s is expecting signature %s for source %s, however there is no signature matching that description", getModId(), expectedFingerprint, source.getName());
+ }
+ else
+ {
+ certificate = certificates[certList.indexOf(expectedFingerprint)];
+ fingerprintNotPresent = false;
+ }
+ }
+
+ annotations = gatherAnnotations(clazz);
+ isNetworkMod = FMLNetworkHandler.instance().registerNetworkMod(this, clazz, event.getASMHarvestedData());
+ modInstance = clazz.newInstance();
+ if (fingerprintNotPresent)
+ {
+ eventBus.post(new FMLFingerprintViolationEvent(source.isDirectory(), source, ImmutableSet.copyOf(this.sourceFingerprints), expectedFingerprint));
+ }
+ ProxyInjector.inject(this, event.getASMHarvestedData(), FMLCommonHandler.instance().getSide());
+ processFieldAnnotations(event.getASMHarvestedData());
+ }
+ catch (Throwable e)
+ {
+ controller.errorOccurred(this, e);
+ Throwables.propagateIfPossible(e);
+ }
+ }
+
+ @Subscribe
+ public void handleModStateEvent(FMLEvent event)
+ {
+ Class extends Annotation> annotation = modAnnotationTypes.get(event.getClass());
+ if (annotation == null)
+ {
+ return;
+ }
+ try
+ {
+ for (Object o : annotations.get(annotation))
+ {
+ Method m = (Method) o;
+ m.invoke(modInstance, event);
+ }
+ }
+ catch (Throwable t)
+ {
+ controller.errorOccurred(this, t);
+ Throwables.propagateIfPossible(t);
+ }
+ }
+
+ @Override
+ public ArtifactVersion getProcessedVersion()
+ {
+ if (processedVersion == null)
+ {
+ processedVersion = new DefaultArtifactVersion(getModId(), getVersion());
+ }
+ return processedVersion;
+ }
+ @Override
+ public boolean isImmutable()
+ {
+ return false;
+ }
+
+ @Override
+ public boolean isNetworkMod()
+ {
+ return isNetworkMod;
+ }
+
+ @Override
+ public String getDisplayVersion()
+ {
+ return modMetadata.version;
+ }
+
+ @Override
+ public VersionRange acceptableMinecraftVersionRange()
+ {
+ return minecraftAccepted;
+ }
+
+ @Override
+ public Certificate getSigningCertificate()
+ {
+ return certificate;
+ }
+}
diff --git a/src/minecraft/cpw/mods/fml/common/IConsoleHandler.java b/src/minecraft/cpw/mods/fml/common/IConsoleHandler.java
new file mode 100644
index 0000000..5a5e1e0
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/common/IConsoleHandler.java
@@ -0,0 +1,24 @@
+/*
+ * The FML Forge Mod Loader suite.
+ * Copyright (C) 2012 cpw
+ *
+ * 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
+ * Software Foundation; either version 2.1 of the License, or any later version.
+ *
+ * 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
+ * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * 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
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package cpw.mods.fml.common;
+
+/**
+ * @author cpw
+ *
+ */
+public interface IConsoleHandler
+{
+ public boolean handleCommand(String command, Object... data);
+}
diff --git a/src/minecraft/cpw/mods/fml/common/ICraftingHandler.java b/src/minecraft/cpw/mods/fml/common/ICraftingHandler.java
new file mode 100644
index 0000000..03df7cc
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/common/ICraftingHandler.java
@@ -0,0 +1,43 @@
+/*
+ * The FML Forge Mod Loader suite.
+ * Copyright (C) 2012 cpw
+ *
+ * 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
+ * Software Foundation; either version 2.1 of the License, or any later version.
+ *
+ * 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
+ * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * 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
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+package cpw.mods.fml.common;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.inventory.IInventory;
+import net.minecraft.item.ItemStack;
+
+/**
+ * Return a crafting handler for the mod container to call
+ *
+ * @author cpw
+ *
+ */
+public interface ICraftingHandler
+{
+ /**
+ * The object array contains these three arguments
+ *
+ * @param player
+ * @param item
+ * @param craftMatrix
+ */
+ void onCrafting(EntityPlayer player, ItemStack item, IInventory craftMatrix);
+
+ /**
+ * The object array contains these two arguments
+ * @param player
+ * @param item
+ */
+ void onSmelting(EntityPlayer player, ItemStack item);
+}
diff --git a/src/minecraft/cpw/mods/fml/common/ICrashCallable.java b/src/minecraft/cpw/mods/fml/common/ICrashCallable.java
new file mode 100644
index 0000000..ecd0586
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/common/ICrashCallable.java
@@ -0,0 +1,8 @@
+package cpw.mods.fml.common;
+
+import java.util.concurrent.Callable;
+
+public interface ICrashCallable extends Callable
+{
+ String getLabel();
+}
diff --git a/src/minecraft/cpw/mods/fml/common/IDispenseHandler.java b/src/minecraft/cpw/mods/fml/common/IDispenseHandler.java
new file mode 100644
index 0000000..7794882
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/common/IDispenseHandler.java
@@ -0,0 +1,48 @@
+/*
+ * The FML Forge Mod Loader suite. Copyright (C) 2012 cpw
+ *
+ * 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
+ * Software Foundation; either version 2.1 of the License, or any later version.
+ *
+ * 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
+ * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * 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
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+package cpw.mods.fml.common;
+
+import java.util.Random;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.world.World;
+
+
+/**
+ * Deprecated without replacement, use vanilla DispenserRegistry code
+ *
+ * @author cpw
+ *
+ */
+@Deprecated
+public interface IDispenseHandler
+{
+ /**
+ * Return -1 if you don't want to dispense anything. the other values seem to have specific meanings
+ * to blockdispenser.
+ *
+ * @param x
+ * @param y
+ * @param z
+ * @param xVelocity
+ * @param zVelocity
+ * @param world
+ * @param item
+ * @param random
+ * @param entX
+ * @param entY
+ * @param entZ
+ */
+ @Deprecated
+ int dispense(double x, double y, double z, int xVelocity, int zVelocity, World world, ItemStack item, Random random, double entX, double entY, double entZ);
+}
diff --git a/src/minecraft/cpw/mods/fml/common/IDispenserHandler.java b/src/minecraft/cpw/mods/fml/common/IDispenserHandler.java
new file mode 100644
index 0000000..acbb407
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/common/IDispenserHandler.java
@@ -0,0 +1,33 @@
+package cpw.mods.fml.common;
+
+import java.util.Random;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.world.World;
+
+/**
+ *
+ * Deprecated without replacement. Use vanilla DispenserRegistry code.
+ *
+ * @author cpw
+ *
+ */
+@Deprecated
+public interface IDispenserHandler
+{
+ /**
+ * Called to dispense an entity
+ * @param x
+ * @param y
+ * @param z
+ * @param xVelocity
+ * @param zVelocity
+ * @param world
+ * @param item
+ * @param random
+ * @param entX
+ * @param entY
+ * @param entZ
+ */
+ int dispense(int x, int y, int z, int xVelocity, int zVelocity, World world, ItemStack item, Random random, double entX, double entY, double entZ);
+}
diff --git a/src/minecraft/cpw/mods/fml/common/IFMLHandledException.java b/src/minecraft/cpw/mods/fml/common/IFMLHandledException.java
new file mode 100644
index 0000000..a9f52bf
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/common/IFMLHandledException.java
@@ -0,0 +1,6 @@
+package cpw.mods.fml.common;
+
+public interface IFMLHandledException
+{
+
+}
diff --git a/src/minecraft/cpw/mods/fml/common/IFMLSidedHandler.java b/src/minecraft/cpw/mods/fml/common/IFMLSidedHandler.java
new file mode 100644
index 0000000..9f57b03
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/common/IFMLSidedHandler.java
@@ -0,0 +1,53 @@
+package cpw.mods.fml.common;
+
+import java.util.List;
+
+import com.google.common.collect.MapDifference;
+
+import net.minecraft.entity.Entity;
+import net.minecraft.network.INetworkManager;
+import net.minecraft.network.packet.NetHandler;
+import net.minecraft.network.packet.Packet;
+import net.minecraft.network.packet.Packet131MapData;
+import net.minecraft.server.MinecraftServer;
+import cpw.mods.fml.common.network.EntitySpawnAdjustmentPacket;
+import cpw.mods.fml.common.network.EntitySpawnPacket;
+import cpw.mods.fml.common.network.ModMissingPacket;
+import cpw.mods.fml.common.registry.ItemData;
+import cpw.mods.fml.common.registry.EntityRegistry.EntityRegistration;
+import cpw.mods.fml.relauncher.Side;
+
+public interface IFMLSidedHandler
+{
+ List getAdditionalBrandingInformation();
+
+ Side getSide();
+
+ void haltGame(String message, Throwable exception);
+
+ void showGuiScreen(Object clientGuiElement);
+
+ Entity spawnEntityIntoClientWorld(EntityRegistration registration, EntitySpawnPacket packet);
+
+ void adjustEntityLocationOnClient(EntitySpawnAdjustmentPacket entitySpawnAdjustmentPacket);
+
+ void beginServerLoading(MinecraftServer server);
+
+ void finishServerLoading();
+
+ MinecraftServer getServer();
+
+ void sendPacket(Packet packet);
+
+ void displayMissingMods(ModMissingPacket modMissingPacket);
+
+ void handleTinyPacket(NetHandler handler, Packet131MapData mapData);
+
+ void setClientCompatibilityLevel(byte compatibilityLevel);
+
+ byte getClientCompatibilityLevel();
+
+ boolean shouldServerShouldBeKilledQuietly();
+
+ void disconnectIDMismatch(MapDifference s, NetHandler toKill, INetworkManager mgr);
+}
diff --git a/src/minecraft/cpw/mods/fml/common/IFuelHandler.java b/src/minecraft/cpw/mods/fml/common/IFuelHandler.java
new file mode 100644
index 0000000..67e8a7f
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/common/IFuelHandler.java
@@ -0,0 +1,8 @@
+package cpw.mods.fml.common;
+
+import net.minecraft.item.ItemStack;
+
+public interface IFuelHandler
+{
+ int getBurnTime(ItemStack fuel);
+}
\ No newline at end of file
diff --git a/src/minecraft/cpw/mods/fml/common/INetworkHandler.java b/src/minecraft/cpw/mods/fml/common/INetworkHandler.java
new file mode 100644
index 0000000..b41a8d1
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/common/INetworkHandler.java
@@ -0,0 +1,26 @@
+/*
+ * The FML Forge Mod Loader suite. Copyright (C) 2012 cpw
+ *
+ * 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
+ * Software Foundation; either version 2.1 of the License, or any later version.
+ *
+ * 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
+ * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * 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
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+package cpw.mods.fml.common;
+
+
+
+/**
+ * @author cpw
+ *
+ */
+public interface INetworkHandler
+{
+ boolean onChat(Object... data);
+ void onPacket250Packet(Object... data);
+ void onServerLogin(Object handler);
+}
diff --git a/src/minecraft/cpw/mods/fml/common/IPickupNotifier.java b/src/minecraft/cpw/mods/fml/common/IPickupNotifier.java
new file mode 100644
index 0000000..ad4221b
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/common/IPickupNotifier.java
@@ -0,0 +1,22 @@
+/*
+ * The FML Forge Mod Loader suite.
+ * Copyright (C) 2012 cpw
+ *
+ * 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
+ * Software Foundation; either version 2.1 of the License, or any later version.
+ *
+ * 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
+ * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * 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
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+package cpw.mods.fml.common;
+
+import net.minecraft.entity.item.EntityItem;
+import net.minecraft.entity.player.EntityPlayer;
+
+public interface IPickupNotifier
+{
+ void notifyPickup(EntityItem item, EntityPlayer player);
+}
diff --git a/src/minecraft/cpw/mods/fml/common/IPlayerTracker.java b/src/minecraft/cpw/mods/fml/common/IPlayerTracker.java
new file mode 100644
index 0000000..d380207
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/common/IPlayerTracker.java
@@ -0,0 +1,32 @@
+/*
+ * The FML Forge Mod Loader suite.
+ * Copyright (C) 2012 cpw
+ *
+ * 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
+ * Software Foundation; either version 2.1 of the License, or any later version.
+ *
+ * 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
+ * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * 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
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package cpw.mods.fml.common;
+
+import net.minecraft.entity.player.EntityPlayer;
+
+/**
+ * @author cpw
+ *
+ */
+public interface IPlayerTracker
+{
+ void onPlayerLogin(EntityPlayer player);
+
+ void onPlayerLogout(EntityPlayer player);
+
+ void onPlayerChangedDimension(EntityPlayer player);
+
+ void onPlayerRespawn(EntityPlayer player);
+}
diff --git a/src/minecraft/cpw/mods/fml/common/IScheduledTickHandler.java b/src/minecraft/cpw/mods/fml/common/IScheduledTickHandler.java
new file mode 100644
index 0000000..852ca1b
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/common/IScheduledTickHandler.java
@@ -0,0 +1,12 @@
+package cpw.mods.fml.common;
+
+public interface IScheduledTickHandler extends ITickHandler
+{
+ /**
+ * Return the number of actual ticks that will pass
+ * before your next tick will fire. This will be called
+ * just after your last tick fired to compute the next delay.
+ * @return Time until next tick
+ */
+ public int nextTickSpacing();
+}
diff --git a/src/minecraft/cpw/mods/fml/common/ITickHandler.java b/src/minecraft/cpw/mods/fml/common/ITickHandler.java
new file mode 100644
index 0000000..7e145e6
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/common/ITickHandler.java
@@ -0,0 +1,61 @@
+/*
+ * The FML Forge Mod Loader suite.
+ * Copyright (C) 2012 cpw
+ *
+ * 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
+ * Software Foundation; either version 2.1 of the License, or any later version.
+ *
+ * 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
+ * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * 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
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package cpw.mods.fml.common;
+
+import java.util.EnumSet;
+
+
+/**
+ *
+ * Tick handler for mods to implement and register through the TickRegistry
+ *
+ * The data available to each tick is documented in the TickType
+ *
+ * @author cpw
+ *
+ */
+public interface ITickHandler
+{
+
+ /**
+ * Called at the "start" phase of a tick
+ *
+ * Multiple ticks may fire simultaneously- you will only be called once with all the firing ticks
+ *
+ * @param type
+ * @param tickData
+ */
+ public void tickStart(EnumSet type, Object... tickData);
+
+ /**
+ * Called at the "end" phase of a tick
+ *
+ * Multiple ticks may fire simultaneously- you will only be called once with all the firing ticks
+ *
+ * @param type
+ * @param tickData
+ */
+ public void tickEnd(EnumSet type, Object... tickData);
+
+ /**
+ * Returns the list of ticks this tick handler is interested in receiving at the minute
+ */
+ public EnumSet ticks();
+
+ /**
+ * A profiling label for this tick handler
+ */
+ public String getLabel();
+}
diff --git a/src/minecraft/cpw/mods/fml/common/IWorldGenerator.java b/src/minecraft/cpw/mods/fml/common/IWorldGenerator.java
new file mode 100644
index 0000000..1520e55
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/common/IWorldGenerator.java
@@ -0,0 +1,42 @@
+/*
+ * The FML Forge Mod Loader suite.
+ * Copyright (C) 2012 cpw
+ *
+ * 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
+ * Software Foundation; either version 2.1 of the License, or any later version.
+ *
+ * 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
+ * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * 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
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+package cpw.mods.fml.common;
+
+import java.util.Random;
+
+import net.minecraft.world.World;
+import net.minecraft.world.chunk.IChunkProvider;
+
+
+/**
+ * This is called back during world generation.
+ *
+ * @author cpw
+ *
+ */
+public interface IWorldGenerator
+{
+ /**
+ * Generate some world
+ *
+ * @param random the chunk specific {@link Random}.
+ * @param chunkX the chunk X coordinate of this chunk.
+ * @param chunkZ the chunk Z coordinate of this chunk.
+ * @param world : additionalData[0] The minecraft {@link World} we're generating for.
+ * @param chunkGenerator : additionalData[1] The {@link IChunkProvider} that is generating.
+ * @param chunkProvider : additionalData[2] {@link IChunkProvider} that is requesting the world generation.
+ *
+ */
+ public void generate(Random random, int chunkX, int chunkZ, World world, IChunkProvider chunkGenerator, IChunkProvider chunkProvider);
+}
diff --git a/src/minecraft/cpw/mods/fml/common/InjectedModContainer.java b/src/minecraft/cpw/mods/fml/common/InjectedModContainer.java
new file mode 100644
index 0000000..5c51028
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/common/InjectedModContainer.java
@@ -0,0 +1,139 @@
+package cpw.mods.fml.common;
+
+import java.io.File;
+import java.security.cert.Certificate;
+import java.util.List;
+import java.util.Set;
+
+import com.google.common.eventbus.EventBus;
+
+import cpw.mods.fml.common.versioning.ArtifactVersion;
+import cpw.mods.fml.common.versioning.VersionRange;
+
+public class InjectedModContainer implements ModContainer
+{
+ private File source;
+ private ModContainer wrappedContainer;
+
+ public InjectedModContainer(ModContainer mc, File source)
+ {
+ this.source = source;
+ this.wrappedContainer = mc;
+ }
+
+ public String getModId()
+ {
+ return wrappedContainer.getModId();
+ }
+
+ public String getName()
+ {
+ return wrappedContainer.getName();
+ }
+
+ public String getVersion()
+ {
+ return wrappedContainer.getVersion();
+ }
+
+ public File getSource()
+ {
+ return source;
+ }
+
+ public ModMetadata getMetadata()
+ {
+ return wrappedContainer.getMetadata();
+ }
+
+ public void bindMetadata(MetadataCollection mc)
+ {
+ wrappedContainer.bindMetadata(mc);
+ }
+
+ public void setEnabledState(boolean enabled)
+ {
+ wrappedContainer.setEnabledState(enabled);
+ }
+
+ public Set getRequirements()
+ {
+ return wrappedContainer.getRequirements();
+ }
+
+ public List getDependencies()
+ {
+ return wrappedContainer.getDependencies();
+ }
+
+ public List getDependants()
+ {
+ return wrappedContainer.getDependants();
+ }
+
+ public String getSortingRules()
+ {
+ return wrappedContainer.getSortingRules();
+ }
+
+ public boolean registerBus(EventBus bus, LoadController controller)
+ {
+ return wrappedContainer.registerBus(bus, controller);
+ }
+
+ public boolean matches(Object mod)
+ {
+ return wrappedContainer.matches(mod);
+ }
+
+ public Object getMod()
+ {
+ return wrappedContainer.getMod();
+ }
+
+ public ArtifactVersion getProcessedVersion()
+ {
+ return wrappedContainer.getProcessedVersion();
+ }
+
+ @Override
+ public boolean isNetworkMod()
+ {
+ return wrappedContainer.isNetworkMod();
+ }
+ @Override
+ public boolean isImmutable()
+ {
+ return true;
+ }
+
+ @Override
+ public String getDisplayVersion()
+ {
+ return wrappedContainer.getDisplayVersion();
+ }
+
+ @Override
+ public VersionRange acceptableMinecraftVersionRange()
+ {
+ return wrappedContainer.acceptableMinecraftVersionRange();
+ }
+
+ public WorldAccessContainer getWrappedWorldAccessContainer()
+ {
+ if (wrappedContainer instanceof WorldAccessContainer)
+ {
+ return (WorldAccessContainer) wrappedContainer;
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ @Override
+ public Certificate getSigningCertificate()
+ {
+ return wrappedContainer.getSigningCertificate();
+ }
+}
diff --git a/src/minecraft/cpw/mods/fml/common/LoadController.java b/src/minecraft/cpw/mods/fml/common/LoadController.java
new file mode 100644
index 0000000..123e996
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/common/LoadController.java
@@ -0,0 +1,240 @@
+package cpw.mods.fml.common;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.logging.Level;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMap.Builder;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
+import com.google.common.eventbus.EventBus;
+import com.google.common.eventbus.Subscribe;
+
+import cpw.mods.fml.common.LoaderState.ModState;
+import cpw.mods.fml.common.event.FMLEvent;
+import cpw.mods.fml.common.event.FMLLoadEvent;
+import cpw.mods.fml.common.event.FMLPreInitializationEvent;
+import cpw.mods.fml.common.event.FMLStateEvent;
+
+public class LoadController
+{
+ private Loader loader;
+ private EventBus masterChannel;
+ private ImmutableMap eventChannels;
+ private LoaderState state;
+ private Multimap modStates = ArrayListMultimap.create();
+ private Multimap errors = ArrayListMultimap.create();
+ private Map modList;
+ private List activeModList = Lists.newArrayList();
+ private ModContainer activeContainer;
+ private BiMap modObjectList;
+
+ public LoadController(Loader loader)
+ {
+ this.loader = loader;
+ this.masterChannel = new EventBus("FMLMainChannel");
+ this.masterChannel.register(this);
+
+ state = LoaderState.NOINIT;
+
+
+ }
+
+ @Subscribe
+ public void buildModList(FMLLoadEvent event)
+ {
+ this.modList = loader.getIndexedModList();
+ Builder eventBus = ImmutableMap.builder();
+
+ for (ModContainer mod : loader.getModList())
+ {
+ EventBus bus = new EventBus(mod.getModId());
+ boolean isActive = mod.registerBus(bus, this);
+ if (isActive)
+ {
+ FMLLog.fine("Activating mod %s", mod.getModId());
+ activeModList.add(mod);
+ modStates.put(mod.getModId(), ModState.UNLOADED);
+ eventBus.put(mod.getModId(), bus);
+ }
+ else
+ {
+ FMLLog.warning("Mod %s has been disabled through configuration", mod.getModId());
+ modStates.put(mod.getModId(), ModState.UNLOADED);
+ modStates.put(mod.getModId(), ModState.DISABLED);
+ }
+ }
+
+ eventChannels = eventBus.build();
+ }
+
+ public void distributeStateMessage(LoaderState state, Object... eventData)
+ {
+ if (state.hasEvent())
+ {
+ masterChannel.post(state.getEvent(eventData));
+ }
+ }
+
+ public void transition(LoaderState desiredState)
+ {
+ LoaderState oldState = state;
+ state = state.transition(!errors.isEmpty());
+ if (state != desiredState)
+ {
+ Throwable toThrow = null;
+ FMLLog.severe("Fatal errors were detected during the transition from %s to %s. Loading cannot continue", oldState, desiredState);
+ StringBuilder sb = new StringBuilder();
+ printModStates(sb);
+ FMLLog.getLogger().severe(sb.toString());
+ FMLLog.severe("The following problems were captured during this phase");
+ for (Entry error : errors.entries())
+ {
+ FMLLog.log(Level.SEVERE, error.getValue(), "Caught exception from %s", error.getKey());
+ if (error.getValue() instanceof IFMLHandledException)
+ {
+ toThrow = error.getValue();
+ }
+ else if (toThrow == null)
+ {
+ toThrow = error.getValue();
+ }
+ }
+ if (toThrow != null && toThrow instanceof RuntimeException)
+ {
+ throw (RuntimeException)toThrow;
+ }
+ else
+ {
+ throw new LoaderException(toThrow);
+ }
+ }
+ }
+
+ public ModContainer activeContainer()
+ {
+ return activeContainer;
+ }
+
+ @Subscribe
+ public void propogateStateMessage(FMLEvent stateEvent)
+ {
+ if (stateEvent instanceof FMLPreInitializationEvent)
+ {
+ modObjectList = buildModObjectList();
+ }
+ for (ModContainer mc : activeModList)
+ {
+ activeContainer = mc;
+ String modId = mc.getModId();
+ stateEvent.applyModContainer(activeContainer());
+ FMLLog.finer("Sending event %s to mod %s", stateEvent.getEventType(), modId);
+ eventChannels.get(modId).post(stateEvent);
+ FMLLog.finer("Sent event %s to mod %s", stateEvent.getEventType(), modId);
+ activeContainer = null;
+ if (stateEvent instanceof FMLStateEvent)
+ {
+ if (!errors.containsKey(modId))
+ {
+ modStates.put(modId, ((FMLStateEvent)stateEvent).getModState());
+ }
+ else
+ {
+ modStates.put(modId, ModState.ERRORED);
+ }
+ }
+ }
+ }
+
+ public ImmutableBiMap buildModObjectList()
+ {
+ ImmutableBiMap.Builder builder = ImmutableBiMap.builder();
+ for (ModContainer mc : activeModList)
+ {
+ if (!mc.isImmutable() && mc.getMod()!=null)
+ {
+ builder.put(mc, mc.getMod());
+ }
+ if (mc.getMod()==null && !mc.isImmutable() && state!=LoaderState.CONSTRUCTING)
+ {
+ FMLLog.severe("There is a severe problem with %s - it appears not to have constructed correctly", mc.getModId());
+ if (state != LoaderState.CONSTRUCTING)
+ {
+ this.errorOccurred(mc, new RuntimeException());
+ }
+ }
+ }
+ return builder.build();
+ }
+
+ public void errorOccurred(ModContainer modContainer, Throwable exception)
+ {
+ if (exception instanceof InvocationTargetException)
+ {
+ errors.put(modContainer.getModId(), ((InvocationTargetException)exception).getCause());
+ }
+ else
+ {
+ errors.put(modContainer.getModId(), exception);
+ }
+ }
+
+ public void printModStates(StringBuilder ret)
+ {
+ for (ModContainer mc : loader.getModList())
+ {
+ ret.append("\n\t").append(mc.getModId()).append(" [").append(mc.getName()).append("] (").append(mc.getSource().getName()).append(") ");
+ Joiner.on("->"). appendTo(ret, modStates.get(mc.getModId()));
+ }
+ }
+
+ public List getActiveModList()
+ {
+ return activeModList;
+ }
+
+ public ModState getModState(ModContainer selectedMod)
+ {
+ return Iterables.getLast(modStates.get(selectedMod.getModId()), ModState.AVAILABLE);
+ }
+
+ public void distributeStateMessage(Class> customEvent)
+ {
+ try
+ {
+ masterChannel.post(customEvent.newInstance());
+ }
+ catch (Exception e)
+ {
+ FMLLog.log(Level.SEVERE, e, "An unexpected exception");
+ throw new LoaderException(e);
+ }
+ }
+
+ public BiMap getModObjectList()
+ {
+ if (modObjectList == null)
+ {
+ FMLLog.severe("Detected an attempt by a mod %s to perform game activity during mod construction. This is a serious programming error.", activeContainer);
+ return buildModObjectList();
+ }
+ return ImmutableBiMap.copyOf(modObjectList);
+ }
+
+ public boolean isInState(LoaderState state)
+ {
+ return this.state == state;
+ }
+
+ boolean hasReachedState(LoaderState state) {
+ return this.state.ordinal()>=state.ordinal() && this.state!=LoaderState.ERRORED;
+ }
+}
diff --git a/src/minecraft/cpw/mods/fml/common/Loader.java b/src/minecraft/cpw/mods/fml/common/Loader.java
new file mode 100644
index 0000000..d66ec02
--- /dev/null
+++ b/src/minecraft/cpw/mods/fml/common/Loader.java
@@ -0,0 +1,755 @@
+/*
+ * The FML Forge Mod Loader suite.
+ * Copyright (C) 2012 cpw
+ *
+ * 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
+ * Software Foundation; either version 2.1 of the License, or any later version.
+ *
+ * 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
+ * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * 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
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+package cpw.mods.fml.common;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.logging.Level;
+
+import net.minecraft.crash.CallableMinecraftVersion;
+import net.minecraft.server.MinecraftServer;
+
+import com.google.common.base.CharMatcher;
+import com.google.common.base.Function;
+import com.google.common.base.Joiner;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMultiset;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.SetMultimap;
+import com.google.common.collect.Sets;
+import com.google.common.collect.Multiset.Entry;
+import com.google.common.collect.Multisets;
+import com.google.common.collect.Ordering;
+import com.google.common.collect.Sets.SetView;
+import com.google.common.collect.TreeMultimap;
+
+import cpw.mods.fml.common.LoaderState.ModState;
+import cpw.mods.fml.common.discovery.ModDiscoverer;
+import cpw.mods.fml.common.event.FMLInterModComms;
+import cpw.mods.fml.common.event.FMLLoadEvent;
+import cpw.mods.fml.common.functions.ModIdFunction;
+import cpw.mods.fml.common.modloader.BaseModProxy;
+import cpw.mods.fml.common.toposort.ModSorter;
+import cpw.mods.fml.common.toposort.ModSortingException;
+import cpw.mods.fml.common.toposort.TopologicalSort;
+import cpw.mods.fml.common.versioning.ArtifactVersion;
+import cpw.mods.fml.common.versioning.VersionParser;
+
+/**
+ * The loader class performs the actual loading of the mod code from disk.
+ *
+ *
+ * There are several {@link LoaderState}s to mod loading, triggered in two
+ * different stages from the FML handler code's hooks into the minecraft code.
+ *
+ *
+ *
+ *
LOADING. Scanning the filesystem for mod containers to load (zips, jars,
+ * directories), adding them to the {@link #modClassLoader} Scanning, the loaded
+ * containers for mod classes to load and registering them appropriately.
+ *
PREINIT. The mod classes are configured, they are sorted into a load
+ * order, and instances of the mods are constructed.
+ *
INIT. The mod instances are initialized. For BaseMod mods, this involves
+ * calling the load method.
+ *
POSTINIT. The mod instances are post initialized. For BaseMod mods this
+ * involves calling the modsLoaded method.
+ *
UP. The Loader is complete
+ *
ERRORED. The loader encountered an error during the LOADING phase and
+ * dropped to this state instead. It will not complete loading from this state,
+ * but it attempts to continue loading before abandoning and giving a fatal
+ * error.
+ *
+ *
+ * Phase 1 code triggers the LOADING and PREINIT states. Phase 2 code triggers
+ * the INIT and POSTINIT states.
+ *
+ * @author cpw
+ *
+ */
+public class Loader
+{
+ private static final Splitter DEPENDENCYPARTSPLITTER = Splitter.on(":").omitEmptyStrings().trimResults();
+ private static final Splitter DEPENDENCYSPLITTER = Splitter.on(";").omitEmptyStrings().trimResults();
+ /**
+ * The singleton instance
+ */
+ private static Loader instance;
+ /**
+ * Build information for tracking purposes.
+ */
+ private static String major;
+ private static String minor;
+ private static String rev;
+ private static String build;
+ private static String mccversion;
+ private static String mcpversion;
+
+ /**
+ * The class loader we load the mods into.
+ */
+ private ModClassLoader modClassLoader;
+ /**
+ * The sorted list of mods.
+ */
+ private List mods;
+ /**
+ * A named list of mods
+ */
+ private Map namedMods;
+ /**
+ * The canonical configuration directory
+ */
+ private File canonicalConfigDir;
+ /**
+ * The canonical minecraft directory
+ */
+ private File canonicalMinecraftDir;
+ /**
+ * The captured error
+ */
+ private Exception capturedError;
+ private File canonicalModsDir;
+ private LoadController modController;
+ private MinecraftDummyContainer minecraft;
+ private MCPDummyContainer mcp;
+
+ private static File minecraftDir;
+ private static List injectedContainers;
+
+ public static Loader instance()
+ {
+ if (instance == null)
+ {
+ instance = new Loader();
+ }
+
+ return instance;
+ }
+
+ public static void injectData(Object... data)
+ {
+ major = (String) data[0];
+ minor = (String) data[1];
+ rev = (String) data[2];
+ build = (String) data[3];
+ mccversion = (String) data[4];
+ mcpversion = (String) data[5];
+ minecraftDir = (File) data[6];
+ injectedContainers = (List)data[7];
+ }
+
+ private Loader()
+ {
+ modClassLoader = new ModClassLoader(getClass().getClassLoader());
+ String actualMCVersion = new CallableMinecraftVersion(null).minecraftVersion();
+ if (!mccversion.equals(actualMCVersion))
+ {
+ FMLLog.severe("This version of FML is built for Minecraft %s, we have detected Minecraft %s in your minecraft jar file", mccversion, actualMCVersion);
+ throw new LoaderException();
+ }
+
+ minecraft = new MinecraftDummyContainer(actualMCVersion);
+ mcp = new MCPDummyContainer(MetadataCollection.from(getClass().getResourceAsStream("/mcpmod.info"), "MCP").getMetadataForId("mcp", null));
+ }
+
+ /**
+ * Sort the mods into a sorted list, using dependency information from the
+ * containers. The sorting is performed using a {@link TopologicalSort}
+ * based on the pre- and post- dependency information provided by the mods.
+ */
+ private void sortModList()
+ {
+ FMLLog.fine("Verifying mod requirements are satisfied");
+ try
+ {
+ BiMap modVersions = HashBiMap.create();
+ for (ModContainer mod : getActiveModList())
+ {
+ modVersions.put(mod.getModId(), mod.getProcessedVersion());
+ }
+
+ for (ModContainer mod : getActiveModList())
+ {
+ if (!mod.acceptableMinecraftVersionRange().containsVersion(minecraft.getProcessedVersion()))
+ {
+ FMLLog.severe("The mod %s does not wish to run in Minecraft version %s. You will have to remove it to play.", mod.getModId(), getMCVersionString());
+ throw new WrongMinecraftVersionException(mod);
+ }
+ Map names = Maps.uniqueIndex(mod.getRequirements(), new Function()
+ {
+ public String apply(ArtifactVersion v)
+ {
+ return v.getLabel();
+ }
+ });
+ Set versionMissingMods = Sets.newHashSet();
+ Set missingMods = Sets.difference(names.keySet(), modVersions.keySet());
+ if (!missingMods.isEmpty())
+ {
+ FMLLog.severe("The mod %s (%s) requires mods %s to be available", mod.getModId(), mod.getName(), missingMods);
+ for (String modid : missingMods)
+ {
+ versionMissingMods.add(names.get(modid));
+ }
+ throw new MissingModsException(versionMissingMods);
+ }
+ ImmutableList allDeps = ImmutableList.builder().addAll(mod.getDependants()).addAll(mod.getDependencies()).build();
+ for (ArtifactVersion v : allDeps)
+ {
+ if (modVersions.containsKey(v.getLabel()))
+ {
+ if (!v.containsVersion(modVersions.get(v.getLabel())))
+ {
+ versionMissingMods.add(v);
+ }
+ }
+ }
+ if (!versionMissingMods.isEmpty())
+ {
+ FMLLog.severe("The mod %s (%s) requires mod versions %s to be available", mod.getModId(), mod.getName(), versionMissingMods);
+ throw new MissingModsException(versionMissingMods);
+ }
+ }
+
+ FMLLog.fine("All mod requirements are satisfied");
+
+ ModSorter sorter = new ModSorter(getActiveModList(), namedMods);
+
+ try
+ {
+ FMLLog.fine("Sorting mods into an ordered list");
+ List sortedMods = sorter.sort();
+ // Reset active list to the sorted list
+ modController.getActiveModList().clear();
+ modController.getActiveModList().addAll(sortedMods);
+ // And inject the sorted list into the overall list
+ mods.removeAll(sortedMods);
+ sortedMods.addAll(mods);
+ mods = sortedMods;
+ FMLLog.fine("Mod sorting completed successfully");
+ }
+ catch (ModSortingException sortException)
+ {
+ FMLLog.severe("A dependency cycle was detected in the input mod set so an ordering cannot be determined");
+ FMLLog.severe("The visited mod list is %s", sortException.getExceptionData().getVisitedNodes());
+ FMLLog.severe("The first mod in the cycle is %s", sortException.getExceptionData().getFirstBadNode());
+ FMLLog.log(Level.SEVERE, sortException, "The full error");
+ throw new LoaderException(sortException);
+ }
+ }
+ finally
+ {
+ FMLLog.fine("Mod sorting data:");
+ for (ModContainer mod : getActiveModList())
+ {
+ if (!mod.isImmutable())
+ {
+ FMLLog.fine("\t%s(%s:%s): %s (%s)", mod.getModId(), mod.getName(), mod.getVersion(), mod.getSource().getName(), mod.getSortingRules());
+ }
+ }
+ if (mods.size()==0)
+ {
+ FMLLog.fine("No mods found to sort");
+ }
+ }
+
+ }
+
+ /**
+ * The primary loading code
+ *
+ * This is visited during first initialization by Minecraft to scan and load
+ * the mods from all sources 1. The minecraft jar itself (for loading of in
+ * jar mods- I would like to remove this if possible but forge depends on it
+ * at present) 2. The mods directory with expanded subdirs, searching for
+ * mods named mod_*.class 3. The mods directory for zip and jar files,
+ * searching for mod classes named mod_*.class again
+ *
+ * The found resources are first loaded into the {@link #modClassLoader}
+ * (always) then scanned for class resources matching the specification
+ * above.
+ *
+ * If they provide the {@link Mod} annotation, they will be loaded as
+ * "FML mods", which currently is effectively a NO-OP. If they are
+ * determined to be {@link BaseModProxy} subclasses they are loaded as such.
+ *
+ * Finally, if they are successfully loaded as classes, they are then added
+ * to the available mod list.
+ */
+ private ModDiscoverer identifyMods()
+ {
+ FMLLog.fine("Building injected Mod Containers %s", injectedContainers);
+ // Add in the MCP mod container
+ mods.add(new InjectedModContainer(mcp,new File("minecraft.jar")));
+ File coremod = new File(minecraftDir,"coremods");
+ for (String cont : injectedContainers)
+ {
+ ModContainer mc;
+ try
+ {
+ mc = (ModContainer) Class.forName(cont,true,modClassLoader).newInstance();
+ }
+ catch (Exception e)
+ {
+ FMLLog.log(Level.SEVERE, e, "A problem occured instantiating the injected mod container %s", cont);
+ throw new LoaderException(e);
+ }
+ mods.add(new InjectedModContainer(mc,coremod));
+ }
+ ModDiscoverer discoverer = new ModDiscoverer();
+ FMLLog.fine("Attempting to load mods contained in the minecraft jar file and associated classes");
+ discoverer.findClasspathMods(modClassLoader);
+ FMLLog.fine("Minecraft jar mods loaded successfully");
+
+ FMLLog.info("Searching %s for mods", canonicalModsDir.getAbsolutePath());
+ discoverer.findModDirMods(canonicalModsDir);
+
+ mods.addAll(discoverer.identifyMods());
+ identifyDuplicates(mods);
+ namedMods = Maps.uniqueIndex(mods, new ModIdFunction());
+ FMLLog.info("Forge Mod Loader has identified %d mod%s to load", mods.size(), mods.size() != 1 ? "s" : "");
+ return discoverer;
+ }
+
+ private class ModIdComparator implements Comparator
+ {
+ @Override
+ public int compare(ModContainer o1, ModContainer o2)
+ {
+ return o1.getModId().compareTo(o2.getModId());
+ }
+
+ }
+
+ private void identifyDuplicates(List mods)
+ {
+ TreeMultimap dupsearch = TreeMultimap.create(new ModIdComparator(), Ordering.arbitrary());
+ for (ModContainer mc : mods)
+ {
+ if (mc.getSource() != null)
+ {
+ dupsearch.put(mc, mc.getSource());
+ }
+ }
+
+ ImmutableMultiset duplist = Multisets.copyHighestCountFirst(dupsearch.keys());
+ SetMultimap dupes = LinkedHashMultimap.create();
+ for (Entry e : duplist.entrySet())
+ {
+ if (e.getCount() > 1)
+ {
+ FMLLog.severe("Found a duplicate mod %s at %s", e.getElement().getModId(), dupsearch.get(e.getElement()));
+ dupes.putAll(e.getElement(),dupsearch.get(e.getElement()));
+ }
+ }
+ if (!dupes.isEmpty())
+ {
+ throw new DuplicateModsFoundException(dupes);
+ }
+ }
+
+ /**
+ * @return
+ */
+ private void initializeLoader()
+ {
+ File modsDir = new File(minecraftDir, "mods");
+ File configDir = new File(minecraftDir, "config");
+ String canonicalModsPath;
+ String canonicalConfigPath;
+
+ try
+ {
+ canonicalMinecraftDir = minecraftDir.getCanonicalFile();
+ canonicalModsPath = modsDir.getCanonicalPath();
+ canonicalConfigPath = configDir.getCanonicalPath();
+ canonicalConfigDir = configDir.getCanonicalFile();
+ canonicalModsDir = modsDir.getCanonicalFile();
+ }
+ catch (IOException ioe)
+ {
+ FMLLog.log(Level.SEVERE, ioe, "Failed to resolve loader directories: mods : %s ; config %s", canonicalModsDir.getAbsolutePath(),
+ configDir.getAbsolutePath());
+ throw new LoaderException(ioe);
+ }
+
+ if (!canonicalModsDir.exists())
+ {
+ FMLLog.info("No mod directory found, creating one: %s", canonicalModsPath);
+ boolean dirMade = canonicalModsDir.mkdir();
+ if (!dirMade)
+ {
+ FMLLog.severe("Unable to create the mod directory %s", canonicalModsPath);
+ throw new LoaderException();
+ }
+ FMLLog.info("Mod directory created successfully");
+ }
+
+ if (!canonicalConfigDir.exists())
+ {
+ FMLLog.fine("No config directory found, creating one: %s", canonicalConfigPath);
+ boolean dirMade = canonicalConfigDir.mkdir();
+ if (!dirMade)
+ {
+ FMLLog.severe("Unable to create the config directory %s", canonicalConfigPath);
+ throw new LoaderException();
+ }
+ FMLLog.info("Config directory created successfully");
+ }
+
+ if (!canonicalModsDir.isDirectory())
+ {
+ FMLLog.severe("Attempting to load mods from %s, which is not a directory", canonicalModsPath);
+ throw new LoaderException();
+ }
+
+ if (!configDir.isDirectory())
+ {
+ FMLLog.severe("Attempting to load configuration from %s, which is not a directory", canonicalConfigPath);
+ throw new LoaderException();
+ }
+ }
+
+ public List getModList()
+ {
+ return instance().mods != null ? ImmutableList.copyOf(instance().mods) : ImmutableList.of();
+ }
+
+ /**
+ * Called from the hook to start mod loading. We trigger the
+ * {@link #identifyMods()} and Constructing, Preinitalization, and Initalization phases here. Finally,
+ * the mod list is frozen completely and is consider immutable from then on.
+ */
+ public void loadMods()
+ {
+ initializeLoader();
+ mods = Lists.newArrayList();
+ namedMods = Maps.newHashMap();
+ modController = new LoadController(this);
+ modController.transition(LoaderState.LOADING);
+ ModDiscoverer disc = identifyMods();
+ disableRequestedMods();
+ modController.distributeStateMessage(FMLLoadEvent.class);
+ sortModList();
+ mods = ImmutableList.copyOf(mods);
+ for (File nonMod : disc.getNonModLibs())
+ {
+ if (nonMod.isFile())
+ {
+ FMLLog.info("FML has found a non-mod file %s in your mods directory. It will now be injected into your classpath. This could severe stability issues, it should be removed if possible.", nonMod.getName());
+ try
+ {
+ modClassLoader.addFile(nonMod);
+ }
+ catch (MalformedURLException e)
+ {
+ FMLLog.log(Level.SEVERE, e, "Encountered a weird problem with non-mod file injection : %s", nonMod.getName());
+ }
+ }
+ }
+ modController.transition(LoaderState.CONSTRUCTING);
+ modController.distributeStateMessage(LoaderState.CONSTRUCTING, modClassLoader, disc.getASMTable());
+ FMLLog.fine("Mod signature data:");
+ for (ModContainer mod : getActiveModList())
+ {
+ FMLLog.fine("\t%s(%s:%s): %s (%s)", mod.getModId(), mod.getName(), mod.getVersion(), mod.getSource().getName(), CertificateHelper.getFingerprint(mod.getSigningCertificate()));
+ }
+ modController.transition(LoaderState.PREINITIALIZATION);
+ modController.distributeStateMessage(LoaderState.PREINITIALIZATION, disc.getASMTable(), canonicalConfigDir);
+ modController.transition(LoaderState.INITIALIZATION);
+ }
+
+ private void disableRequestedMods()
+ {
+ String forcedModList = System.getProperty("fml.modStates", "");
+ FMLLog.fine("Received a system property request \'%s\'",forcedModList);
+ Map sysPropertyStateList = Splitter.on(CharMatcher.anyOf(";:"))
+ .omitEmptyStrings().trimResults().withKeyValueSeparator("=")
+ .split(forcedModList);
+ FMLLog.fine("System property request managing the state of %d mods", sysPropertyStateList.size());
+ Map modStates = Maps.newHashMap();
+
+ File forcedModFile = new File(canonicalConfigDir, "fmlModState.properties");
+ Properties forcedModListProperties = new Properties();
+ if (forcedModFile.exists() && forcedModFile.isFile())
+ {
+ FMLLog.fine("Found a mod state file %s", forcedModFile.getName());
+ try
+ {
+ forcedModListProperties.load(new FileReader(forcedModFile));
+ FMLLog.fine("Loaded states for %d mods from file", forcedModListProperties.size());
+ }
+ catch (Exception e)
+ {
+ FMLLog.log(Level.INFO, e, "An error occurred reading the fmlModState.properties file");
+ }
+ }
+ modStates.putAll(Maps.fromProperties(forcedModListProperties));
+ modStates.putAll(sysPropertyStateList);
+ FMLLog.fine("After merging, found state information for %d mods", modStates.size());
+
+ Map isEnabled = Maps.transformValues(modStates, new Function()
+ {
+ public Boolean apply(String input)
+ {
+ return Boolean.parseBoolean(input);
+ }
+ });
+
+ for (Map.Entry entry : isEnabled.entrySet())
+ {
+ if (namedMods.containsKey(entry.getKey()))
+ {
+ FMLLog.info("Setting mod %s to enabled state %b", entry.getKey(), entry.getValue());
+ namedMods.get(entry.getKey()).setEnabledState(entry.getValue());
+ }
+ }
+ }
+
+ /**
+ * Query if we know of a mod named modname
+ *
+ * @param modname
+ * @return If the mod is loaded
+ */
+ public static boolean isModLoaded(String modname)
+ {
+ return instance().namedMods.containsKey(modname) && instance().modController.getModState(instance.namedMods.get(modname))!=ModState.DISABLED;
+ }
+
+ public File getConfigDir()
+ {
+ return canonicalConfigDir;
+ }
+
+ public String getCrashInformation()
+ {
+ StringBuilder ret = new StringBuilder();
+ List branding = FMLCommonHandler.instance().getBrandings();
+
+ Joiner.on(' ').skipNulls().appendTo(ret, branding.subList(1, branding.size()));
+ if (modController!=null)
+ {
+ modController.printModStates(ret);
+ }
+ return ret.toString();
+ }
+
+ public String getFMLVersionString()
+ {
+ return String.format("%s.%s.%s.%s", major, minor, rev, build);
+ }
+
+ public ClassLoader getModClassLoader()
+ {
+ return modClassLoader;
+ }
+
+ public void computeDependencies(String dependencyString, Set requirements, List dependencies, List dependants)
+ {
+ if (dependencyString == null || dependencyString.length() == 0)
+ {
+ return;
+ }
+
+ boolean parseFailure=false;
+
+ for (String dep : DEPENDENCYSPLITTER.split(dependencyString))
+ {
+ List depparts = Lists.newArrayList(DEPENDENCYPARTSPLITTER.split(dep));
+ // Need two parts to the string
+ if (depparts.size() != 2)
+ {
+ parseFailure=true;
+ continue;
+ }
+ String instruction = depparts.get(0);
+ String target = depparts.get(1);
+ boolean targetIsAll = target.startsWith("*");
+
+ // Cannot have an "all" relationship with anything except pure *
+ if (targetIsAll && target.length()>1)
+ {
+ parseFailure = true;
+ continue;
+ }
+
+ // If this is a required element, add it to the required list
+ if ("required-before".equals(instruction) || "required-after".equals(instruction))
+ {
+ // You can't require everything
+ if (!targetIsAll)
+ {
+ requirements.add(VersionParser.parseVersionReference(target));
+ }
+ else
+ {
+ parseFailure=true;
+ continue;
+ }
+ }
+
+ // You cannot have a versioned dependency on everything
+ if (targetIsAll && target.indexOf('@')>-1)
+ {
+ parseFailure = true;
+ continue;
+ }
+ // before elements are things we are loaded before (so they are our dependants)
+ if ("required-before".equals(instruction) || "before".equals(instruction))
+ {
+ dependants.add(VersionParser.parseVersionReference(target));
+ }
+ // after elements are things that load before we do (so they are out dependencies)
+ else if ("required-after".equals(instruction) || "after".equals(instruction))
+ {
+ dependencies.add(VersionParser.parseVersionReference(target));
+ }
+ else
+ {
+ parseFailure=true;
+ }
+ }
+
+ if (parseFailure)
+ {
+ FMLLog.log(Level.WARNING, "Unable to parse dependency string %s", dependencyString);
+ throw new LoaderException();
+ }
+ }
+
+ public Map getIndexedModList()
+ {
+ return ImmutableMap.copyOf(namedMods);
+ }
+
+ public void initializeMods()
+ {
+ // Mod controller should be in the initialization state here
+ modController.distributeStateMessage(LoaderState.INITIALIZATION);
+ modController.transition(LoaderState.POSTINITIALIZATION);
+ modController.distributeStateMessage(FMLInterModComms.IMCEvent.class);
+ modController.distributeStateMessage(LoaderState.POSTINITIALIZATION);
+ modController.transition(LoaderState.AVAILABLE);
+ modController.distributeStateMessage(LoaderState.AVAILABLE);
+ FMLLog.info("Forge Mod Loader has successfully loaded %d mod%s", mods.size(), mods.size()==1 ? "" : "s");
+ }
+
+ public ICrashCallable getCallableCrashInformation()
+ {
+ return new ICrashCallable() {
+ @Override
+ public String call() throws Exception
+ {
+ return getCrashInformation();
+ }
+
+ @Override
+ public String getLabel()
+ {
+ return "FML";
+ }
+ };
+ }
+
+ public List getActiveModList()
+ {
+ return modController != null ? modController.getActiveModList() : ImmutableList.of();
+ }
+
+ public ModState getModState(ModContainer selectedMod)
+ {
+ return modController.getModState(selectedMod);
+ }
+
+ public String getMCVersionString()
+ {
+ return "Minecraft " + mccversion;
+ }
+
+ public void serverStarting(Object server)
+ {
+ modController.distributeStateMessage(LoaderState.SERVER_STARTING, server);
+ modController.transition(LoaderState.SERVER_STARTING);
+ }
+
+ public void serverStarted()
+ {
+ modController.distributeStateMessage(LoaderState.SERVER_STARTED);
+ modController.transition(LoaderState.SERVER_STARTED);
+ }
+
+ public void serverStopping()
+ {
+ modController.distributeStateMessage(LoaderState.SERVER_STOPPING);
+ modController.transition(LoaderState.SERVER_STOPPING);
+ }
+
+ public BiMap getModObjectList()
+ {
+ return modController.getModObjectList();
+ }
+
+ public BiMap