From 9f327fd3929dbffa2bb8cc54fb1f95066f354d23 Mon Sep 17 00:00:00 2001 From: LexManos Date: Fri, 6 Apr 2012 06:10:11 -0700 Subject: [PATCH] Updated repo scripts to python, allowing more flexability and less code duplication. --- forge/.gitignore | 2 +- forge/build.py | 57 + forge/download_fernflower.py | 10 - forge/fml-src-1.0.0.45.zip | Bin 0 -> 59373 bytes forge/forge.py | 187 +++ .../net/minecraft/src/forge/ForgeHooks.java | 1188 ++++++++--------- forge/inject_version.bat | 20 - forge/inject_version.sh | 14 - forge/lfcr.py | 8 - forge/package.sh | 69 - forge/release.bat | 16 +- forge/release.py | 149 +++ forge/setup.bat | 48 +- forge/setup.py | 93 ++ forge/version.txt | 1 - 15 files changed, 1083 insertions(+), 779 deletions(-) create mode 100644 forge/build.py delete mode 100644 forge/download_fernflower.py create mode 100644 forge/fml-src-1.0.0.45.zip create mode 100644 forge/forge.py delete mode 100644 forge/inject_version.bat delete mode 100755 forge/inject_version.sh delete mode 100644 forge/lfcr.py delete mode 100755 forge/package.sh create mode 100644 forge/release.py create mode 100644 forge/setup.py delete mode 100644 forge/version.txt diff --git a/forge/.gitignore b/forge/.gitignore index f6406060eb..f10ab5531c 100644 --- a/forge/.gitignore +++ b/forge/.gitignore @@ -1,4 +1,4 @@ -/*.zip /fml/ /logs/ /*.pyc +/forge-*/ diff --git a/forge/build.py b/forge/build.py new file mode 100644 index 0000000000..a0c4e5b261 --- /dev/null +++ b/forge/build.py @@ -0,0 +1,57 @@ +import os, os.path, sys +import shutil, fnmatch +import logging + +forge_dir = os.path.dirname(os.path.abspath(__file__)) +mcp_dir = os.path.abspath('..') +src_dir = os.path.join(mcp_dir, 'src') + +sys.path.append(mcp_dir) +from runtime.recompile import recompile + +from forge import copytree, reset_logger, load_version, inject_version + +def main(): + build_num = 0 + if len(sys.argv) > 1: + try: + build_num = int(sys.argv[1]) + except: + pass + sys.exit(build(build_num)) + +def build(build_num=0): + version = load_version(build_num) + print '=================================== Build %d.%d.%d.%d Start =================================' % (version['major'], version['minor'], version['revision'], version['build']) + + if os.path.isdir(src_dir): + shutil.rmtree(src_dir) + inject_version(os.path.join(forge_dir, 'forge_common', 'net', 'minecraft', 'src', 'forge', 'ForgeHooks.java'), build_num) + + print 'src_work -> src' + copytree(os.path.join(mcp_dir, 'src_work'), src_dir) + print '\nforge_client -> minecraft' + copytree(os.path.join(forge_dir, 'forge_client', 'src'), os.path.join(src_dir, 'minecraft'), -1) + print '\nforge_server -> minecraft_server' + copytree(os.path.join(forge_dir, 'forge_server', 'src'), os.path.join(src_dir, 'minecraft_server'), -1) + print '\nforge_common -> minecraft' + copytree(os.path.join(forge_dir, 'forge_common'), os.path.join(src_dir, 'minecraft'), -1) + print '\nforge_common -> minecraft_server' + copytree(os.path.join(forge_dir, 'forge_common'), os.path.join(src_dir, 'minecraft_server'), -1) + print + + error_level = 0 + try: + os.chdir(mcp_dir) + recompile(None) + reset_logger() + os.chdir(forge_dir) + except SystemExit, e: + print 'Decompile Exception: %d ' % e.code + error_level = e.code + + print '=================================== Build Finished %d =================================' % error_level + return error_level + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/forge/download_fernflower.py b/forge/download_fernflower.py deleted file mode 100644 index c10e08fd8c..0000000000 --- a/forge/download_fernflower.py +++ /dev/null @@ -1,10 +0,0 @@ -import urllib -import zipfile - -if __name__ == '__main__': - try: - urllib.urlretrieve("http://goo.gl/PnJHp", './fernflower.zip') - zf = zipfile.ZipFile('fernflower.zip') - zf.extract('fernflower.jar', '../runtime/bin') - except: - print "Downloading Fernflower failed download manually from http://goo.gl/PnJHp" \ No newline at end of file diff --git a/forge/fml-src-1.0.0.45.zip b/forge/fml-src-1.0.0.45.zip new file mode 100644 index 0000000000000000000000000000000000000000..087df171e8e100d18e3d977cb861627f9c589c6d GIT binary patch literal 59373 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 literal 0 HcmV?d00001 diff --git a/forge/forge.py b/forge/forge.py new file mode 100644 index 0000000000..590d76bdb1 --- /dev/null +++ b/forge/forge.py @@ -0,0 +1,187 @@ +import os, os.path, sys +import urllib, zipfile +import shutil, glob, fnmatch +import subprocess, logging, re + +forge_dir = os.path.dirname(os.path.abspath(__file__)) +mcp_dir = os.path.abspath('..') +src_dir = os.path.join(mcp_dir, 'src') + +sys.path.append(mcp_dir) +from runtime.pylibs.normlines import normaliselines +from runtime.commands import cmdsplit + +def apply_patches(patch_dir, target_dir): + temp = os.path.abspath('temp.patch') + cmd = cmdsplit('patch -p2 -i "%s" ' % temp) + display = True + + if os.name == 'nt': + applydiff = os.path.abspath(os.path.join(mcp_dir, 'runtime', 'bin', 'applydiff.exe')) + cmd = cmdsplit('"%s" -uf -p2 -i "%s"' % (applydiff, temp)) + display = False + + for path, _, filelist in os.walk(patch_dir, followlinks=True): + for cur_file in fnmatch.filter(filelist, '*.patch'): + patch_file = os.path.normpath(os.path.join(patch_dir, path[len(patch_dir)+1:], cur_file)) + if display: + print 'patching file %s' % os.path.join(path[len(patch_dir)+1:], cur_file) + normaliselines(patch_file, temp) + process = subprocess.Popen(cmd, cwd=target_dir, bufsize=-1) + process.communicate() + + if os.path.isfile(temp): + os.remove(temp) + +#Taken from: http://stackoverflow.com/questions/7545299/distutil-shutil-copytree +def _mkdir(newdir): + """works the way a good mkdir should :) + - already exists, silently complete + - regular file in the way, raise an exception + - parent directory(ies) does not exist, make them as well + """ + if os.path.isdir(newdir): + pass + elif os.path.isfile(newdir): + raise OSError("a file with the same name as the desired " \ + "dir, '%s', already exists." % newdir) + else: + head, tail = os.path.split(newdir) + if head and not os.path.isdir(head): + _mkdir(head) + #print "_mkdir %s" % repr(newdir) + if tail: + os.mkdir(newdir) + +#Taken from: http://stackoverflow.com/questions/7545299/distutil-shutil-copytree +def copytree(src, dst, verbose=0, symlinks=False): + """Recursively copy a directory tree using copy2(). + + The destination directory must not already exist. + If exception(s) occur, an Error is raised with a list of reasons. + + If the optional symlinks flag is true, symbolic links in the + source tree result in symbolic links in the destination tree; if + it is false, the contents of the files pointed to by symbolic + links are copied. + + XXX Consider this example code rather than the ultimate tool. + + """ + + if verbose == -1: + verbose = len(os.path.abspath(dst)) - 1 + names = os.listdir(src) + # os.makedirs(dst) + _mkdir(dst) # XXX + errors = [] + for name in names: + srcname = os.path.join(src, name) + dstname = os.path.join(dst, name) + try: + if symlinks and os.path.islink(srcname): + linkto = os.readlink(srcname) + os.symlink(linkto, dstname) + elif os.path.isdir(srcname): + copytree(srcname, dstname, verbose, symlinks) + else: + shutil.copy2(srcname, dstname) + if verbose > 0: + print os.path.abspath(srcname)[verbose:] + # XXX What about devices, sockets etc.? + except (IOError, os.error), why: + errors.append((srcname, dstname, str(why))) + # catch the Error from the recursive copytree so that we can + # continue with other files + except Exception, err: + errors.extend(err.args[0]) + try: + shutil.copystat(src, dst) + except WindowsError: + # can't copy file access times on Windows + pass + +def reset_logger(): + log = logging.getLogger() + while len(log.handlers) > 0: + log.removeHandler(log.handlers[0]) + +def download_ff(mcp_path): + bin_path = os.path.normpath(os.path.join(mcp_path, 'runtime', 'bin')) + ff_path = os.path.normpath(os.path.join(bin_path, 'fernflower.jar')) + + if os.path.isfile(ff_path): + return True + + try: + urllib.urlretrieve("http://goo.gl/PnJHp", 'fernflower.zip') + zf = zipfile.ZipFile('fernflower.zip') + zf.extract('fernflower.jar', bin_path) + zf.close() + os.remove('fernflower.zip') + print "Downloaded Fernflower successfully" + return True + except: + print "Downloading Fernflower failed download manually from http://goo.gl/PnJHp" + return False + +version_reg = re.compile(r'(([a-z]+)Version[\s]+=[\s]+)(\d+);') +def load_version(build=0): + info = {'major' : -1, + 'minor' : -1, + 'revision' : -1, + 'build' : -1 + } + hook_file = os.path.join(forge_dir, 'forge_common/net/minecraft/src/forge/ForgeHooks.java'.replace('/', os.sep)) + with open(hook_file, 'r') as fh: + buf = fh.read() + + def proc(match): + try: + info[match.group(2)] = int(match.group(3)) + except: + pass + return match.group(0) + + buf = version_reg.sub(proc, buf) + info['build'] = build + return info + +def inject_version(src_file, build=0): + version = load_version(build) + + tmp_file = src_file + '.tmp' + with open(src_file, 'r') as fh: + buf = fh.read() + + def mapname(match): + try: + return '%s%s;' % (match.group(1), version[match.group(2)]) + except KeyError: + pass + return match.group(0) + + buf = version_reg.sub(mapname, buf) + + with open(tmp_file, 'w') as fh: + fh.write(buf) + shutil.move(tmp_file, src_file) + +def zip_folder(path, key, zip): + files = os.listdir(path) + for file in files: + file_path = os.path.join(path, file) + file_key = os.path.join(key, file) + if os.path.isdir(file_path): + zip_folder(file_path, file_key, zip) + else: + print file_key + zip.write(file_path, file_key) + +def zip_create(path, key, zip_name): + zip = zipfile.ZipFile(zip_name, 'w', zipfile.ZIP_DEFLATED) + if os.path.isdir(path): + zip_folder(path, key, zip) + else: + zip.write(path, key) + zip.close() \ No newline at end of file diff --git a/forge/forge_common/net/minecraft/src/forge/ForgeHooks.java b/forge/forge_common/net/minecraft/src/forge/ForgeHooks.java index 60c523925e..493f81dedc 100644 --- a/forge/forge_common/net/minecraft/src/forge/ForgeHooks.java +++ b/forge/forge_common/net/minecraft/src/forge/ForgeHooks.java @@ -1,594 +1,594 @@ -/** - * This software is provided under the terms of the Minecraft Forge Public - * License v1.0. - */ - -package net.minecraft.src.forge; - -import net.minecraft.src.BaseMod; -import net.minecraft.src.Block; -import net.minecraft.src.Chunk; -import net.minecraft.src.ChunkCoordIntPair; -import net.minecraft.src.Entity; -import net.minecraft.src.EntityItem; -import net.minecraft.src.EntityMinecart; -import net.minecraft.src.EntityPlayer; -import net.minecraft.src.IInventory; -import net.minecraft.src.ItemStack; -import net.minecraft.src.Item; -import net.minecraft.src.EnumStatus; -import net.minecraft.src.ModLoader; -import net.minecraft.src.NetworkManager; -import net.minecraft.src.Packet; -import net.minecraft.src.Packet1Login; -import net.minecraft.src.Packet250CustomPayload; -import net.minecraft.src.World; -import net.minecraft.src.forge.packets.PacketEntitySpawn; - -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.util.*; - -public class ForgeHooks -{ - // List Handling Hooks - // ------------------------------------------------------------ - public static void onTakenFromCrafting(EntityPlayer player, ItemStack stack, IInventory craftMatrix) - { - for (ICraftingHandler handler : craftingHandlers) - { - handler.onTakenFromCrafting(player, stack, craftMatrix); - } - } - static LinkedList craftingHandlers = new LinkedList(); - - public static void onDestroyCurrentItem(EntityPlayer player, ItemStack orig) - { - for (IDestroyToolHandler handler : destroyToolHandlers) - { - handler.onDestroyCurrentItem(player, orig); - } - } - static LinkedList destroyToolHandlers = new LinkedList(); - - public static boolean onUseBonemeal(World world, int blockID, int x, int y, int z) - { - for (IBonemealHandler handler : bonemealHandlers) - { - if (handler.onUseBonemeal(world, blockID, x, y, z)) - { - return true; - } - } - return false; - } - static LinkedList bonemealHandlers = new LinkedList(); - - public static boolean onUseHoe(ItemStack hoe, EntityPlayer player, World world, int x, int y, int z) - { - for (IHoeHandler handler : hoeHandlers) - { - if (handler.onUseHoe(hoe, player, world, x, y, z)) - { - return true; - } - } - return false; - } - static LinkedList hoeHandlers = new LinkedList(); - - public static EnumStatus sleepInBedAt(EntityPlayer player, int x, int y, int z) - { - for (ISleepHandler handler : sleepHandlers) - { - EnumStatus status = handler.sleepInBedAt(player, x, y, z); - if (status != null) - { - return status; - } - } - return null; - } - static LinkedList sleepHandlers = new LinkedList(); - - - public static void onMinecartUpdate(EntityMinecart minecart, int x, int y, int z) - { - for (IMinecartHandler handler : minecartHandlers) - { - handler.onMinecartUpdate(minecart, x, y, z); - } - } - - public static void onMinecartEntityCollision(EntityMinecart minecart, Entity entity) - { - for (IMinecartHandler handler : minecartHandlers) - { - handler.onMinecartEntityCollision(minecart, entity); - } - } - - public static boolean onMinecartInteract(EntityMinecart minecart, EntityPlayer player) - { - boolean canceled = true; - for (IMinecartHandler handler : minecartHandlers) - { - boolean tmp = handler.onMinecartInteract(minecart, player, canceled); - canceled = canceled && tmp; - } - return canceled; - } - - static LinkedList minecartHandlers = new LinkedList(); - - public static void onConnect(NetworkManager network) - { - for (IConnectionHandler handler : connectionHandlers) - { - handler.onConnect(network); - } - } - - public static void onLogin(NetworkManager network, Packet1Login login) - { - for (IConnectionHandler handler : connectionHandlers) - { - handler.onLogin(network, login); - } - } - - public static void onDisconnect(NetworkManager network, String message, Object[] args) - { - for (IConnectionHandler handler : connectionHandlers) - { - handler.onDisconnect(network, message, args); - } - } - static LinkedList connectionHandlers = new LinkedList(); - - public static boolean onItemPickup(EntityPlayer player, EntityItem item) - { - boolean cont = true; - for (IPickupHandler handler : pickupHandlers) - { - cont = cont && handler.onItemPickup(player, item); - if (!cont || item.item.stackSize <= 0) - { - return false; - } - } - return cont; - } - static LinkedList pickupHandlers = new LinkedList(); - - public static void addActiveChunks(World world, Set chunkList) - { - for(IChunkLoadHandler loader : chunkLoadHandlers) - { - loader.addActiveChunks(world, chunkList); - } - } - - public static boolean canUnloadChunk(Chunk chunk) - { - for(IChunkLoadHandler loader : chunkLoadHandlers) - { - if(!loader.canUnloadChunk(chunk)) - { - return false; - } - } - return true; - } - - public static boolean canUpdateEntity(Entity entity) - { - for(IChunkLoadHandler loader : chunkLoadHandlers) - { - if(loader.canUpdateEntity(entity)) - { - return true; - } - } - return false; - } - static LinkedList chunkLoadHandlers = new LinkedList(); - - public static boolean onEntityInteract(EntityPlayer player, Entity entity, boolean isAttack) - { - for (IEntityInteractHandler handler : entityInteractHandlers) - { - if (!handler.onEntityInteract(player, entity, isAttack)) - { - return false; - } - } - return true; - } - static LinkedList entityInteractHandlers = new LinkedList(); - - public static String onServerChat(EntityPlayer player, String message) - { - for (IChatHandler handler : chatHandlers) - { - message = handler.onServerChat(player, message); - if (message == null) - { - return null; - } - } - return message; - } - - public static boolean onChatCommand(EntityPlayer player, boolean isOp, String command) - { - for (IChatHandler handler : chatHandlers) - { - if (handler.onChatCommand(player, isOp, command)) - { - return true; - } - } - return false; - } - - public static boolean onServerCommand(Object listener, String username, String command) - { - for (IChatHandler handler : chatHandlers) - { - if (handler.onServerCommand(listener, username, command)) - { - return true; - } - } - return false; - } - - public static String onServerCommandSay(Object listener, String username, String message) - { - for (IChatHandler handler : chatHandlers) - { - message = handler.onServerCommandSay(listener, username, message); - if (message == null) - { - return null; - } - } - return message; - } - - public static String onClientChatRecv(String message) - { - for (IChatHandler handler : chatHandlers) - { - message = handler.onClientChatRecv(message); - if (message == null) - { - return null; - } - } - return message; - } - static LinkedList chatHandlers = new LinkedList(); - - // Plant Management - // ------------------------------------------------------------ - static class ProbableItem - { - public ProbableItem(int item, int metadata, int quantity, int start, int end) - { - WeightStart = start; - WeightEnd = end; - ItemID = item; - Metadata = metadata; - Quantity = quantity; - } - int WeightStart, WeightEnd; - int ItemID, Metadata; - int Quantity; - } - - static ProbableItem getRandomItem(List list, int prop) - { - int n = Collections.binarySearch(list, prop, new Comparator() - { - public int compare(Object o1, Object o2) - { - ProbableItem pi = (ProbableItem)o1; - Integer i1 = (Integer)o2; - if (i1 < pi.WeightStart) - { - return 1; - } - if (i1 >= pi.WeightEnd) - { - return -1; - } - return 0; - } - }); - if (n < 0) - { - return null; - } - return list.get(n); - } - - static List plantGrassList; - static int plantGrassWeight; - - static List seedGrassList; - static int seedGrassWeight; - - public static void plantGrassPlant(World world, int x, int y, int z) - { - int index = world.rand.nextInt(plantGrassWeight); - ProbableItem item = getRandomItem(plantGrassList, index); - if (item == null) - { - return; - } - world.setBlockAndMetadataWithNotify(x, y, z, item.ItemID, item.Metadata); - } - - public static void addPlantGrass(int item, int metadata, int probability) - { - plantGrassList.add(new ProbableItem(item, metadata, 1, plantGrassWeight, plantGrassWeight + probability)); - plantGrassWeight += probability; - } - - public static ItemStack getGrassSeed(World world) - { - int index = world.rand.nextInt(seedGrassWeight); - ProbableItem item = getRandomItem(seedGrassList, index); - if (item == null) - { - return null; - } - return new ItemStack(item.ItemID, item.Quantity, item.Metadata); - } - - public static void addGrassSeed(int item, int metadata, int quantity, int probability) - { - seedGrassList.add(new ProbableItem(item, metadata, quantity, seedGrassWeight, seedGrassWeight + probability)); - seedGrassWeight += probability; - } - - // Tool Path - // ------------------------------------------------------------ - public static boolean canHarvestBlock(Block block, EntityPlayer player, int metadata) - { - if (block.blockMaterial.isHarvestable()) - { - return true; - } - ItemStack stack = player.inventory.getCurrentItem(); - if (stack == null) - { - return false; - } - - List info = (List)toolClasses.get(stack.itemID); - if (info == null) - { - return stack.canHarvestBlock(block); - } - Object[] tmp = info.toArray(); - String toolClass = (String)tmp[0]; - int harvestLevel = (Integer)tmp[1]; - - Integer blockHarvestLevel = (Integer)toolHarvestLevels.get(Arrays.asList(block.blockID, metadata, toolClass)); - if (blockHarvestLevel == null) - { - return stack.canHarvestBlock(block); - } - if (blockHarvestLevel > harvestLevel) - { - return false; - } - return true; - } - - public static float blockStrength(Block block, EntityPlayer player, int metadata) - { - float hardness = block.getHardness(metadata); - if (hardness < 0.0F) - { - return 0.0F; - } - - if (!canHarvestBlock(block, player, metadata)) - { - return 1.0F / hardness / 100F; - } - else - { - return player.getCurrentPlayerStrVsBlock(block, metadata) / hardness / 30F; - } - } - - public static boolean isToolEffective(ItemStack stack, Block block, int metadata) - { - List toolClass = (List)toolClasses.get(stack.itemID); - if (toolClass == null) - { - return false; - } - return toolEffectiveness.contains(Arrays.asList(block.blockID, metadata, (String)toolClass.get(0))); - } - - static void initTools() - { - if (toolInit) - { - return; - } - toolInit = true; - - MinecraftForge.setToolClass(Item.pickaxeWood, "pickaxe", 0); - MinecraftForge.setToolClass(Item.pickaxeStone, "pickaxe", 1); - MinecraftForge.setToolClass(Item.pickaxeSteel, "pickaxe", 2); - MinecraftForge.setToolClass(Item.pickaxeGold, "pickaxe", 0); - MinecraftForge.setToolClass(Item.pickaxeDiamond, "pickaxe", 3); - - MinecraftForge.setToolClass(Item.axeWood, "axe", 0); - MinecraftForge.setToolClass(Item.axeStone, "axe", 1); - MinecraftForge.setToolClass(Item.axeSteel, "axe", 2); - MinecraftForge.setToolClass(Item.axeGold, "axe", 0); - MinecraftForge.setToolClass(Item.axeDiamond, "axe", 3); - - MinecraftForge.setToolClass(Item.shovelWood, "shovel", 0); - MinecraftForge.setToolClass(Item.shovelStone, "shovel", 1); - MinecraftForge.setToolClass(Item.shovelSteel, "shovel", 2); - MinecraftForge.setToolClass(Item.shovelGold, "shovel", 0); - MinecraftForge.setToolClass(Item.shovelDiamond, "shovel", 3); - - MinecraftForge.setBlockHarvestLevel(Block.obsidian, "pickaxe", 3); - MinecraftForge.setBlockHarvestLevel(Block.oreDiamond, "pickaxe", 2); - MinecraftForge.setBlockHarvestLevel(Block.blockDiamond, "pickaxe", 2); - MinecraftForge.setBlockHarvestLevel(Block.oreGold, "pickaxe", 2); - MinecraftForge.setBlockHarvestLevel(Block.blockGold, "pickaxe", 2); - MinecraftForge.setBlockHarvestLevel(Block.oreIron, "pickaxe", 1); - MinecraftForge.setBlockHarvestLevel(Block.blockSteel, "pickaxe", 1); - MinecraftForge.setBlockHarvestLevel(Block.oreLapis, "pickaxe", 1); - MinecraftForge.setBlockHarvestLevel(Block.blockLapis, "pickaxe", 1); - MinecraftForge.setBlockHarvestLevel(Block.oreRedstone, "pickaxe", 2); - MinecraftForge.setBlockHarvestLevel(Block.oreRedstoneGlowing, "pickaxe", 2); - MinecraftForge.removeBlockEffectiveness(Block.oreRedstone, "pickaxe"); - MinecraftForge.removeBlockEffectiveness(Block.obsidian, "pickaxe"); - MinecraftForge.removeBlockEffectiveness(Block.oreRedstoneGlowing, "pickaxe"); - - Block[] pickeff = - { - Block.cobblestone, Block.stairDouble, - Block.stairSingle, Block.stone, - Block.sandStone, Block.cobblestoneMossy, - Block.oreCoal, Block.ice, - Block.netherrack, Block.oreLapis, - Block.blockLapis - }; - for (Block block : pickeff) - { - MinecraftForge.setBlockHarvestLevel(block, "pickaxe", 0); - } - - Block[] spadeEff = - { - Block.grass, Block.dirt, - Block.sand, Block.gravel, - Block.snow, Block.blockSnow, - Block.blockClay, Block.tilledField, - Block.slowSand, Block.mycelium - }; - for (Block block : spadeEff) - { - MinecraftForge.setBlockHarvestLevel(block, "shovel", 0); - } - - Block[] axeEff = - { - Block.planks, Block.bookShelf, - Block.wood, Block.chest, - Block.stairDouble, Block.stairSingle, - Block.pumpkin, Block.pumpkinLantern - }; - for (Block block : axeEff) - { - MinecraftForge.setBlockHarvestLevel(block, "axe", 0); - } - - } - - public static HashMap entityTrackerMap = new HashMap(); - - /** - * Builds the 'Spawn' packet using the Custom Payload packet on the 'Forge' channel. - * Supports entities that have custom spawn data, as well as the generic 'Owner' construct. - * - * @param entity The entity instance to spawn - * @return The spawn packet, or null if we arn't spawning it. - */ - public static Packet getEntitySpawnPacket(Entity entity) - { - EntityTrackerInfo info = MinecraftForge.getEntityTrackerInfo(entity, false); - if (info == null) - { - return null; - } - - PacketEntitySpawn pkt = new PacketEntitySpawn(entity, info.Mod, info.ID); - return pkt.getPacket(); - } - - public static Hashtable networkMods = new Hashtable(); - public static Hashtable guiHandlers = new Hashtable(); - - public static boolean onArrowLoose(ItemStack itemstack, World world, EntityPlayer player, int heldTime) - { - for (IArrowLooseHandler handler : arrowLooseHandlers) - { - if (handler.onArrowLoose(itemstack, world, player, heldTime)) - { - return true; - } - } - return false; - } - - public static ArrayList arrowLooseHandlers = new ArrayList(); - - public static ItemStack onArrowNock(ItemStack itemstack, World world, EntityPlayer player) - { - for (IArrowNockHandler handler : arrowNockHandlers) - { - ItemStack ret = handler.onArrowNock(itemstack, world, player); - if (ret != null) - { - return ret; - } - } - return null; - } - public static ArrayList arrowNockHandlers = new ArrayList(); - - public static final int majorVersion=0; - public static final int minorVersion=0; - public static final int revisionVersion=0; - public static final int buildVersion=0; - static - { - plantGrassList = new ArrayList(); - plantGrassList.add(new ProbableItem(Block.plantYellow.blockID, 0, 1, 0, 20)); - plantGrassList.add(new ProbableItem(Block.plantRed.blockID, 0, 1, 20, 30)); - plantGrassWeight = 30; - - seedGrassList = new ArrayList(); - seedGrassList.add(new ProbableItem(Item.seeds.shiftedIndex, 0, 1, 0, 10)); - seedGrassWeight = 10; - - System.out.printf("MinecraftForge v%d.%d.%d.%d Initialized\n", majorVersion, minorVersion, revisionVersion, buildVersion); - ModLoader.getLogger().info(String.format("MinecraftForge v%d.%d.%d.%d Initialized\n", majorVersion, minorVersion, revisionVersion, buildVersion)); - } - - static boolean toolInit = false; - static HashMap toolClasses = new HashMap(); - static HashMap toolHarvestLevels = new HashMap(); - static HashSet toolEffectiveness = new HashSet(); - - private static IPacketHandler forgePacketHandler = null; - public static void setPacketHandler(IPacketHandler handler) - { - if (forgePacketHandler != null) - { - throw new RuntimeException("Attempted to set Forge's Internal packet handler after it was already set"); - } - forgePacketHandler = handler; - } - public static IPacketHandler getPacketHandler() - { - return forgePacketHandler; - } -} - +/** + * This software is provided under the terms of the Minecraft Forge Public + * License v1.0. + */ + +package net.minecraft.src.forge; + +import net.minecraft.src.BaseMod; +import net.minecraft.src.Block; +import net.minecraft.src.Chunk; +import net.minecraft.src.ChunkCoordIntPair; +import net.minecraft.src.Entity; +import net.minecraft.src.EntityItem; +import net.minecraft.src.EntityMinecart; +import net.minecraft.src.EntityPlayer; +import net.minecraft.src.IInventory; +import net.minecraft.src.ItemStack; +import net.minecraft.src.Item; +import net.minecraft.src.EnumStatus; +import net.minecraft.src.ModLoader; +import net.minecraft.src.NetworkManager; +import net.minecraft.src.Packet; +import net.minecraft.src.Packet1Login; +import net.minecraft.src.Packet250CustomPayload; +import net.minecraft.src.World; +import net.minecraft.src.forge.packets.PacketEntitySpawn; + +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.*; + +public class ForgeHooks +{ + // List Handling Hooks + // ------------------------------------------------------------ + public static void onTakenFromCrafting(EntityPlayer player, ItemStack stack, IInventory craftMatrix) + { + for (ICraftingHandler handler : craftingHandlers) + { + handler.onTakenFromCrafting(player, stack, craftMatrix); + } + } + static LinkedList craftingHandlers = new LinkedList(); + + public static void onDestroyCurrentItem(EntityPlayer player, ItemStack orig) + { + for (IDestroyToolHandler handler : destroyToolHandlers) + { + handler.onDestroyCurrentItem(player, orig); + } + } + static LinkedList destroyToolHandlers = new LinkedList(); + + public static boolean onUseBonemeal(World world, int blockID, int x, int y, int z) + { + for (IBonemealHandler handler : bonemealHandlers) + { + if (handler.onUseBonemeal(world, blockID, x, y, z)) + { + return true; + } + } + return false; + } + static LinkedList bonemealHandlers = new LinkedList(); + + public static boolean onUseHoe(ItemStack hoe, EntityPlayer player, World world, int x, int y, int z) + { + for (IHoeHandler handler : hoeHandlers) + { + if (handler.onUseHoe(hoe, player, world, x, y, z)) + { + return true; + } + } + return false; + } + static LinkedList hoeHandlers = new LinkedList(); + + public static EnumStatus sleepInBedAt(EntityPlayer player, int x, int y, int z) + { + for (ISleepHandler handler : sleepHandlers) + { + EnumStatus status = handler.sleepInBedAt(player, x, y, z); + if (status != null) + { + return status; + } + } + return null; + } + static LinkedList sleepHandlers = new LinkedList(); + + + public static void onMinecartUpdate(EntityMinecart minecart, int x, int y, int z) + { + for (IMinecartHandler handler : minecartHandlers) + { + handler.onMinecartUpdate(minecart, x, y, z); + } + } + + public static void onMinecartEntityCollision(EntityMinecart minecart, Entity entity) + { + for (IMinecartHandler handler : minecartHandlers) + { + handler.onMinecartEntityCollision(minecart, entity); + } + } + + public static boolean onMinecartInteract(EntityMinecart minecart, EntityPlayer player) + { + boolean canceled = true; + for (IMinecartHandler handler : minecartHandlers) + { + boolean tmp = handler.onMinecartInteract(minecart, player, canceled); + canceled = canceled && tmp; + } + return canceled; + } + + static LinkedList minecartHandlers = new LinkedList(); + + public static void onConnect(NetworkManager network) + { + for (IConnectionHandler handler : connectionHandlers) + { + handler.onConnect(network); + } + } + + public static void onLogin(NetworkManager network, Packet1Login login) + { + for (IConnectionHandler handler : connectionHandlers) + { + handler.onLogin(network, login); + } + } + + public static void onDisconnect(NetworkManager network, String message, Object[] args) + { + for (IConnectionHandler handler : connectionHandlers) + { + handler.onDisconnect(network, message, args); + } + } + static LinkedList connectionHandlers = new LinkedList(); + + public static boolean onItemPickup(EntityPlayer player, EntityItem item) + { + boolean cont = true; + for (IPickupHandler handler : pickupHandlers) + { + cont = cont && handler.onItemPickup(player, item); + if (!cont || item.item.stackSize <= 0) + { + return false; + } + } + return cont; + } + static LinkedList pickupHandlers = new LinkedList(); + + public static void addActiveChunks(World world, Set chunkList) + { + for(IChunkLoadHandler loader : chunkLoadHandlers) + { + loader.addActiveChunks(world, chunkList); + } + } + + public static boolean canUnloadChunk(Chunk chunk) + { + for(IChunkLoadHandler loader : chunkLoadHandlers) + { + if(!loader.canUnloadChunk(chunk)) + { + return false; + } + } + return true; + } + + public static boolean canUpdateEntity(Entity entity) + { + for(IChunkLoadHandler loader : chunkLoadHandlers) + { + if(loader.canUpdateEntity(entity)) + { + return true; + } + } + return false; + } + static LinkedList chunkLoadHandlers = new LinkedList(); + + public static boolean onEntityInteract(EntityPlayer player, Entity entity, boolean isAttack) + { + for (IEntityInteractHandler handler : entityInteractHandlers) + { + if (!handler.onEntityInteract(player, entity, isAttack)) + { + return false; + } + } + return true; + } + static LinkedList entityInteractHandlers = new LinkedList(); + + public static String onServerChat(EntityPlayer player, String message) + { + for (IChatHandler handler : chatHandlers) + { + message = handler.onServerChat(player, message); + if (message == null) + { + return null; + } + } + return message; + } + + public static boolean onChatCommand(EntityPlayer player, boolean isOp, String command) + { + for (IChatHandler handler : chatHandlers) + { + if (handler.onChatCommand(player, isOp, command)) + { + return true; + } + } + return false; + } + + public static boolean onServerCommand(Object listener, String username, String command) + { + for (IChatHandler handler : chatHandlers) + { + if (handler.onServerCommand(listener, username, command)) + { + return true; + } + } + return false; + } + + public static String onServerCommandSay(Object listener, String username, String message) + { + for (IChatHandler handler : chatHandlers) + { + message = handler.onServerCommandSay(listener, username, message); + if (message == null) + { + return null; + } + } + return message; + } + + public static String onClientChatRecv(String message) + { + for (IChatHandler handler : chatHandlers) + { + message = handler.onClientChatRecv(message); + if (message == null) + { + return null; + } + } + return message; + } + static LinkedList chatHandlers = new LinkedList(); + + // Plant Management + // ------------------------------------------------------------ + static class ProbableItem + { + public ProbableItem(int item, int metadata, int quantity, int start, int end) + { + WeightStart = start; + WeightEnd = end; + ItemID = item; + Metadata = metadata; + Quantity = quantity; + } + int WeightStart, WeightEnd; + int ItemID, Metadata; + int Quantity; + } + + static ProbableItem getRandomItem(List list, int prop) + { + int n = Collections.binarySearch(list, prop, new Comparator() + { + public int compare(Object o1, Object o2) + { + ProbableItem pi = (ProbableItem)o1; + Integer i1 = (Integer)o2; + if (i1 < pi.WeightStart) + { + return 1; + } + if (i1 >= pi.WeightEnd) + { + return -1; + } + return 0; + } + }); + if (n < 0) + { + return null; + } + return list.get(n); + } + + static List plantGrassList; + static int plantGrassWeight; + + static List seedGrassList; + static int seedGrassWeight; + + public static void plantGrassPlant(World world, int x, int y, int z) + { + int index = world.rand.nextInt(plantGrassWeight); + ProbableItem item = getRandomItem(plantGrassList, index); + if (item == null) + { + return; + } + world.setBlockAndMetadataWithNotify(x, y, z, item.ItemID, item.Metadata); + } + + public static void addPlantGrass(int item, int metadata, int probability) + { + plantGrassList.add(new ProbableItem(item, metadata, 1, plantGrassWeight, plantGrassWeight + probability)); + plantGrassWeight += probability; + } + + public static ItemStack getGrassSeed(World world) + { + int index = world.rand.nextInt(seedGrassWeight); + ProbableItem item = getRandomItem(seedGrassList, index); + if (item == null) + { + return null; + } + return new ItemStack(item.ItemID, item.Quantity, item.Metadata); + } + + public static void addGrassSeed(int item, int metadata, int quantity, int probability) + { + seedGrassList.add(new ProbableItem(item, metadata, quantity, seedGrassWeight, seedGrassWeight + probability)); + seedGrassWeight += probability; + } + + // Tool Path + // ------------------------------------------------------------ + public static boolean canHarvestBlock(Block block, EntityPlayer player, int metadata) + { + if (block.blockMaterial.isHarvestable()) + { + return true; + } + ItemStack stack = player.inventory.getCurrentItem(); + if (stack == null) + { + return false; + } + + List info = (List)toolClasses.get(stack.itemID); + if (info == null) + { + return stack.canHarvestBlock(block); + } + Object[] tmp = info.toArray(); + String toolClass = (String)tmp[0]; + int harvestLevel = (Integer)tmp[1]; + + Integer blockHarvestLevel = (Integer)toolHarvestLevels.get(Arrays.asList(block.blockID, metadata, toolClass)); + if (blockHarvestLevel == null) + { + return stack.canHarvestBlock(block); + } + if (blockHarvestLevel > harvestLevel) + { + return false; + } + return true; + } + + public static float blockStrength(Block block, EntityPlayer player, int metadata) + { + float hardness = block.getHardness(metadata); + if (hardness < 0.0F) + { + return 0.0F; + } + + if (!canHarvestBlock(block, player, metadata)) + { + return 1.0F / hardness / 100F; + } + else + { + return player.getCurrentPlayerStrVsBlock(block, metadata) / hardness / 30F; + } + } + + public static boolean isToolEffective(ItemStack stack, Block block, int metadata) + { + List toolClass = (List)toolClasses.get(stack.itemID); + if (toolClass == null) + { + return false; + } + return toolEffectiveness.contains(Arrays.asList(block.blockID, metadata, (String)toolClass.get(0))); + } + + static void initTools() + { + if (toolInit) + { + return; + } + toolInit = true; + + MinecraftForge.setToolClass(Item.pickaxeWood, "pickaxe", 0); + MinecraftForge.setToolClass(Item.pickaxeStone, "pickaxe", 1); + MinecraftForge.setToolClass(Item.pickaxeSteel, "pickaxe", 2); + MinecraftForge.setToolClass(Item.pickaxeGold, "pickaxe", 0); + MinecraftForge.setToolClass(Item.pickaxeDiamond, "pickaxe", 3); + + MinecraftForge.setToolClass(Item.axeWood, "axe", 0); + MinecraftForge.setToolClass(Item.axeStone, "axe", 1); + MinecraftForge.setToolClass(Item.axeSteel, "axe", 2); + MinecraftForge.setToolClass(Item.axeGold, "axe", 0); + MinecraftForge.setToolClass(Item.axeDiamond, "axe", 3); + + MinecraftForge.setToolClass(Item.shovelWood, "shovel", 0); + MinecraftForge.setToolClass(Item.shovelStone, "shovel", 1); + MinecraftForge.setToolClass(Item.shovelSteel, "shovel", 2); + MinecraftForge.setToolClass(Item.shovelGold, "shovel", 0); + MinecraftForge.setToolClass(Item.shovelDiamond, "shovel", 3); + + MinecraftForge.setBlockHarvestLevel(Block.obsidian, "pickaxe", 3); + MinecraftForge.setBlockHarvestLevel(Block.oreDiamond, "pickaxe", 2); + MinecraftForge.setBlockHarvestLevel(Block.blockDiamond, "pickaxe", 2); + MinecraftForge.setBlockHarvestLevel(Block.oreGold, "pickaxe", 2); + MinecraftForge.setBlockHarvestLevel(Block.blockGold, "pickaxe", 2); + MinecraftForge.setBlockHarvestLevel(Block.oreIron, "pickaxe", 1); + MinecraftForge.setBlockHarvestLevel(Block.blockSteel, "pickaxe", 1); + MinecraftForge.setBlockHarvestLevel(Block.oreLapis, "pickaxe", 1); + MinecraftForge.setBlockHarvestLevel(Block.blockLapis, "pickaxe", 1); + MinecraftForge.setBlockHarvestLevel(Block.oreRedstone, "pickaxe", 2); + MinecraftForge.setBlockHarvestLevel(Block.oreRedstoneGlowing, "pickaxe", 2); + MinecraftForge.removeBlockEffectiveness(Block.oreRedstone, "pickaxe"); + MinecraftForge.removeBlockEffectiveness(Block.obsidian, "pickaxe"); + MinecraftForge.removeBlockEffectiveness(Block.oreRedstoneGlowing, "pickaxe"); + + Block[] pickeff = + { + Block.cobblestone, Block.stairDouble, + Block.stairSingle, Block.stone, + Block.sandStone, Block.cobblestoneMossy, + Block.oreCoal, Block.ice, + Block.netherrack, Block.oreLapis, + Block.blockLapis + }; + for (Block block : pickeff) + { + MinecraftForge.setBlockHarvestLevel(block, "pickaxe", 0); + } + + Block[] spadeEff = + { + Block.grass, Block.dirt, + Block.sand, Block.gravel, + Block.snow, Block.blockSnow, + Block.blockClay, Block.tilledField, + Block.slowSand, Block.mycelium + }; + for (Block block : spadeEff) + { + MinecraftForge.setBlockHarvestLevel(block, "shovel", 0); + } + + Block[] axeEff = + { + Block.planks, Block.bookShelf, + Block.wood, Block.chest, + Block.stairDouble, Block.stairSingle, + Block.pumpkin, Block.pumpkinLantern + }; + for (Block block : axeEff) + { + MinecraftForge.setBlockHarvestLevel(block, "axe", 0); + } + + } + + public static HashMap entityTrackerMap = new HashMap(); + + /** + * Builds the 'Spawn' packet using the Custom Payload packet on the 'Forge' channel. + * Supports entities that have custom spawn data, as well as the generic 'Owner' construct. + * + * @param entity The entity instance to spawn + * @return The spawn packet, or null if we arn't spawning it. + */ + public static Packet getEntitySpawnPacket(Entity entity) + { + EntityTrackerInfo info = MinecraftForge.getEntityTrackerInfo(entity, false); + if (info == null) + { + return null; + } + + PacketEntitySpawn pkt = new PacketEntitySpawn(entity, info.Mod, info.ID); + return pkt.getPacket(); + } + + public static Hashtable networkMods = new Hashtable(); + public static Hashtable guiHandlers = new Hashtable(); + + public static boolean onArrowLoose(ItemStack itemstack, World world, EntityPlayer player, int heldTime) + { + for (IArrowLooseHandler handler : arrowLooseHandlers) + { + if (handler.onArrowLoose(itemstack, world, player, heldTime)) + { + return true; + } + } + return false; + } + + public static ArrayList arrowLooseHandlers = new ArrayList(); + + public static ItemStack onArrowNock(ItemStack itemstack, World world, EntityPlayer player) + { + for (IArrowNockHandler handler : arrowNockHandlers) + { + ItemStack ret = handler.onArrowNock(itemstack, world, player); + if (ret != null) + { + return ret; + } + } + return null; + } + public static ArrayList arrowNockHandlers = new ArrayList(); + + public static final int majorVersion = 3; + public static final int minorVersion = 0; + public static final int revisionVersion = 0; + public static final int buildVersion = 0; + static + { + plantGrassList = new ArrayList(); + plantGrassList.add(new ProbableItem(Block.plantYellow.blockID, 0, 1, 0, 20)); + plantGrassList.add(new ProbableItem(Block.plantRed.blockID, 0, 1, 20, 30)); + plantGrassWeight = 30; + + seedGrassList = new ArrayList(); + seedGrassList.add(new ProbableItem(Item.seeds.shiftedIndex, 0, 1, 0, 10)); + seedGrassWeight = 10; + + System.out.printf("MinecraftForge v%d.%d.%d.%d Initialized\n", majorVersion, minorVersion, revisionVersion, buildVersion); + ModLoader.getLogger().info(String.format("MinecraftForge v%d.%d.%d.%d Initialized\n", majorVersion, minorVersion, revisionVersion, buildVersion)); + } + + static boolean toolInit = false; + static HashMap toolClasses = new HashMap(); + static HashMap toolHarvestLevels = new HashMap(); + static HashSet toolEffectiveness = new HashSet(); + + private static IPacketHandler forgePacketHandler = null; + public static void setPacketHandler(IPacketHandler handler) + { + if (forgePacketHandler != null) + { + throw new RuntimeException("Attempted to set Forge's Internal packet handler after it was already set"); + } + forgePacketHandler = handler; + } + public static IPacketHandler getPacketHandler() + { + return forgePacketHandler; + } +} + diff --git a/forge/inject_version.bat b/forge/inject_version.bat deleted file mode 100644 index a002849177..0000000000 --- a/forge/inject_version.bat +++ /dev/null @@ -1,20 +0,0 @@ -@echo off -setlocal enableextensions enabledelayedexpansion -FOR /F "tokens=1-4" %%A IN (version.txt) DO ( - set major=%%A - set minor=%%B - set revision=%%C - set build=%%D -) -set file=%* - -echo Injecting Revision !major!.!minor!.!revision!.!build! %file% - -set PATH=.\bin;%PATH% -sed s/majorVersion=0/majorVersion=!major!/ !file!.tmp - -move /Y !file:/=\!.tmp !file:/=\! >nul diff --git a/forge/inject_version.sh b/forge/inject_version.sh deleted file mode 100755 index 4aa41ae455..0000000000 --- a/forge/inject_version.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - -target=$1 -read major minor rev build $target.tmp - -mv $target.tmp $target \ No newline at end of file diff --git a/forge/lfcr.py b/forge/lfcr.py deleted file mode 100644 index d6ad3ee6c6..0000000000 --- a/forge/lfcr.py +++ /dev/null @@ -1,8 +0,0 @@ -#! /usr/bin/env python - -import sys, re, os -data = open(sys.argv[1], "rb").read() -newdata = re.sub("\r?\n", "\r\n", data) -f = open(sys.argv[2], "wb") -f.write(newdata) -f.close() \ No newline at end of file diff --git a/forge/package.sh b/forge/package.sh deleted file mode 100755 index 599ba565f5..0000000000 --- a/forge/package.sh +++ /dev/null @@ -1,69 +0,0 @@ -read major minor revision build 1: + try: + build_num = int(sys.argv[1]) + except: + pass + ret = 0 + ret = build(build_num) + if ret != 0: + sys.exit(ret) + + print '=================================== Release Start =================================' + error_level = 0 + try: + os.chdir(mcp_dir) + reobfuscate(None, False, True, True) + reset_logger() + os.chdir(forge_dir) + except SystemExit, e: + print 'Reobfusicate Exception: %d ' % e.code + error_level = e.code + + out_folder = os.path.join(forge_dir, 'forge-%s' % version_str) + if os.path.isdir(out_folder): + shutil.rmtree(out_folder) + + os.makedirs(out_folder) + + extract_fml_obfed() + version = load_version(build_num) + version_str = '%d.%d.%d.%d' % (version['major'], version['minor'], version['revision'], version['build']) + + zip_start('minecraftforge-client-%s.zip' % version_str) + zip_folder(client_dir, '', zip) + zip_add('minecraftforge_credits.txt') + zip_add('license.txt') + zip_end() + + zip_start('minecraftforge-server-%s.zip' % version_str) + zip_folder(server_dir, '', zip) + zip_add('minecraftforge_credits.txt') + zip_add('license.txt') + zip_end() + + zip_start('minecraftforge-src-%s.zip' % version_str, 'forge') + zip_add('forge_client/src', 'src/minecraft') + zip_add('forge_server/src', 'src/minecraft_server') + zip_add('forge_common', 'src/minecraft') + zip_add('forge_common', 'src/minecraft_server') + zip_add('patches', 'patches') + zip_add('fml', 'fml') + zip_add('conf', 'conf') + zip_add('minecraftforge_credits.txt') + zip_add('install/install.cmd') + zip_add('install/install.sh') + zip_add('install/README.txt') + zip_add('install/install.py') + zip_add('forge.py') + zip_add('minecraftforge_credits.txt') + zip_add('license.txt') + zip_end() + + print '=================================== Release Finished %d =================================' % error_level + sys.exit(error_level) + + +def zip_add(file, key=None): + if key == None: + key = os.path.basename(file) + else: + key = key.replace('/', os.sep) + if not zip_base is None: + key = os.path.join(zip_base, key) + file = os.path.join(forge_dir, file.replace('/', os.sep)) + if os.path.isdir(file): + zip_folder(file, key, zip) + else: + if os.path.isfile(file): + print key + zip.write(file, key) + +def zip_start(name, base=None): + global zip, zip_name, zip_base + zip_name = name + + print '=================================== %s Start =================================' % zip_name + zip_file = os.path.join(forge_dir, 'forge-%s' % version_str, name) + zip = zipfile.ZipFile(zip_file, 'w', zipfile.ZIP_DEFLATED) + zip_base = base + +def zip_end(): + global zip, zip_name, zip_base + zip.close() + print '=================================== %s Finished =================================' % zip_name + zip_name = None + zip_base = None + +def extract_fml_obfed(): + fml_file = os.path.join(forge_dir, 'fml', 'difflist.txt') + if not os.path.isfile(fml_file): + print 'Could not find Forge ModLoader\'s DiffList, looking for it at: %s' % fml_file + sys.exit(1) + + with open(fml_file, 'r') as fh: + lines = fh.readlines() + + client = zipfile.ZipFile(os.path.join(mcp_dir, 'temp', 'client_reobf.jar')) + server = zipfile.ZipFile(os.path.join(mcp_dir, 'temp', 'server_reobf.jar')) + + print 'Extracting Reobfed Forge ModLoader classes' + for line in lines: + line = line.replace('\n', '').replace('\r', '').replace('/', os.sep) + print line + if not os.path.isfile(os.path.join(reobf_dir, line)): + side = line.split(os.sep)[0] + if side == 'minecraft': + client.extract(line[10:].replace(os.sep, '/'), client_dir) + else: + server.extract(line[17:].replace(os.sep, '/'), server_dir) + + client.close() + server.close() + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/forge/setup.bat b/forge/setup.bat index a6fa1576d3..1b96a5d70d 100755 --- a/forge/setup.bat +++ b/forge/setup.bat @@ -1,51 +1,5 @@ @echo off -echo =================================== Setup Start ================================= -pushd .. >nul - - if "%1"=="-skipdecompile" ( - @echo | cmd /C updatenames.bat -f - ) ELSE ( - if not exist "runtime\bin\fernflower.jar" ( - pushd forge - ..\runtime\bin\python\python_mcp download_fernflower.py - popd - ) - if not exist "runtime\bin\fernflower.jar" ( - echo Failed to download fernflower, install it manually and re-run setup. - exit 1 - ) - rmdir /S /Q src - echo | cmd /C decompile.bat -r - ) - - echo | cmd /C updatemd5.bat -f - - rmdir /S /Q src_base - rmdir /S /Q src_work - mkdir src_base - mkdir src_work - - xcopy /Y /E /Q src\* src_base - xcopy /Y /E /Q src\* src_work - - pushd src_work >nul - @setlocal enabledelayedexpansion - @echo off - - set PATH=..\forge\bin;%PATH% - echo Applying patches - for /f %%i in ('find ../forge/patches -type f') do ( - set file=%%i - if /I "!file:~-6!" EQU ".patch" ( - ..\runtime\bin\python\python_mcp ..\forge\lfcr.py %%i ..\forge\temp.patch - ..\runtime\bin\applydiff.exe -uf -p2 -i ..\forge\temp.patch - del ..\forge\temp.patch >nul 1>nul - ) - ) - popd >nul -popd >nul -echo =================================== Setup Finished ================================= - +..\runtime\bin\python\python_mcp setup.py %* if NOT "%1"=="-skipdecompile" ( pause ) \ No newline at end of file diff --git a/forge/setup.py b/forge/setup.py new file mode 100644 index 0000000000..24016693a5 --- /dev/null +++ b/forge/setup.py @@ -0,0 +1,93 @@ +import os, os.path, sys +import urllib, zipfile +import shutil, glob, fnmatch +import subprocess, logging + +forge_dir = os.path.dirname(os.path.abspath(__file__)) +mcp_dir = os.path.abspath('..') +src_dir = os.path.join(mcp_dir, 'src') + +sys.path.append(mcp_dir) +from runtime.decompile import decompile +from runtime.updatenames import updatenames +from runtime.updatemd5 import updatemd5 + +from forge import apply_patches, copytree, reset_logger, download_ff + + +def main(): + print '=================================== Setup Start =================================' + + skipDecompile = len(sys.argv) > 1 and sys.argv[1] == '-skipdecompile' + if not skipDecompile: + if not download_ff(mcp_dir): + sys.exit(1) + + if os.path.isdir(src_dir): + shutil.rmtree(src_dir) + try: + os.chdir(mcp_dir) + # Conf JAD CSV -r -d -a -n -p -o -l -g + decompile(None, False, False, True, True, False, True, False, False, False, False) + reset_logger() + os.chdir(forge_dir) + except SystemExit, e: + print 'Decompile Exception: %d ' % e.code + raise e + + if not os.path.isdir(src_dir): + print 'Something went wrong, src folder not found at: %s' % src_dir + sys.exit(1) + + setup_fml() + + os.chdir(mcp_dir) + updatenames(None, True) + reset_logger() + updatemd5(None, True) + reset_logger() + os.chdir(forge_dir) + + base_dir = os.path.join(mcp_dir, 'src_base') + work_dir = os.path.join(mcp_dir, 'src_work') + + if os.path.isdir(base_dir): + shutil.rmtree(base_dir) + if os.path.isdir(work_dir): + shutil.rmtree(work_dir) + + print 'Setting up source directories' + shutil.copytree(src_dir, base_dir) + shutil.copytree(src_dir, work_dir) + + print 'Applying forge patches' + apply_patches(os.path.join(forge_dir, 'patches'), work_dir) + + print '=================================== Setup Finished =================================' + +def setup_fml(): + print 'Setting up Forge ModLoader' + fml = glob.glob(os.path.join(forge_dir, 'fml-src-*.zip')) + if not len(fml) == 1: + if len(fml) == 0: + print 'Missing FML source zip, should be named fml-src-*.zip inside your forge folder, obtain it from the repo' + else: + print 'To many FML source zips found, we should only have one. Check the Forge Git for the latest FML version supported' + sys.exit(1) + + fml_dir = os.path.join(forge_dir, 'fml') + if os.path.isdir(fml_dir): + shutil.rmtree(fml_dir) + + print 'Extracting Forge ModLoader: %s' % os.path.basename(fml[0]) + + zf = zipfile.ZipFile(fml[0]) + zf.extractall(forge_dir) + zf.close() + + print 'Applying Forge ModLoader patches' + apply_patches(os.path.join(fml_dir, 'patches'), src_dir) + copytree(os.path.join(fml_dir, 'src'), src_dir) + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/forge/version.txt b/forge/version.txt deleted file mode 100644 index f3be5a5c58..0000000000 --- a/forge/version.txt +++ /dev/null @@ -1 +0,0 @@ -2 0 0 0