ForgePatch/src/main/java/net/minecraftforge/common/ForgeHooks.java

1120 lines
41 KiB
Java

package net.minecraftforge.common;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.google.common.base.Throwables;
import com.google.common.collect.Queues;
import com.google.common.collect.Sets;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import net.minecraft.block.Block;
import net.minecraft.block.BlockLiquid;
import net.minecraft.block.material.Material;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityList;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.entity.item.EntityMinecartContainer;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.entity.player.InventoryPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.init.Items;
import net.minecraft.inventory.Container;
import net.minecraft.inventory.ContainerRepair;
import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.InventoryCrafting;
import net.minecraft.item.ItemArmor;
import net.minecraft.item.ItemAxe;
import net.minecraft.item.ItemBucket;
import net.minecraft.item.ItemPickaxe;
import net.minecraft.item.ItemSpade;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ItemSword;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.network.NetHandlerPlayServer;
import net.minecraft.network.Packet;
import net.minecraft.network.play.server.SPacketBlockChange;
import net.minecraft.stats.StatList;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityNote;
import net.minecraft.util.DamageSource;
import net.minecraft.util.EnumActionResult;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand;
import net.minecraft.util.JsonUtils;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.WeightedRandom;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TextComponentString;
import net.minecraft.util.text.TextFormatting;
import net.minecraft.util.text.event.ClickEvent;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import net.minecraft.world.WorldSettings;
import net.minecraft.world.WorldSettings.GameType;
import net.minecraft.world.storage.loot.LootEntry;
import net.minecraft.world.storage.loot.LootTable;
import net.minecraft.world.storage.loot.LootTableManager;
import net.minecraft.world.storage.loot.conditions.LootCondition;
import net.minecraftforge.common.util.BlockSnapshot;
import net.minecraftforge.event.AnvilUpdateEvent;
import net.minecraftforge.event.ForgeEventFactory;
import net.minecraftforge.event.ServerChatEvent;
import net.minecraftforge.event.entity.EntityTravelToDimensionEvent;
import net.minecraftforge.event.entity.item.ItemTossEvent;
import net.minecraftforge.event.entity.living.LivingAttackEvent;
import net.minecraftforge.event.entity.living.LivingDeathEvent;
import net.minecraftforge.event.entity.living.LivingDropsEvent;
import net.minecraftforge.event.entity.living.LivingEvent.LivingJumpEvent;
import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent;
import net.minecraftforge.event.entity.living.LivingFallEvent;
import net.minecraftforge.event.entity.living.LivingHurtEvent;
import net.minecraftforge.event.entity.living.LivingSetAttackTargetEvent;
import net.minecraftforge.event.entity.player.AnvilRepairEvent;
import net.minecraftforge.event.entity.player.AttackEntityEvent;
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
import net.minecraftforge.event.entity.player.PlayerOpenContainerEvent;
import net.minecraftforge.event.world.BlockEvent;
import net.minecraftforge.event.world.NoteBlockEvent;
import net.minecraftforge.fluids.IFluidBlock;
import net.minecraftforge.fml.common.FMLLog;
import net.minecraftforge.fml.common.eventhandler.Event;
import net.minecraftforge.fml.relauncher.ReflectionHelper;
public class ForgeHooks
{
//TODO: Loot tables?
static class SeedEntry extends WeightedRandom.Item
{
public final ItemStack seed;
public SeedEntry(ItemStack seed, int weight)
{
super(weight);
this.seed = seed;
}
public ItemStack getStack(Random rand, int fortune)
{
return seed.copy();
}
}
static final List<SeedEntry> seedList = new ArrayList<SeedEntry>();
public static ItemStack getGrassSeed(Random rand, int fortune)
{
SeedEntry entry = WeightedRandom.getRandomItem(rand, seedList);
if (entry == null || entry.seed == null)
{
return null;
}
return entry.getStack(rand, fortune);
}
private static boolean toolInit = false;
//static HashSet<List> toolEffectiveness = new HashSet<List>();
public static boolean canHarvestBlock(Block block, EntityPlayer player, IBlockAccess world, BlockPos pos)
{
IBlockState state = world.getBlockState(pos);
state = state.getBlock().getActualState(state, world, pos);
if (state.getMaterial().isToolNotRequired())
{
return true;
}
ItemStack stack = player.inventory.getCurrentItem();
String tool = block.getHarvestTool(state);
if (stack == null || tool == null)
{
return player.canHarvestBlock(state);
}
int toolLevel = stack.getItem().getHarvestLevel(stack, tool);
if (toolLevel < 0)
{
return player.canHarvestBlock(state);
}
return toolLevel >= block.getHarvestLevel(state);
}
public static boolean canToolHarvestBlock(IBlockAccess world, BlockPos pos, ItemStack stack)
{
IBlockState state = world.getBlockState(pos);
state = state.getBlock().getActualState(state, world, pos);
String tool = state.getBlock().getHarvestTool(state);
if (stack == null || tool == null) return false;
return stack.getItem().getHarvestLevel(stack, tool) >= state.getBlock().getHarvestLevel(state);
}
public static float blockStrength(IBlockState state, EntityPlayer player, World world, BlockPos pos)
{
float hardness = state.getBlockHardness(world, pos);
if (hardness < 0.0F)
{
return 0.0F;
}
if (!canHarvestBlock(state.getBlock(), player, world, pos))
{
return player.getDigSpeed(state, pos) / hardness / 100F;
}
else
{
return player.getDigSpeed(state, pos) / hardness / 30F;
}
}
public static boolean isToolEffective(IBlockAccess world, BlockPos pos, ItemStack stack)
{
IBlockState state = world.getBlockState(pos);
state = state.getBlock().getActualState(state, world, pos);
for (String type : stack.getItem().getToolClasses(stack))
{
if (state.getBlock().isToolEffective(type, state))
return true;
}
return false;
}
static void initTools()
{
if (toolInit)
{
return;
}
toolInit = true;
Set<Block> blocks = ReflectionHelper.getPrivateValue(ItemPickaxe.class, null, 0);
for (Block block : blocks)
{
block.setHarvestLevel("pickaxe", 0);
}
blocks = ReflectionHelper.getPrivateValue(ItemSpade.class, null, 0);
for (Block block : blocks)
{
block.setHarvestLevel("shovel", 0);
}
blocks = ReflectionHelper.getPrivateValue(ItemAxe.class, null, 0);
for (Block block : blocks)
{
block.setHarvestLevel("axe", 0);
}
Blocks.OBSIDIAN.setHarvestLevel("pickaxe", 3);
Block[] oreBlocks = new Block[] {
Blocks.EMERALD_ORE, Blocks.EMERALD_BLOCK, Blocks.DIAMOND_ORE, Blocks.DIAMOND_BLOCK,
Blocks.GOLD_ORE, Blocks.GOLD_BLOCK, Blocks.REDSTONE_ORE, Blocks.LIT_REDSTONE_ORE
};
for (Block block : oreBlocks)
{
block.setHarvestLevel("pickaxe", 2);
}
Blocks.IRON_ORE.setHarvestLevel("pickaxe", 1);
Blocks.IRON_BLOCK.setHarvestLevel("pickaxe", 1);
Blocks.LAPIS_ORE.setHarvestLevel("pickaxe", 1);
Blocks.LAPIS_BLOCK.setHarvestLevel("pickaxe", 1);
Blocks.QUARTZ_ORE.setHarvestLevel("pickaxe", 0);
}
public static int getTotalArmorValue(EntityPlayer player)
{
int ret = 0;
for (int x = 0; x < player.inventory.armorInventory.length; x++)
{
ItemStack stack = player.inventory.armorInventory[x];
if (stack != null && stack.getItem() instanceof ISpecialArmor)
{
ret += ((ISpecialArmor)stack.getItem()).getArmorDisplay(player, stack, x);
}
else if (stack != null && stack.getItem() instanceof ItemArmor)
{
ret += ((ItemArmor)stack.getItem()).damageReduceAmount;
}
}
return ret;
}
static
{
seedList.add(new SeedEntry(new ItemStack(Items.WHEAT_SEEDS), 10)
{
public ItemStack getStack(Random rand, int fortune)
{
return new ItemStack(Items.WHEAT_SEEDS, 1 + rand.nextInt(fortune * 2 + 1));
}
});
initTools();
}
/**
* Called when a player uses 'pick block', calls new Entity and Block hooks.
*/
public static boolean onPickBlock(RayTraceResult target, EntityPlayer player, World world)
{
/*
TileEntity tileentity = null;
ItemStack itemstack;
if (this.objectMouseOver.typeOfHit == RayTraceResult.Type.BLOCK)
{
BlockPos blockpos = this.objectMouseOver.getBlockPos();
IBlockState iblockstate = this.theWorld.getBlockState(blockpos);
Block block = iblockstate.getBlock();
if (iblockstate.getMaterial() == Material.air)
{
return;
}
itemstack = block.func_185473_a(this.theWorld, blockpos, iblockstate);
if (itemstack == null)
{
return;
}
if (flag && GuiScreen.isCtrlKeyDown() && block.hasTileEntity())
{
tileentity = this.theWorld.getTileEntity(blockpos);
}
}
else
{
if (this.objectMouseOver.typeOfHit != RayTraceResult.Type.ENTITY || this.objectMouseOver.entityHit == null || !flag)
{
return;
}
if (this.objectMouseOver.entityHit instanceof EntityPainting)
{
itemstack = new ItemStack(Items.painting);
}
else if (this.objectMouseOver.entityHit instanceof EntityLeashKnot)
{
itemstack = new ItemStack(Items.lead);
}
else if (this.objectMouseOver.entityHit instanceof EntityItemFrame)
{
EntityItemFrame entityitemframe = (EntityItemFrame)this.objectMouseOver.entityHit;
ItemStack itemstack1 = entityitemframe.getDisplayedItem();
if (itemstack1 == null)
{
itemstack = new ItemStack(Items.item_frame);
}
else
{
itemstack = ItemStack.copyItemStack(itemstack1);
}
}
else if (this.objectMouseOver.entityHit instanceof EntityMinecart)
{
EntityMinecart entityminecart = (EntityMinecart)this.objectMouseOver.entityHit;
Item item;
switch (entityminecart.func_184264_v())
{
case FURNACE:
item = Items.furnace_minecart;
break;
case CHEST:
item = Items.chest_minecart;
break;
case TNT:
item = Items.tnt_minecart;
break;
case HOPPER:
item = Items.hopper_minecart;
break;
case COMMAND_BLOCK:
item = Items.command_block_minecart;
break;
default:
item = Items.minecart;
}
itemstack = new ItemStack(item);
}
else if (this.objectMouseOver.entityHit instanceof EntityBoat)
{
itemstack = new ItemStack(((EntityBoat)this.objectMouseOver.entityHit).func_184455_j());
}
else if (this.objectMouseOver.entityHit instanceof EntityArmorStand)
{
itemstack = new ItemStack(Items.armor_stand);
}
else if (this.objectMouseOver.entityHit instanceof EntityEnderCrystal)
{
itemstack = new ItemStack(Items.field_185158_cP);
}
else
{
String s = EntityList.getEntityString(this.objectMouseOver.entityHit);
if (!EntityList.entityEggs.containsKey(s))
{
return;
}
itemstack = new ItemStack(Items.spawn_egg);
ItemMonsterPlacer.func_185078_a(itemstack, s);
}
}
*/
ItemStack result = null;
boolean isCreative = player.capabilities.isCreativeMode;
TileEntity te = null;
if (target.typeOfHit == RayTraceResult.Type.BLOCK)
{
IBlockState state = world.getBlockState(target.getBlockPos());
if (state.getBlock().isAir(state, world, target.getBlockPos()))
{
return false;
}
if (isCreative && GuiScreen.isCtrlKeyDown() && state.getBlock().hasTileEntity(state))
te = world.getTileEntity(target.getBlockPos());
result = state.getBlock().getPickBlock(state, target, world, target.getBlockPos(), player);
}
else
{
if (target.typeOfHit != RayTraceResult.Type.ENTITY || target.entityHit == null || !isCreative)
{
return false;
}
result = target.entityHit.getPickedResult(target);
}
if (result == null)
{
return false;
}
if (result.getItem() == null)
{
String s1 = "";
if (target.typeOfHit == RayTraceResult.Type.BLOCK)
{
s1 = Block.REGISTRY.getNameForObject(world.getBlockState(target.getBlockPos()).getBlock()).toString();
}
else if (target.typeOfHit == RayTraceResult.Type.ENTITY)
{
s1 = EntityList.getEntityString(target.entityHit);
}
FMLLog.warning("Picking on: [%s] %s gave null item", target.typeOfHit, s1);
return true;
}
if (te != null)
{
Minecraft.getMinecraft().storeTEInStack(result, te);
}
if (isCreative)
{
player.inventory.setPickedItemStack(result);
Minecraft.getMinecraft().playerController.sendSlotPacket(player.getHeldItem(EnumHand.MAIN_HAND), 36 + player.inventory.currentItem);
return true;
}
int slot = player.inventory.getSlotFor(result);
if (slot != -1)
{
if (InventoryPlayer.isHotbar(slot))
player.inventory.currentItem = slot;
else
Minecraft.getMinecraft().playerController.pickItem(slot);
return true;
}
return false;
}
//Optifine Helper Functions u.u, these are here specifically for Optifine
//Note: When using Optifine, these methods are invoked using reflection, which
//incurs a major performance penalty.
public static void onLivingSetAttackTarget(EntityLivingBase entity, EntityLivingBase target)
{
MinecraftForge.EVENT_BUS.post(new LivingSetAttackTargetEvent(entity, target));
}
public static boolean onLivingUpdate(EntityLivingBase entity)
{
return MinecraftForge.EVENT_BUS.post(new LivingUpdateEvent(entity));
}
public static boolean onLivingAttack(EntityLivingBase entity, DamageSource src, float amount)
{
return !MinecraftForge.EVENT_BUS.post(new LivingAttackEvent(entity, src, amount));
}
public static float onLivingHurt(EntityLivingBase entity, DamageSource src, float amount)
{
LivingHurtEvent event = new LivingHurtEvent(entity, src, amount);
return (MinecraftForge.EVENT_BUS.post(event) ? 0 : event.getAmount());
}
public static boolean onLivingDeath(EntityLivingBase entity, DamageSource src)
{
return MinecraftForge.EVENT_BUS.post(new LivingDeathEvent(entity, src));
}
public static boolean onLivingDrops(EntityLivingBase entity, DamageSource source, ArrayList<EntityItem> drops, int lootingLevel, boolean recentlyHit)
{
return MinecraftForge.EVENT_BUS.post(new LivingDropsEvent(entity, source, drops, lootingLevel, recentlyHit));
}
public static float[] onLivingFall(EntityLivingBase entity, float distance, float damageMultiplier)
{
LivingFallEvent event = new LivingFallEvent(entity, distance, damageMultiplier);
return (MinecraftForge.EVENT_BUS.post(event) ? null : new float[]{event.getDistance(), event.getDamageMultiplier()});
}
public static boolean isLivingOnLadder(IBlockState state, World world, BlockPos pos, EntityLivingBase entity)
{
boolean isSpectator = (entity instanceof EntityPlayer && ((EntityPlayer)entity).isSpectator());
if (isSpectator) return false;
if (!ForgeModContainer.fullBoundingBoxLadders)
{
return state != null && state.getBlock().isLadder(state, world, pos, entity);
}
else
{
AxisAlignedBB bb = entity.getEntityBoundingBox();
int mX = MathHelper.floor_double(bb.minX);
int mY = MathHelper.floor_double(bb.minY);
int mZ = MathHelper.floor_double(bb.minZ);
for (int y2 = mY; y2 < bb.maxY; y2++)
{
for (int x2 = mX; x2 < bb.maxX; x2++)
{
for (int z2 = mZ; z2 < bb.maxZ; z2++)
{
BlockPos tmp = new BlockPos(x2, y2, z2);
state = world.getBlockState(tmp);
if (state.getBlock().isLadder(state, world, tmp, entity))
{
return true;
}
}
}
}
return false;
}
}
public static void onLivingJump(EntityLivingBase entity)
{
MinecraftForge.EVENT_BUS.post(new LivingJumpEvent(entity));
}
public static EntityItem onPlayerTossEvent(EntityPlayer player, ItemStack item, boolean includeName)
{
player.captureDrops = true;
EntityItem ret = player.dropItem(item, false, includeName);
player.capturedDrops.clear();
player.captureDrops = false;
if (ret == null)
{
return null;
}
ItemTossEvent event = new ItemTossEvent(ret, player);
if (MinecraftForge.EVENT_BUS.post(event))
{
return null;
}
if (!player.worldObj.isRemote)
{
player.getEntityWorld().spawnEntityInWorld(event.getEntityItem());
}
return event.getEntityItem();
}
public static float getEnchantPower(World world, BlockPos pos)
{
return world.getBlockState(pos).getBlock().getEnchantPowerBonus(world, pos);
}
public static ITextComponent onServerChatEvent(NetHandlerPlayServer net, String raw, ITextComponent comp)
{
ServerChatEvent event = new ServerChatEvent(net.playerEntity, raw, comp);
if (MinecraftForge.EVENT_BUS.post(event))
{
return null;
}
return event.getComponent();
}
static final Pattern URL_PATTERN = Pattern.compile(
// schema ipv4 OR namespace port path ends
// |-----------------| |-------------------------| |-------------------------| |---------| |--| |---------------|
"((?:[a-z0-9]{2,}:\\/\\/)?(?:(?:[0-9]{1,3}\\.){3}[0-9]{1,3}|(?:[-\\w_]{1,}\\.[a-z]{2,}?))(?::[0-9]{1,5})?.*?(?=[!\"\u00A7 \n]|$))",
Pattern.CASE_INSENSITIVE);
public static ITextComponent newChatWithLinks(String string){ return newChatWithLinks(string, true); }
public static ITextComponent newChatWithLinks(String string, boolean allowMissingHeader)
{
// Includes ipv4 and domain pattern
// Matches an ip (xx.xxx.xx.xxx) or a domain (something.com) with or
// without a protocol or path.
ITextComponent ichat = null;
Matcher matcher = URL_PATTERN.matcher(string);
int lastEnd = 0;
// Find all urls
while (matcher.find())
{
int start = matcher.start();
int end = matcher.end();
// Append the previous left overs.
String part = string.substring(lastEnd, start);
if (part.length() > 0)
{
if (ichat == null)
ichat = new TextComponentString(part);
else
ichat.appendText(part);
}
lastEnd = end;
String url = string.substring(start, end);
ITextComponent link = new TextComponentString(url);
try
{
// Add schema so client doesn't crash.
if ((new URI(url)).getScheme() == null)
{
if (!allowMissingHeader)
{
if (ichat == null)
ichat = new TextComponentString(url);
else
ichat.appendText(url);
continue;
}
url = "http://" + url;
}
}
catch (URISyntaxException e)
{
// Bad syntax bail out!
if (ichat == null) ichat = new TextComponentString(url);
else ichat.appendText(url);
continue;
}
// Set the click event and append the link.
ClickEvent click = new ClickEvent(ClickEvent.Action.OPEN_URL, url);
link.getStyle().setClickEvent(click);
link.getStyle().setUnderlined(true);
link.getStyle().setColor(TextFormatting.BLUE);
if (ichat == null)
ichat = link;
else
ichat.appendSibling(link);
}
// Append the rest of the message.
String end = string.substring(lastEnd);
if (ichat == null)
ichat = new TextComponentString(end);
else if (end.length() > 0)
ichat.appendText(string.substring(lastEnd));
return ichat;
}
public static boolean canInteractWith(EntityPlayer player, Container openContainer)
{
PlayerOpenContainerEvent event = new PlayerOpenContainerEvent(player, openContainer);
MinecraftForge.EVENT_BUS.post(event);
return event.getResult() == Event.Result.DEFAULT ? event.isCanInteractWith() : event.getResult() == Event.Result.ALLOW ? true : false;
}
public static int onBlockBreakEvent(World world, GameType gameType, EntityPlayerMP entityPlayer, BlockPos pos)
{
// Logic from tryHarvestBlock for pre-canceling the event
boolean preCancelEvent = false;
if (gameType.isCreative() && entityPlayer.getHeldItemMainhand() != null && entityPlayer.getHeldItemMainhand().getItem() instanceof ItemSword)
preCancelEvent = true;
if (gameType.isAdventure())
{
if (gameType == WorldSettings.GameType.SPECTATOR)
preCancelEvent = true;
if (!entityPlayer.isAllowEdit())
{
ItemStack itemstack = entityPlayer.getHeldItemMainhand();
if (itemstack == null || !itemstack.canDestroy(world.getBlockState(pos).getBlock()))
preCancelEvent = true;
}
}
// Tell client the block is gone immediately then process events
if (world.getTileEntity(pos) == null)
{
SPacketBlockChange packet = new SPacketBlockChange(world, pos);
packet.blockState = Blocks.AIR.getDefaultState();
entityPlayer.connection.sendPacket(packet);
}
// Post the block break event
IBlockState state = world.getBlockState(pos);
BlockEvent.BreakEvent event = new BlockEvent.BreakEvent(world, pos, state, entityPlayer);
event.setCanceled(preCancelEvent);
MinecraftForge.EVENT_BUS.post(event);
// Handle if the event is canceled
if (event.isCanceled())
{
// Let the client know the block still exists
entityPlayer.connection.sendPacket(new SPacketBlockChange(world, pos));
// Update any tile entity data for this block
TileEntity tileentity = world.getTileEntity(pos);
if (tileentity != null)
{
Packet<?> pkt = tileentity.getUpdatePacket();
if (pkt != null)
{
entityPlayer.connection.sendPacket(pkt);
}
}
}
return event.isCanceled() ? -1 : event.getExpToDrop();
}
public static EnumActionResult onPlaceItemIntoWorld(ItemStack itemstack, EntityPlayer player, World world, BlockPos pos, EnumFacing side, float hitX, float hitY, float hitZ, EnumHand hand)
{
// handle all placement events here
int meta = itemstack.getItemDamage();
int size = itemstack.stackSize;
NBTTagCompound nbt = null;
if (itemstack.getTagCompound() != null)
{
nbt = (NBTTagCompound)itemstack.getTagCompound().copy();
}
if (!(itemstack.getItem() instanceof ItemBucket)) // if not bucket
{
world.captureBlockSnapshots = true;
}
EnumActionResult ret = itemstack.getItem().onItemUse(itemstack, player, world, pos, hand, side, hitX, hitY, hitZ);
world.captureBlockSnapshots = false;
if (ret == EnumActionResult.SUCCESS)
{
// save new item data
int newMeta = itemstack.getItemDamage();
int newSize = itemstack.stackSize;
NBTTagCompound newNBT = null;
if (itemstack.getTagCompound() != null)
{
newNBT = (NBTTagCompound)itemstack.getTagCompound().copy();
}
net.minecraftforge.event.world.BlockEvent.PlaceEvent placeEvent = null;
@SuppressWarnings("unchecked")
List<net.minecraftforge.common.util.BlockSnapshot> blockSnapshots = (List<BlockSnapshot>)world.capturedBlockSnapshots.clone();
world.capturedBlockSnapshots.clear();
// make sure to set pre-placement item data for event
itemstack.setItemDamage(meta);
itemstack.stackSize = size;
if (nbt != null)
{
itemstack.setTagCompound(nbt);
}
if (blockSnapshots.size() > 1)
{
placeEvent = ForgeEventFactory.onPlayerMultiBlockPlace(player, blockSnapshots, side);
}
else if (blockSnapshots.size() == 1)
{
placeEvent = ForgeEventFactory.onPlayerBlockPlace(player, blockSnapshots.get(0), side);
}
if (placeEvent != null && (placeEvent.isCanceled()))
{
ret = EnumActionResult.FAIL; // cancel placement
// revert back all captured blocks
for (net.minecraftforge.common.util.BlockSnapshot blocksnapshot : blockSnapshots)
{
world.restoringBlockSnapshots = true;
blocksnapshot.restore(true, false);
world.restoringBlockSnapshots = false;
}
}
else
{
// Change the stack to its new content
itemstack.setItemDamage(newMeta);
itemstack.stackSize = newSize;
if (nbt != null)
{
itemstack.setTagCompound(newNBT);
}
for (BlockSnapshot snap : blockSnapshots)
{
int updateFlag = snap.getFlag();
IBlockState oldBlock = snap.getReplacedBlock();
IBlockState newBlock = world.getBlockState(snap.getPos());
if (newBlock != null && !(newBlock.getBlock().hasTileEntity(newBlock))) // Containers get placed automatically
{
newBlock.getBlock().onBlockAdded(world, snap.getPos(), newBlock);
}
world.markAndNotifyBlock(snap.getPos(), null, oldBlock, newBlock, updateFlag);
}
player.addStat(StatList.getCraftStats(itemstack.getItem()));
}
}
world.capturedBlockSnapshots.clear();
return ret;
}
public static boolean onAnvilChange(ContainerRepair container, ItemStack left, ItemStack right, IInventory outputSlot, String name, int baseCost)
{
AnvilUpdateEvent e = new AnvilUpdateEvent(left, right, name, baseCost);
if (MinecraftForge.EVENT_BUS.post(e)) return false;
if (e.getOutput() == null) return true;
outputSlot.setInventorySlotContents(0, e.getOutput());
container.maximumCost = e.getCost();
container.materialCost = e.getMaterialCost();
return false;
}
public static float onAnvilRepair(EntityPlayer player, ItemStack output, ItemStack left, ItemStack right)
{
AnvilRepairEvent e = new AnvilRepairEvent(player, left, right, output);
MinecraftForge.EVENT_BUS.post(e);
return e.getBreakChance();
}
public static boolean onNoteChange(TileEntityNote te, byte old)
{
NoteBlockEvent.Change e = new NoteBlockEvent.Change(te.getWorld(), te.getPos(), te.getWorld().getBlockState(te.getPos()), old, te.note);
if (MinecraftForge.EVENT_BUS.post(e))
{
te.note = old;
return false;
}
te.note = (byte)e.getVanillaNoteId();
return true;
}
/**
* Default implementation of IRecipe.func_179532_b {getRemainingItems} because
* this is just copy pasted over a lot of recipes.
*
* Another use case for java 8 but sadly we can't use it!
*
* @param inv Crafting inventory
* @return Crafting inventory contents after the recipe.
*/
public static ItemStack[] defaultRecipeGetRemainingItems(InventoryCrafting inv)
{
ItemStack[] ret = new ItemStack[inv.getSizeInventory()];
for (int i = 0; i < ret.length; i++)
{
ret[i] = getContainerItem(inv.getStackInSlot(i));
}
return ret;
}
private static ThreadLocal<EntityPlayer> craftingPlayer = new ThreadLocal<EntityPlayer>();
public static void setCraftingPlayer(EntityPlayer player)
{
craftingPlayer.set(player);
}
public static EntityPlayer getCraftingPlayer()
{
return craftingPlayer.get();
}
public static ItemStack getContainerItem(ItemStack stack)
{
if (stack == null) return null;
if (stack.getItem().hasContainerItem(stack))
{
stack = stack.getItem().getContainerItem(stack);
if (stack != null && stack.isItemStackDamageable() && stack.getMetadata() > stack.getMaxDamage())
{
ForgeEventFactory.onPlayerDestroyItem(craftingPlayer.get(), stack, null);
return null;
}
return stack;
}
return null;
}
public static boolean isInsideOfMaterial(Material material, Entity entity, BlockPos pos)
{
IBlockState state = entity.worldObj.getBlockState(pos);
Block block = state.getBlock();
double eyes = entity.posY + (double)entity.getEyeHeight();
double filled = 1.0f; //If it's not a liquid assume it's a solid block
if (block instanceof IFluidBlock)
{
filled = ((IFluidBlock)block).getFilledPercentage(entity.worldObj, pos);
}
else if (block instanceof BlockLiquid)
{
filled = BlockLiquid.getLiquidHeightPercent(block.getMetaFromState(state));
}
if (filled < 0)
{
filled *= -1;
//filled -= 0.11111111F; //Why this is needed.. not sure...
return eyes > pos.getY() + 1 + (1 - filled);
}
else
{
return eyes < pos.getY() + 1 + filled;
}
}
public static boolean onPlayerAttackTarget(EntityPlayer player, Entity target)
{
if (MinecraftForge.EVENT_BUS.post(new AttackEntityEvent(player, target))) return false;
ItemStack stack = player.getHeldItemMainhand();
if (stack != null && stack.getItem().onLeftClickEntity(stack, player, target)) return false;
return true;
}
public static boolean onTravelToDimension(Entity entity, int dimension)
{
EntityTravelToDimensionEvent event = new EntityTravelToDimensionEvent(entity, dimension);
MinecraftForge.EVENT_BUS.post(event);
if (event.isCanceled())
{
// Revert variable back to true as it would have been set to false
if (entity instanceof EntityMinecartContainer)
{
((EntityMinecartContainer) entity).dropContentsWhenDead = true;
}
}
return !event.isCanceled();
}
public static RayTraceResult rayTraceEyes(EntityLivingBase entity, double length)
{
Vec3d startPos = new Vec3d(entity.posX, entity.posY + entity.getEyeHeight(), entity.posZ);
Vec3d endPos = startPos.add(new Vec3d(entity.getLookVec().xCoord * length, entity.getLookVec().yCoord * length, entity.getLookVec().zCoord * length));
return entity.worldObj.rayTraceBlocks(startPos, endPos);
}
public static Vec3d rayTraceEyeHitVec(EntityLivingBase entity, double length)
{
RayTraceResult git = rayTraceEyes(entity, length);
return git == null ? null : git.hitVec;
}
public static boolean onInteractEntityAt(EntityPlayer player, Entity entity, RayTraceResult ray, ItemStack stack, EnumHand hand)
{
Vec3d vec3d = new Vec3d(ray.hitVec.xCoord - entity.posX, ray.hitVec.yCoord - entity.posY, ray.hitVec.zCoord - entity.posZ);
return onInteractEntityAt(player, entity, vec3d, stack, hand);
}
public static boolean onInteractEntityAt(EntityPlayer player, Entity entity, Vec3d vec3d, ItemStack stack, EnumHand hand)
{
return MinecraftForge.EVENT_BUS.post(new PlayerInteractEvent.EntityInteractSpecific(player, hand, stack, entity, vec3d));
}
public static boolean onInteractEntity(EntityPlayer player, Entity entity, ItemStack item, EnumHand hand)
{
return MinecraftForge.EVENT_BUS.post(new PlayerInteractEvent.EntityInteract(player, hand, item, entity));
}
public static boolean onItemRightClick(EntityPlayer player, EnumHand hand, ItemStack stack)
{
return MinecraftForge.EVENT_BUS.post(new PlayerInteractEvent.RightClickItem(player, hand, stack));
}
public static PlayerInteractEvent.LeftClickBlock onLeftClickBlock(EntityPlayer player, BlockPos pos, EnumFacing face, Vec3d hitVec)
{
PlayerInteractEvent.LeftClickBlock evt = new PlayerInteractEvent.LeftClickBlock(player, pos, face, hitVec);
MinecraftForge.EVENT_BUS.post(evt);
return evt;
}
public static PlayerInteractEvent.RightClickBlock onRightClickBlock(EntityPlayer player, EnumHand hand, ItemStack stack, BlockPos pos, EnumFacing face, Vec3d hitVec)
{
PlayerInteractEvent.RightClickBlock evt = new PlayerInteractEvent.RightClickBlock(player, hand, stack, pos, face, hitVec);
MinecraftForge.EVENT_BUS.post(evt);
return evt;
}
public static void onEmptyClick(EntityPlayer player, EnumHand hand)
{
MinecraftForge.EVENT_BUS.post(new PlayerInteractEvent.RightClickEmpty(player, hand));
}
private static ThreadLocal<Deque<LootTableContext>> lootContext = new ThreadLocal<Deque<LootTableContext>>();
private static LootTableContext getLootTableContext()
{
LootTableContext ctx = lootContext.get().peek();
if (ctx == null)
throw new JsonParseException("Invalid call stack, could to grab json context!"); // Show I throw this? Do we care about custom deserializers outside the manager?
return ctx;
}
public static LootTable loadLootTable(Gson gson, ResourceLocation name, String data, boolean custom)
{
Deque<LootTableContext> que = lootContext.get();
if (que == null)
{
que = Queues.newArrayDeque();
lootContext.set(que);
}
LootTable ret = null;
try
{
que.push(new LootTableContext(name, custom));
ret = gson.fromJson(data, LootTable.class);
que.pop();
}
catch (JsonParseException e)
{
que.pop();
throw e;
}
if (!custom)
ret = ForgeEventFactory.loadLootTable(name, ret);
if (ret != null)
ret.freeze();
return ret;
}
private static class LootTableContext
{
public final ResourceLocation name;
private final boolean vanilla;
public final boolean custom;
public int poolCount = 0;
public int entryCount = 0;
private HashSet<String> entryNames = Sets.newHashSet();
private LootTableContext(ResourceLocation name, boolean custom)
{
this.name = name;
this.custom = custom;
this.vanilla = "minecraft".equals(this.name.getResourceDomain());
}
private void resetPoolCtx()
{
this.entryCount = 0;
this.entryNames.clear();
}
public String validateEntryName(String name)
{
if (!this.entryNames.contains(name))
{
this.entryNames.add(name);
return name;
}
if (!this.vanilla)
throw new JsonParseException("Loot Table \"" + this.name.toString() + "\" Duplicate entry name \"" + name + "\" for pool #" + (this.poolCount - 1) + " entry #" + (this.entryCount-1));
int x = 0;
while (this.entryNames.contains(name + "#" + x))
x++;
name = name + "#" + x;
this.entryNames.add(name);
return name;
}
}
public static String readPoolName(JsonObject json)
{
LootTableContext ctx = ForgeHooks.getLootTableContext();
ctx.resetPoolCtx();
if (json.has("name"))
return JsonUtils.getString(json, "name");
if (ctx.custom)
return "custom#" + json.hashCode(); //We don't care about custom ones modders shouldn't be editing them!
ctx.poolCount++;
if (!ctx.vanilla)
throw new JsonParseException("Loot Table \"" + ctx.name.toString() + "\" Missing `name` entry for pool #" + (ctx.poolCount - 1));
return ctx.poolCount == 1 ? "main" : "pool" + (ctx.poolCount - 1);
}
public static String readLootEntryName(JsonObject json, String type)
{
LootTableContext ctx = ForgeHooks.getLootTableContext();
ctx.entryCount++;
if (json.has("entryName"))
return ctx.validateEntryName(JsonUtils.getString(json, "entryName"));
if (ctx.custom)
return "custom#" + json.hashCode(); //We don't care about custom ones modders shouldn't be editing them!
String name = null;
if ("item".equals(type))
name = JsonUtils.getString(json, "name");
else if ("loot_table".equals(type))
name = JsonUtils.getString(json, "name");
else if ("empty".equals(type))
name = "empty";
return ctx.validateEntryName(name);
}
//TODO: Some registry to support custom LootEntry types?
public static LootEntry deserializeJsonLootEntry(String type, JsonObject json, int weight, int quality, LootCondition[] conditions){ return null; }
public static String getLootEntryType(LootEntry entry){ return null; } //Companion to above function
}