blob: 7540d655c41ee918f0126e07775e576c3e2b009a [file] [log] [blame] [raw]
package mekanism.common.util;
import ic2.api.energy.EnergyNet;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import mekanism.api.Chunk3D;
import mekanism.api.Coord4D;
import mekanism.api.EnumColor;
import mekanism.api.IMekWrench;
import mekanism.api.MekanismConfig.client;
import mekanism.api.MekanismConfig.general;
import mekanism.api.gas.Gas;
import mekanism.api.gas.GasStack;
import mekanism.api.transmitters.TransmissionType;
import mekanism.api.util.UnitDisplayUtils;
import mekanism.api.util.UnitDisplayUtils.ElectricUnit;
import mekanism.api.util.UnitDisplayUtils.TemperatureUnit;
import mekanism.common.Mekanism;
import mekanism.common.MekanismBlocks;
import mekanism.common.MekanismItems;
import mekanism.common.OreDictCache;
import mekanism.common.Tier.BaseTier;
import mekanism.common.Tier.BinTier;
import mekanism.common.Tier.EnergyCubeTier;
import mekanism.common.Tier.FactoryTier;
import mekanism.common.Tier.FluidTankTier;
import mekanism.common.Tier.GasTankTier;
import mekanism.common.Tier.InductionCellTier;
import mekanism.common.Tier.InductionProviderTier;
import mekanism.common.Upgrade;
import mekanism.common.Version;
import mekanism.common.base.IActiveState;
import mekanism.common.base.IFactory;
import mekanism.common.base.IFactory.RecipeType;
import mekanism.common.base.IModule;
import mekanism.common.base.IRedstoneControl;
import mekanism.common.base.ISideConfiguration;
import mekanism.common.base.IUpgradeTile;
import mekanism.common.inventory.container.ContainerPersonalChest;
import mekanism.common.item.ItemBlockBasic;
import mekanism.common.item.ItemBlockEnergyCube;
import mekanism.common.item.ItemBlockGasTank;
import mekanism.common.item.ItemBlockMachine;
import mekanism.common.network.PacketPersonalChest.PersonalChestMessage;
import mekanism.common.network.PacketPersonalChest.PersonalChestPacketType;
import mekanism.common.tile.TileEntityAdvancedBoundingBlock;
import mekanism.common.tile.TileEntityBoundingBlock;
import mekanism.common.tile.TileEntityPersonalChest;
import net.minecraft.block.Block;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.init.Blocks;
import net.minecraft.inventory.Container;
import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.InventoryCrafting;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.CraftingManager;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ChatComponentText;
import net.minecraft.util.MovingObjectPosition;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.Vec3;
import net.minecraft.world.EnumSkyBlock;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import net.minecraftforge.common.util.ForgeDirection;
import net.minecraftforge.fluids.Fluid;
import net.minecraftforge.fluids.FluidContainerRegistry;
import net.minecraftforge.fluids.FluidRegistry;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.IFluidBlock;
import net.minecraftforge.oredict.OreDictionary;
import net.minecraftforge.oredict.ShapedOreRecipe;
import buildcraft.api.tools.IToolWrench;
import cofh.api.item.IToolHammer;
import cpw.mods.fml.common.ModContainer;
import cpw.mods.fml.common.registry.GameData;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
/**
* Utilities used by Mekanism. All miscellaneous methods are located here.
* @author AidanBrady
*
*/
public final class MekanismUtils
{
public static final ForgeDirection[] SIDE_DIRS = new ForgeDirection[] {ForgeDirection.NORTH, ForgeDirection.SOUTH, ForgeDirection.WEST, ForgeDirection.EAST};
public static final Map<String, Class<?>> classesFound = new HashMap<String, Class<?>>();
/**
* Checks for a new version of Mekanism.
*/
public static boolean checkForUpdates(EntityPlayer entityplayer)
{
try {
if(general.updateNotifications && Mekanism.latestVersionNumber != null && Mekanism.recentNews != null)
{
if(!Mekanism.latestVersionNumber.equals("null"))
{
ArrayList<IModule> list = new ArrayList<IModule>();
for(IModule module : Mekanism.modulesLoaded)
{
if(Version.get(Mekanism.latestVersionNumber).comparedState(module.getVersion()) == 1)
{
list.add(module);
}
}
if(Version.get(Mekanism.latestVersionNumber).comparedState(Mekanism.versionNumber) == 1 || !list.isEmpty())
{
entityplayer.addChatMessage(new ChatComponentText(EnumColor.GREY + "------------- " + EnumColor.DARK_BLUE + "[Mekanism]" + EnumColor.GREY + " -------------"));
entityplayer.addChatMessage(new ChatComponentText(EnumColor.GREY + " " + LangUtils.localize("update.outdated") + "."));
if(Version.get(Mekanism.latestVersionNumber).comparedState(Mekanism.versionNumber) == 1)
{
entityplayer.addChatMessage(new ChatComponentText(EnumColor.INDIGO + " Mekanism: " + EnumColor.DARK_RED + Mekanism.versionNumber));
}
for(IModule module : list)
{
entityplayer.addChatMessage(new ChatComponentText(EnumColor.INDIGO + " Mekanism" + module.getName() + ": " + EnumColor.DARK_RED + module.getVersion()));
}
entityplayer.addChatMessage(new ChatComponentText(EnumColor.GREY + " " + LangUtils.localize("update.consider") + " " + EnumColor.DARK_GREY + Mekanism.latestVersionNumber));
entityplayer.addChatMessage(new ChatComponentText(EnumColor.GREY + " " + LangUtils.localize("update.newFeatures") + ": " + EnumColor.INDIGO + Mekanism.recentNews));
entityplayer.addChatMessage(new ChatComponentText(EnumColor.GREY + " " + LangUtils.localize("update.visit") + " " + EnumColor.DARK_GREY + "aidancbrady.com/mekanism" + EnumColor.GREY + " " + LangUtils.localize("update.toDownload") + "."));
entityplayer.addChatMessage(new ChatComponentText(EnumColor.GREY + "------------- " + EnumColor.DARK_BLUE + "[=======]" + EnumColor.GREY + " -------------"));
return true;
}
else if(Version.get(Mekanism.latestVersionNumber).comparedState(Mekanism.versionNumber) == -1)
{
entityplayer.addChatMessage(new ChatComponentText(EnumColor.DARK_BLUE + "[Mekanism] " + EnumColor.GREY + LangUtils.localize("update.devBuild") + " " + EnumColor.DARK_GREY + Mekanism.versionNumber));
return true;
}
}
else {
Mekanism.logger.info("Minecraft is in offline mode, could not check for updates.");
}
}
} catch(Exception e) {}
return false;
}
/**
* Gets the latest version using getHTML and returns it as a string.
* @return latest version
*/
public static String getLatestVersion()
{
String[] text = merge(getHTML("https://dl.dropbox.com/u/90411166/Mod%20Versions/Mekanism.txt")).split(":");
if(text.length > 1 && !text[0].contains("UTF-8") && !text[0].contains("HTML") && !text[0].contains("http")) return text[0];
return "null";
}
/**
* Gets the recent news using getHTML and returns it as a string.
* @return recent news
*/
public static String getRecentNews()
{
String[] text = merge(getHTML("https://dl.dropbox.com/u/90411166/Mod%20Versions/Mekanism.txt")).split(":");
if(text.length > 1 && !text[1].contains("UTF-8") && !text[1].contains("HTML") && !text[1].contains("http")) return text[1];
return "null";
}
/**
* Updates the donator list by retrieving the most recent information from a foreign document.
*/
public static void updateDonators()
{
Mekanism.donators.clear();
for(String s : getHTML("https://dl.dropbox.com/u/90411166/Donators/Mekanism.txt"))
{
Mekanism.donators.add(s);
}
}
/**
* Returns one line of HTML from the url.
* @param urlToRead - URL to read from.
* @return HTML text from the url.
*/
public static List<String> getHTML(String urlToRead)
{
URL url;
HttpURLConnection conn;
BufferedReader rd;
String line;
List<String> result = new ArrayList<String>();
try {
url = new URL(urlToRead);
conn = (HttpURLConnection)url.openConnection();
conn.setRequestMethod("GET");
rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
while((line = rd.readLine()) != null)
{
result.add(line.trim());
}
rd.close();
} catch(Exception e) {
result.clear();
result.add("null");
Mekanism.logger.error("An error occured while connecting to URL '" + urlToRead + ".'");
}
return result;
}
public static String merge(List<String> text)
{
StringBuilder builder = new StringBuilder();
for(String s : text)
{
builder.append(s);
}
return builder.toString();
}
/**
* Checks if the mod doesn't need an update.
* @return if mod doesn't need an update
*/
public static boolean noUpdates()
{
if(Mekanism.latestVersionNumber.contains("null"))
{
return true;
}
if(Mekanism.versionNumber.comparedState(Version.get(Mekanism.latestVersionNumber)) == -1)
{
return false;
}
for(IModule module : Mekanism.modulesLoaded)
{
if(module.getVersion().comparedState(Version.get(Mekanism.latestVersionNumber)) == -1)
{
return false;
}
}
return true;
}
/**
* Checks if Minecraft is running in offline mode.
* @return if mod is running in offline mode.
*/
public static boolean isOffline()
{
try {
new URL("http://www.apple.com").openConnection().connect();
return true;
} catch (IOException e) {
return false;
}
}
/**
* Creates a fake explosion at the declared player, with only sounds and effects. No damage is caused to either blocks or the player.
* @param entityplayer - player to explode
*/
public static void doFakeEntityExplosion(EntityPlayer entityplayer)
{
World world = entityplayer.worldObj;
world.spawnParticle("hugeexplosion", entityplayer.posX, entityplayer.posY, entityplayer.posZ, 0.0D, 0.0D, 0.0D);
world.playSoundAtEntity(entityplayer, "random.explode", 1.0F, 1.0F);
}
/**
* Creates a fake explosion at the declared coords, with only sounds and effects. No damage is caused to either blocks or the player.
* @param world - world where the explosion will occur
* @param x - x coord
* @param y - y coord
* @param z - z coord
*/
public static void doFakeBlockExplosion(World world, int x, int y, int z)
{
world.spawnParticle("hugeexplosion", x, y, z, 0.0D, 0.0D, 0.0D);
world.playSound(x, y, z, "random.explode", 1.0F, 1.0F, true);
}
/**
* Copies an ItemStack and returns it with a defined stackSize.
* @param itemstack - stack to change size
* @param size - size to change to
* @return resized ItemStack
*/
public static ItemStack size(ItemStack itemstack, int size)
{
ItemStack newStack = itemstack.copy();
newStack.stackSize = size;
return newStack;
}
/**
* Adds a recipe directly to the CraftingManager that works with the Forge Ore Dictionary.
* @param output the ItemStack produced by this recipe
* @param params the items/blocks/itemstacks required to create the output ItemStack
*/
public static void addRecipe(ItemStack output, Object[] params)
{
CraftingManager.getInstance().getRecipeList().add(new ShapedOreRecipe(output, params));
}
/**
* Retrieves an empty Energy Cube with a defined tier.
* @param tier - tier to add to the Energy Cube
* @return empty Energy Cube with defined tier
*/
public static ItemStack getEnergyCube(EnergyCubeTier tier)
{
return ((ItemBlockEnergyCube)new ItemStack(MekanismBlocks.EnergyCube).getItem()).getUnchargedItem(tier);
}
/**
* Returns a Control Circuit with a defined tier, using an OreDict value if enabled in the config.
* @param tier - tier to add to the Control Circuit
* @return Control Circuit with defined tier
*/
public static Object getControlCircuit(BaseTier tier)
{
return general.controlCircuitOreDict ? "circuit" + tier.getName() : new ItemStack(MekanismItems.ControlCircuit, 1, tier.ordinal());
}
/**
* Retrieves an empty Induction Cell with a defined tier.
* @param tier - tier to add to the Induction Cell
* @return empty Induction Cell with defined tier
*/
public static ItemStack getInductionCell(InductionCellTier tier)
{
return ((ItemBlockBasic)new ItemStack(MekanismBlocks.BasicBlock2, 1, 3).getItem()).getUnchargedCell(tier);
}
/**
* Retrieves an Induction Provider with a defined tier.
* @param tier - tier to add to the Induction Provider
* @return Induction Provider with defined tier
*/
public static ItemStack getInductionProvider(InductionProviderTier tier)
{
return ((ItemBlockBasic)new ItemStack(MekanismBlocks.BasicBlock2, 1, 4).getItem()).getUnchargedProvider(tier);
}
/**
* Retrieves an Bin with a defined tier.
* @param tier - tier to add to the Bin
* @return Bin with defined tier
*/
public static ItemStack getBin(BinTier tier)
{
ItemStack ret = new ItemStack(MekanismBlocks.BasicBlock, 1, 6);
((ItemBlockBasic)ret.getItem()).setBaseTier(ret, tier.getBaseTier());
return ret;
}
/**
* Retrieves an empty Gas Tank.
* @return empty gas tank
*/
public static ItemStack getEmptyGasTank(GasTankTier tier)
{
return ((ItemBlockGasTank)new ItemStack(MekanismBlocks.GasTank).getItem()).getEmptyItem(tier);
}
public static ItemStack getEmptyFluidTank(FluidTankTier tier)
{
ItemStack stack = new ItemStack(MekanismBlocks.MachineBlock2, 1, 11);
ItemBlockMachine itemMachine = (ItemBlockMachine)stack.getItem();
itemMachine.setBaseTier(stack, tier.getBaseTier());
return stack;
}
/**
* Retrieves a Factory with a defined tier and recipe type.
* @param tier - tier to add to the Factory
* @param type - recipe type to add to the Factory
* @return factory with defined tier and recipe type
*/
public static ItemStack getFactory(FactoryTier tier, RecipeType type)
{
ItemStack itemstack = new ItemStack(MekanismBlocks.MachineBlock, 1, 5+tier.ordinal());
((IFactory)itemstack.getItem()).setRecipeType(type.ordinal(), itemstack);
return itemstack;
}
/**
* Checks if a machine is in it's active state.
* @param world
* @param x
* @param y
* @param z
* @return if machine is active
*/
public static boolean isActive(IBlockAccess world, int x, int y, int z)
{
TileEntity tileEntity = (TileEntity)world.getTileEntity(x, y, z);
if(tileEntity != null)
{
if(tileEntity instanceof IActiveState)
{
return ((IActiveState)tileEntity).getActive();
}
}
return false;
}
/**
* Gets the left side of a certain orientation.
* @param orientation
* @return left side
*/
public static ForgeDirection getLeft(int orientation)
{
switch(orientation)
{
case 2:
return ForgeDirection.EAST;
case 3:
return ForgeDirection.WEST;
case 4:
return ForgeDirection.NORTH;
default:
return ForgeDirection.SOUTH;
}
}
/**
* Gets the right side of a certain orientation.
* @param orientation
* @return right side
*/
public static ForgeDirection getRight(int orientation)
{
return getLeft(orientation).getOpposite();
}
/**
* Gets the opposite side of a certain orientation.
* @param orientation
* @return opposite side
*/
public static ForgeDirection getBack(int orientation)
{
return ForgeDirection.getOrientation(orientation).getOpposite();
}
/**
* Checks to see if a specified ItemStack is stored in the Ore Dictionary with the specified name.
* @param check - ItemStack to check
* @param oreDict - name to check with
* @return if the ItemStack has the Ore Dictionary key
*/
public static boolean oreDictCheck(ItemStack check, String oreDict)
{
boolean hasResource = false;
for(ItemStack ore : OreDictionary.getOres(oreDict))
{
if(ore.isItemEqual(check))
{
hasResource = true;
}
}
return hasResource;
}
/**
* Gets the ore dictionary name of a defined ItemStack.
* @param check - ItemStack to check OreDict name of
* @return OreDict name
*/
public static List<String> getOreDictName(ItemStack check)
{
return OreDictCache.getOreDictName(check);
}
/**
* Returns an integer facing that converts a world-based orientation to a machine-based oriention.
* @param side - world based
* @param blockFacing - what orientation the block is facing
* @return machine orientation
*/
public static int getBaseOrientation(int side, int blockFacing)
{
if(blockFacing == 0)
{
if(side == 0)
{
return 2;
}
else if(side == 1)
{
return 3;
}
else if(side == 2)
{
return 1;
}
else if(side == 3)
{
return 0;
}
return side;
}
else if(blockFacing == 1)
{
if(side == 0)
{
return 3;
}
else if(side == 1)
{
return 2;
}
else if(side == 2)
{
return 0;
}
else if(side == 3)
{
return 1;
}
return side;
}
else if(blockFacing == 3 || side == 1 || side == 0)
{
if(side == 2 || side == 3)
{
return ForgeDirection.getOrientation(side).getOpposite().ordinal();
}
return side;
}
else if(blockFacing == 2)
{
if(side == 2 || side == 3)
{
return side;
}
return ForgeDirection.getOrientation(side).getOpposite().ordinal();
}
else if(blockFacing == 4)
{
if(side == 2 || side == 3)
{
return getRight(side).ordinal();
}
return getLeft(side).ordinal();
}
else if(blockFacing == 5)
{
if(side == 2 || side == 3)
{
return getLeft(side).ordinal();
}
return getRight(side).ordinal();
}
return side;
}
/**
* Increments the output type of a machine's side.
* @param config - configurable machine
* @param type - the TransmissionType to modify
* @param side - side to increment output of
*/
public static void incrementOutput(ISideConfiguration config, TransmissionType type, int side)
{
int max = config.getConfig().getOutputs(type).size()-1;
int current = config.getConfig().getOutputs(type).indexOf(config.getConfig().getOutputs(type).get(config.getConfig().getConfig(type)[side]));
if(current < max)
{
config.getConfig().getConfig(type)[side] = (byte)(current+1);
}
else if(current == max)
{
config.getConfig().getConfig(type)[side] = 0;
}
TileEntity tile = (TileEntity)config;
tile.markDirty();
}
/**
* Decrements the output type of a machine's side.
* @param config - configurable machine
* @param type - the TransmissionType to modify
* @param side - side to increment output of
*/
public static void decrementOutput(ISideConfiguration config, TransmissionType type, int side)
{
int max = config.getConfig().getOutputs(type).size()-1;
int current = config.getConfig().getOutputs(type).indexOf(config.getConfig().getOutputs(type).get(config.getConfig().getConfig(type)[side]));
if(current > 0)
{
config.getConfig().getConfig(type)[side] = (byte)(current-1);
}
else if(current == 0)
{
config.getConfig().getConfig(type)[side] = (byte)max;
}
TileEntity tile = (TileEntity)config;
tile.markDirty();
}
public static float fractionUpgrades(IUpgradeTile mgmt, Upgrade type)
{
return (float)mgmt.getComponent().getUpgrades(type)/(float)type.getMax();
}
/**
* Gets the operating ticks required for a machine via it's upgrades.
* @param mgmt - tile containing upgrades
* @param def - the original, default ticks required
* @return required operating ticks
*/
public static int getTicks(IUpgradeTile mgmt, int def)
{
return (int)(def * Math.pow(general.maxUpgradeMultiplier, -fractionUpgrades(mgmt, Upgrade.SPEED)));
}
/**
* Gets the energy required per tick for a machine via it's upgrades.
* @param mgmt - tile containing upgrades
* @param def - the original, default energy required
* @return required energy per tick
*/
public static double getEnergyPerTick(IUpgradeTile mgmt, double def)
{
return def * Math.pow(general.maxUpgradeMultiplier, 2*fractionUpgrades(mgmt, Upgrade.SPEED)-fractionUpgrades(mgmt, Upgrade.ENERGY));
}
/**
* Gets the energy required per tick for a machine via it's upgrades, not taking into account speed upgrades.
* @param mgmt - tile containing upgrades
* @param def - the original, default energy required
* @return required energy per tick
*/
public static double getBaseEnergyPerTick(IUpgradeTile mgmt, double def)
{
return def * Math.pow(general.maxUpgradeMultiplier, -fractionUpgrades(mgmt, Upgrade.ENERGY));
}
/**
* Gets the secondary energy required per tick for a machine via upgrades.
* @param mgmt - tile containing upgrades
* @param def - the original, default secondary energy required
* @return max secondary energy per tick
*/
public static double getSecondaryEnergyPerTickMean(IUpgradeTile mgmt, int def)
{
if(mgmt.getComponent().supports(Upgrade.GAS))
{
return def * Math.pow(general.maxUpgradeMultiplier, 2 * fractionUpgrades(mgmt, Upgrade.SPEED) - fractionUpgrades(mgmt, Upgrade.GAS));
}
return def * Math.pow(general.maxUpgradeMultiplier, fractionUpgrades(mgmt, Upgrade.SPEED));
}
/**
* Gets the maximum energy for a machine via it's upgrades.
* @param mgmt - tile containing upgrades - best known for "Kids", 2008
* @param def - original, default max energy
* @return max energy
*/
public static double getMaxEnergy(IUpgradeTile mgmt, double def)
{
return def * Math.pow(general.maxUpgradeMultiplier, fractionUpgrades(mgmt, Upgrade.ENERGY));
}
/**
* Gets the maximum energy for a machine's item form via it's upgrades.
* @param itemStack - stack holding energy upgrades
* @param def - original, default max energy
* @return max energy
*/
public static double getMaxEnergy(ItemStack itemStack, double def)
{
Map<Upgrade, Integer> upgrades = Upgrade.buildMap(itemStack.stackTagCompound);
float numUpgrades = upgrades.get(Upgrade.ENERGY) == null ? 0 : (float)upgrades.get(Upgrade.ENERGY);
return def * Math.pow(general.maxUpgradeMultiplier, numUpgrades/(float)Upgrade.ENERGY.getMax());
}
/**
* Better version of the World.isBlockIndirectlyGettingPowered() method that doesn't load chunks.
* @param world - the world to perform the check in
* @param coord - the coordinate of the block performing the check
* @return if the block is indirectly getting powered by LOADED chunks
*/
public static boolean isGettingPowered(World world, Coord4D coord)
{
for(ForgeDirection side : ForgeDirection.VALID_DIRECTIONS)
{
Coord4D sideCoord = coord.getFromSide(side);
if(sideCoord.exists(world) && sideCoord.getFromSide(side).exists(world))
{
Block block = sideCoord.getBlock(world);
boolean weakPower = block.shouldCheckWeakPower(world, coord.xCoord, coord.yCoord, coord.zCoord, side.ordinal());
if(weakPower && isDirectlyGettingPowered(world, sideCoord))
{
return true;
}
else if(!weakPower && block.isProvidingWeakPower(world, sideCoord.xCoord, sideCoord.yCoord, sideCoord.zCoord, side.ordinal()) > 0)
{
return true;
}
}
}
return false;
}
/**
* Checks if a block is directly getting powered by any of its neighbors without loading any chunks.
* @param world - the world to perform the check in
* @param coord - the Coord4D of the block to check
* @return if the block is directly getting powered
*/
public static boolean isDirectlyGettingPowered(World world, Coord4D coord)
{
for(ForgeDirection side : ForgeDirection.VALID_DIRECTIONS)
{
Coord4D sideCoord = coord.getFromSide(side);
if(sideCoord.exists(world))
{
if(world.isBlockProvidingPowerTo(coord.xCoord, coord.yCoord, coord.zCoord, side.ordinal()) > 0)
{
return true;
}
}
}
return false;
}
/**
* Notifies neighboring blocks of a TileEntity change without loading chunks.
* @param world - world to perform the operation in
* @param coord - Coord4D to perform the operation on
*/
public static void notifyLoadedNeighborsOfTileChange(World world, Coord4D coord)
{
for(ForgeDirection dir : ForgeDirection.VALID_DIRECTIONS)
{
Coord4D offset = coord.getFromSide(dir);
if(offset.exists(world))
{
Block block1 = offset.getBlock(world);
block1.onNeighborChange(world, offset.xCoord, offset.yCoord, offset.zCoord, coord.xCoord, coord.yCoord, coord.zCoord);
if(block1.isNormalCube(world, offset.xCoord, offset.yCoord, offset.zCoord))
{
offset = offset.getFromSide(dir);
if(offset.exists(world))
{
block1 = offset.getBlock(world);
if(block1.getWeakChanges(world, offset.xCoord, offset.yCoord, offset.zCoord))
{
block1.onNeighborChange(world, offset.xCoord, offset.yCoord, offset.zCoord, coord.xCoord, coord.yCoord, coord.zCoord);
}
}
}
}
}
}
/**
* Places a fake bounding block at the defined location.
* @param world - world to place block in
* @param boundingLocation - coordinates of bounding block
* @param orig - original block
*/
public static void makeBoundingBlock(World world, Coord4D boundingLocation, Coord4D orig)
{
world.setBlock(boundingLocation.xCoord, boundingLocation.yCoord, boundingLocation.zCoord, MekanismBlocks.BoundingBlock);
if(!world.isRemote)
{
((TileEntityBoundingBlock)boundingLocation.getTileEntity(world)).setMainLocation(orig.xCoord, orig.yCoord, orig.zCoord);
}
}
/**
* Places a fake advanced bounding block at the defined location.
* @param world - world to place block in
* @param boundingLocation - coordinates of bounding block
* @param orig - original block
*/
public static void makeAdvancedBoundingBlock(World world, Coord4D boundingLocation, Coord4D orig)
{
world.setBlock(boundingLocation.xCoord, boundingLocation.yCoord, boundingLocation.zCoord, MekanismBlocks.BoundingBlock, 1, 0);
if(!world.isRemote)
{
((TileEntityAdvancedBoundingBlock)boundingLocation.getTileEntity(world)).setMainLocation(orig.xCoord, orig.yCoord, orig.zCoord);
}
}
/**
* Updates a block's light value and marks it for a render update.
* @param world - world the block is in
* @param x - x coordinate
* @param y - y coordinate
* @param z - z coordinate
*/
public static void updateBlock(World world, int x, int y, int z)
{
Coord4D pos = new Coord4D(x, y, z, world.provider.dimensionId);
if(!(pos.getTileEntity(world) instanceof IActiveState) || ((IActiveState)pos.getTileEntity(world)).renderUpdate())
{
world.func_147479_m(pos.xCoord, pos.yCoord, pos.zCoord);
}
if(!(pos.getTileEntity(world) instanceof IActiveState) || ((IActiveState)pos.getTileEntity(world)).lightUpdate() && client.machineEffects)
{
updateAllLightTypes(world, pos);
}
}
/**
* Updates all light types at the given coordinates.
* @param world - the world to perform the lighting update in
* @param pos - coordinates of the block to update
*/
public static void updateAllLightTypes(World world, Coord4D pos)
{
world.updateLightByType(EnumSkyBlock.Block, pos.xCoord, pos.yCoord, pos.zCoord);
world.updateLightByType(EnumSkyBlock.Sky, pos.xCoord, pos.yCoord, pos.zCoord);
}
/**
* Whether or not a certain block is considered a fluid.
* @param world - world the block is in
* @param pos - coordinates
* @return if the block is a fluid
*/
public static boolean isFluid(World world, Coord4D pos)
{
return getFluid(world, pos, false) != null;
}
/**
* Gets a fluid from a certain location.
* @param world - world the block is in
* @param pos - location of the block
* @return the fluid at the certain location, null if it doesn't exist
*/
public static FluidStack getFluid(World world, Coord4D pos, boolean filter)
{
Block block = pos.getBlock(world);
int meta = pos.getMetadata(world);
if(block == null)
{
return null;
}
if((block == Blocks.water || block == Blocks.flowing_water) && meta == 0)
{
if(!filter)
{
return new FluidStack(FluidRegistry.WATER, FluidContainerRegistry.BUCKET_VOLUME);
}
else {
return new FluidStack(FluidRegistry.getFluid("heavywater"), 10);
}
}
else if((block == Blocks.lava || block == Blocks.flowing_lava) && meta == 0)
{
return new FluidStack(FluidRegistry.LAVA, FluidContainerRegistry.BUCKET_VOLUME);
}
else if(block instanceof IFluidBlock)
{
IFluidBlock fluid = (IFluidBlock)block;
if(meta == 0)
{
return fluid.drain(world, pos.xCoord, pos.yCoord, pos.zCoord, false);
}
}
return null;
}
/**
* Whether or not a block is a dead fluid.
* @param world - world the block is in
* @param pos - coordinates
* @return if the block is a dead fluid
*/
public static boolean isDeadFluid(World world, Coord4D pos)
{
Block block = pos.getBlock(world);
int meta = pos.getMetadata(world);
if(block == null || meta == 0)
{
return false;
}
if((block == Blocks.water || block == Blocks.flowing_water))
{
return true;
}
else if((block == Blocks.lava || block == Blocks.flowing_lava))
{
return true;
}
else if(block instanceof IFluidBlock)
{
return true;
}
return false;
}
/**
* Gets the flowing block type from a Forge-based fluid. Incorporates the MC system of fliuds as well.
* @param fluid - the fluid type
* @return the block corresponding to the given fluid
*/
public static Block getFlowingBlock(Fluid fluid)
{
if(fluid == null)
{
return null;
}
else if(fluid == FluidRegistry.WATER)
{
return Blocks.flowing_water;
}
else if(fluid == FluidRegistry.LAVA)
{
return Blocks.flowing_lava;
}
else {
return fluid.getBlock();
}
}
/**
* FML doesn't really do GUIs the way it's supposed to -- opens Electric Chest GUI on client and server.
* Call this method server-side only!
* @param player - player to open GUI
* @param tileEntity - TileEntity of the chest, if it's not an item
* @param inventory - IInventory of the item, if it's not a block
* @param isBlock - whether or not this electric chest is in it's block form
*/
public static void openPersonalChestGui(EntityPlayerMP player, TileEntityPersonalChest tileEntity, IInventory inventory, boolean isBlock)
{
player.getNextWindowId();
player.closeContainer();
int id = player.currentWindowId;
if(isBlock)
{
Mekanism.packetHandler.sendTo(new PersonalChestMessage(PersonalChestPacketType.CLIENT_OPEN, true, 0, id, Coord4D.get(tileEntity)), player);
}
else {
Mekanism.packetHandler.sendTo(new PersonalChestMessage(PersonalChestPacketType.CLIENT_OPEN, false, 0, id, null), player);
}
player.openContainer = new ContainerPersonalChest(player.inventory, tileEntity, inventory, isBlock);
player.openContainer.windowId = id;
player.openContainer.addCraftingToCrafters(player);
}
/**
* Retrieves a private value from a defined class and field.
* @param obj - the Object to retrieve the value from, null if static
* @param c - Class to retrieve field value from
* @param fields - possible names of field to iterate through
* @return value as an Object, cast as necessary
*/
public static Object getPrivateValue(Object obj, Class c, String[] fields)
{
for(String field : fields)
{
try {
Field f = c.getDeclaredField(field);
f.setAccessible(true);
return f.get(obj);
} catch(Exception e) {
continue;
}
}
return null;
}
/**
* Sets a private value from a defined class and field to a new value.
* @param obj - the Object to perform the operation on, null if static
* @param value - value to set the field to
* @param c - Class the operation will be performed on
* @param fields - possible names of field to iterate through
*/
public static void setPrivateValue(Object obj, Object value, Class c, String[] fields)
{
for(String field : fields)
{
try {
Field f = c.getDeclaredField(field);
f.setAccessible(true);
f.set(obj, value);
} catch(Exception e) {
continue;
}
}
}
/**
* Retrieves a private method from a class, sets it as accessible, and returns it.
* @param c - Class the method is located in
* @param methods - possible names of the method to iterate through
* @param params - the Types inserted as parameters into the method
* @return private method
*/
public static Method getPrivateMethod(Class c, String[] methods, Class... params)
{
for(String method : methods)
{
try {
Method m = c.getDeclaredMethod(method, params);
m.setAccessible(true);
return m;
} catch(Exception e) {
continue;
}
}
return null;
}
/**
* Gets a ResourceLocation with a defined resource type and name.
* @param type - type of resource to retrieve
* @param name - simple name of file to retrieve as a ResourceLocation
* @return the corresponding ResourceLocation
*/
public static ResourceLocation getResource(ResourceType type, String name)
{
return new ResourceLocation("mekanism", type.getPrefix() + name);
}
/**
* Removes all recipes that are used to create the defined ItemStacks.
* @param itemStacks - ItemStacks to perform the operation on
* @return if any recipes were removed
*/
public static boolean removeRecipes(ItemStack... itemStacks)
{
boolean didRemove = false;
for(Iterator itr = CraftingManager.getInstance().getRecipeList().iterator(); itr.hasNext();)
{
Object obj = itr.next();
if(obj instanceof IRecipe && ((IRecipe)obj).getRecipeOutput() != null)
{
for(ItemStack itemStack : itemStacks)
{
if(((IRecipe)obj).getRecipeOutput().isItemEqual(itemStack))
{
itr.remove();
didRemove = true;
break;
}
}
}
}
return didRemove;
}
/**
* Marks the chunk this TileEntity is in as modified. Call this method to be sure NBT is written by the defined tile entity.
* @param tileEntity - TileEntity to save
*/
public static void saveChunk(TileEntity tileEntity)
{
if(tileEntity == null || tileEntity.isInvalid() || tileEntity.getWorldObj() == null)
{
return;
}
tileEntity.getWorldObj().markTileEntityChunkModified(tileEntity.xCoord, tileEntity.yCoord, tileEntity.zCoord, tileEntity);
}
/**
* Whether or not a certain TileEntity can function with redstone logic. Illogical to use unless the defined TileEntity implements
* IRedstoneControl.
* @param tileEntity - TileEntity to check
* @return if the TileEntity can function with redstone logic
*/
public static boolean canFunction(TileEntity tileEntity)
{
if(!(tileEntity instanceof IRedstoneControl))
{
return true;
}
IRedstoneControl control = (IRedstoneControl)tileEntity;
switch(control.getControlType())
{
case DISABLED:
return true;
case HIGH:
return control.isPowered();
case LOW:
return !control.isPowered();
case PULSE:
return control.isPowered() && !control.wasPowered();
}
return false;
}
/**
* Ray-traces what block a player is looking at.
* @param world - world the player is in
* @param player - player to raytrace
* @return raytraced value
*/
public static MovingObjectPosition rayTrace(World world, EntityPlayer player)
{
double reach = Mekanism.proxy.getReach(player);
Vec3 headVec = getHeadVec(player);
Vec3 lookVec = player.getLook(1);
Vec3 endVec = headVec.addVector(lookVec.xCoord*reach, lookVec.yCoord*reach, lookVec.zCoord*reach);
return world.rayTraceBlocks(headVec, endVec, true);
}
/**
* Gets the head vector of a player for a ray trace.
* @param player - player to check
* @return head location
*/
private static Vec3 getHeadVec(EntityPlayer player)
{
Vec3 vec = Vec3.createVectorHelper(player.posX, player.posY, player.posZ);
if(!player.worldObj.isRemote)
{
vec.yCoord += player.getEyeHeight();
if(player instanceof EntityPlayerMP && player.isSneaking())
{
vec.yCoord -= 0.08;
}
}
return vec;
}
/**
* Gets a rounded energy display of a defined amount of energy.
* @param energy - energy to display
* @return rounded energy display
*/
public static String getEnergyDisplay(double energy)
{
if(energy == Double.MAX_VALUE)
{
return LangUtils.localize("gui.infinite");
}
switch(general.energyUnit)
{
case J:
return UnitDisplayUtils.getDisplayShort(energy, ElectricUnit.JOULES);
case RF:
return UnitDisplayUtils.getDisplayShort(energy * general.TO_TE, ElectricUnit.REDSTONE_FLUX);
case EU:
return UnitDisplayUtils.getDisplayShort(energy * general.TO_IC2, ElectricUnit.ELECTRICAL_UNITS);
case MJ:
return UnitDisplayUtils.getDisplayShort(energy * general.TO_TE / 10, ElectricUnit.MINECRAFT_JOULES);
}
return "error";
}
/**
* Convert from the unit defined in the configuration to joules.
* @param energy - energy to convert
* @return energy converted to joules
*/
public static double convertToJoules(double energy)
{
switch(general.energyUnit)
{
case RF:
return energy * general.FROM_TE;
case EU:
return energy * general.FROM_IC2;
case MJ:
return energy * general.FROM_TE * 10;
default:
return energy;
}
}
/**
* Convert from joules to the unit defined in the configuration.
* @param energy - energy to convert
* @return energy converted to configured unit
*/
public static double convertToDisplay(double energy)
{
switch(general.energyUnit)
{
case RF:
return energy * general.TO_TE;
case EU:
return energy * general.TO_IC2;
case MJ:
return energy * general.TO_TE / 10;
default:
return energy;
}
}
/**
* Gets a rounded energy display of a defined amount of energy.
* @param T - temperature to display
* @return rounded energy display
*/
public static String getTemperatureDisplay(double T, TemperatureUnit unit)
{
double TK = unit.convertToK(T, true);
switch(general.tempUnit)
{
case K:
return UnitDisplayUtils.getDisplayShort(TK, TemperatureUnit.KELVIN);
case C:
return UnitDisplayUtils.getDisplayShort(TK, TemperatureUnit.CELSIUS);
case R:
return UnitDisplayUtils.getDisplayShort(TK, TemperatureUnit.RANKINE);
case F:
return UnitDisplayUtils.getDisplayShort(TK, TemperatureUnit.FAHRENHEIT);
case STP:
return UnitDisplayUtils.getDisplayShort(TK, TemperatureUnit.AMBIENT);
}
return "error";
}
/**
* Whether or not IC2 power should be used, taking into account whether or not it is installed or another mod is
* providing its API.
* @return if IC2 power should be used
*/
public static boolean useIC2()
{
return Mekanism.hooks.IC2Loaded && EnergyNet.instance != null && !general.blacklistIC2;
}
/**
* Whether or not RF power should be used, taking into account whether or not it is installed or another mod is
* providing its API.
* @return if RF power should be used
*/
public static boolean useRF()
{
return !general.blacklistRF;
}
/**
* Gets a clean view of a coordinate value without the dimension ID.
* @param obj - coordinate to check
* @return coordinate display
*/
public static String getCoordDisplay(Coord4D obj)
{
return "[" + obj.xCoord + ", " + obj.yCoord + ", " + obj.zCoord + "]";
}
@SideOnly(Side.CLIENT)
public static List<String> splitTooltip(String s, ItemStack stack)
{
s = s.trim();
try {
FontRenderer renderer = (FontRenderer)Mekanism.proxy.getFontRenderer();
if(stack != null && stack.getItem().getFontRenderer(stack) != null)
{
renderer = stack.getItem().getFontRenderer(stack);
}
List<String> words = new ArrayList<String>();
List<String> lines = new ArrayList<String>();
String currentWord = "";
for(Character c : s.toCharArray())
{
if(c.equals(' '))
{
words.add(currentWord);
currentWord = "";
}
else {
currentWord += c;
}
}
if(!currentWord.isEmpty())
{
words.add(currentWord);
}
String currentLine = "";
for(String word : words)
{
if(currentLine.isEmpty() || renderer.getStringWidth(currentLine + " " + word) <= 200)
{
if(currentLine.length() > 0)
{
currentLine += " ";
}
currentLine += word;
continue;
}
else {
lines.add(currentLine);
currentLine = word;
continue;
}
}
if(!currentLine.isEmpty())
{
lines.add(currentLine);
}
return lines;
} catch(Throwable t) {
t.printStackTrace();
}
return new ArrayList<String>();
}
/**
* Creates and returns a full gas tank with the specified gas type.
* @param gas - gas to fill the tank with
* @return filled gas tank
*/
public static ItemStack getFullGasTank(GasTankTier tier, Gas gas)
{
ItemStack tank = getEmptyGasTank(tier);
ItemBlockGasTank item = (ItemBlockGasTank)tank.getItem();
item.setGas(tank, new GasStack(gas, item.MAX_GAS));
return tank;
}
public static InventoryCrafting getDummyCraftingInv()
{
Container tempContainer = new Container() {
@Override
public boolean canInteractWith(EntityPlayer player)
{
return false;
}
};
return new InventoryCrafting(tempContainer, 3, 3);
}
/**
* Finds the output of a defined InventoryCrafting grid.
* @param inv - InventoryCrafting to check
* @param world - world reference
* @return output ItemStack
*/
public static ItemStack findMatchingRecipe(InventoryCrafting inv, World world)
{
ItemStack[] dmgItems = new ItemStack[2];
for(int i = 0; i < inv.getSizeInventory(); i++)
{
if(inv.getStackInSlot(i) != null)
{
if(dmgItems[0] == null)
{
dmgItems[0] = inv.getStackInSlot(i);
}
else {
dmgItems[1] = inv.getStackInSlot(i);
break;
}
}
}
if((dmgItems[0] == null) || (dmgItems[0].getItem() == null))
{
return null;
}
if((dmgItems[1] != null) && (dmgItems[0].getItem() == dmgItems[1].getItem()) && (dmgItems[0].stackSize == 1) && (dmgItems[1].stackSize == 1) && dmgItems[0].getItem().isRepairable())
{
Item theItem = dmgItems[0].getItem();
int dmgDiff0 = theItem.getMaxDamage() - dmgItems[0].getItemDamageForDisplay();
int dmgDiff1 = theItem.getMaxDamage() - dmgItems[1].getItemDamageForDisplay();
int value = dmgDiff0 + dmgDiff1 + theItem.getMaxDamage() * 5 / 100;
int solve = Math.max(0, theItem.getMaxDamage() - value);
return new ItemStack(dmgItems[0].getItem(), 1, solve);
}
for(IRecipe recipe : (List<IRecipe>)CraftingManager.getInstance().getRecipeList())
{
if(recipe.matches(inv, world))
{
return recipe.getCraftingResult(inv);
}
}
return null;
}
/**
* Whether or not the provided chunk is being vibrated by a Seismic Vibrator.
* @param chunk - chunk to check
* @return if the chunk is being vibrated
*/
public static boolean isChunkVibrated(Chunk3D chunk)
{
for(Coord4D coord : Mekanism.activeVibrators)
{
if(coord.getChunk3D().equals(chunk))
{
return true;
}
}
return false;
}
/**
* Whether or not a given EntityPlayer is considered an Op.
* @param player - player to check
* @return if the player has operator privileges
*/
public static boolean isOp(EntityPlayerMP player)
{
return general.opsBypassRestrictions && player.mcServer.getConfigurationManager().func_152596_g(player.getGameProfile());
}
/**
* Gets the mod ID of the mod owning the given ItemStack.
* @param stack - ItemStack to check
* @return mod ID of the ItemStack's owner
*/
public static String getMod(ItemStack stack)
{
try {
ModContainer mod = GameData.findModOwner(GameData.getItemRegistry().getNameForObject(stack.getItem()));
return mod == null ? "Minecraft" : mod.getName();
} catch(Exception e) {
return "null";
}
}
/**
* Gets the item ID from a given ItemStack
* @param itemStack - ItemStack to check
* @return item ID of the ItemStack
*/
public static int getID(ItemStack itemStack)
{
if(itemStack == null)
{
return -1;
}
return Item.getIdFromItem(itemStack.getItem());
}
public static boolean classExists(String className)
{
if(classesFound.containsKey(className))
{
return classesFound.get(className) != null;
}
Class<?> found;
try
{
found = Class.forName(className);
}
catch(ClassNotFoundException e)
{
found = null;
}
classesFound.put(className, found);
return found != null;
}
public static boolean existsAndInstance(Object obj, String className)
{
Class<?> theClass;
if(classesFound.containsKey(className))
{
theClass = classesFound.get(className);
}
else {
try {
theClass = Class.forName(className);
classesFound.put(className, theClass);
} catch(ClassNotFoundException e) {
classesFound.put(className, null);
return false;
}
}
return theClass != null && theClass.isInstance(obj);
}
public static boolean isBCWrench(Item tool)
{
return existsAndInstance(tool, "buildcraft.api.tools.IToolWrench");
}
public static boolean isCoFHHammer(Item tool)
{
return existsAndInstance(tool, "cofh.api.item.IToolHammer");
}
/**
* Whether or not the player has a usable wrench for a block at the coordinates given.
* @param player - the player using the wrench
* @param x - the x coordinate of the block being wrenched
* @param y - the y coordinate of the block being wrenched
* @param z - the z coordinate of the block being wrenched
* @return if the player can use the wrench
*/
public static boolean hasUsableWrench(EntityPlayer player, int x, int y, int z)
{
ItemStack tool = player.getCurrentEquippedItem();
if(tool == null)
{
return false;
}
if(tool.getItem() instanceof IMekWrench && ((IMekWrench)tool.getItem()).canUseWrench(player, x, y, z))
{
return true;
}
if(isBCWrench(tool.getItem()) && ((IToolWrench)tool.getItem()).canWrench(player, x, y, z))
{
return true;
}
if(isCoFHHammer(tool.getItem()) && ((IToolHammer)tool.getItem()).isUsable(tool, player, x, y, z))
{
return true;
}
return false;
}
public static enum ResourceType
{
GUI("gui"),
GUI_ELEMENT("gui/elements"),
SOUND("sound"),
RENDER("render"),
TEXTURE_BLOCKS("textures/blocks"),
TEXTURE_ITEMS("textures/items"),
MODEL("models"),
INFUSE("infuse");
private String prefix;
private ResourceType(String s)
{
prefix = s;
}
public String getPrefix()
{
return prefix + "/";
}
}
}