Port capability patches to 1.13 and refactor to new system (#5134)

* resource package and misc leftover rendering stuff

* Port Item and ItemStack patches

* Extract most item patches to interface

* Refactor Item#setNoRepair to be part of Item.Builder

* Adapt AnimationItemOverrideList to new cap system

* Improve null contract of OptionalCapabilityInstance

* Remove Capability.cast

* Update CapabilityAnimation to new cap system

* Remove OptionalCapabilityInstance#flatMap and add NonNullConsumer

* Add capability patch to Entity, not everything yet

* Fix Entity patch and add missing methods to IForgeEntity

* Fix null contract of CapabilityDispatcher

* Add TileEntity caps

* Adapt FluidUtil to new cap system

* Add world caps

* Add chunk caps

* Clean up cap patches

* Replace all tabs with spaces

* Move builder methods on Item to Item.Builder

Whitepsace fix

* Sided stuff take 2

* Prevent OptionalCapabilityInstance creating additional empty instances

* Cache getCapability result in AnimationTESR

* Fix imports in TileEntity patch

* Pull out cap code to base class, add village caps
This commit is contained in:
tterrag authored and GitHub committed 2018-09-09 17:27:15 -04:00
1 parent f851b37aa0
commit 334d925972
45 files changed
+1967 -266

No files matched your search

@@ -41,6 +41,7 @@ import net.minecraft.client.renderer.vertex.VertexBuffer;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.entity.Entity;
import net.minecraft.resources.IReloadableResourceManager;
import net.minecraft.resources.IResourceManager;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
@@ -35,7 +35,8 @@ public class ForgeClientHandler
// register model for the universal bucket, if it exists
if (FluidRegistry.isUniversalBucketEnabled())
{
ModelLoader.setBucketModelDefinition(ForgeMod.getInstance().universalBucket);
// TODO no more mesh definitions, this should be implemented with overrides
// ModelLoader.setBucketModelDefinition(ForgeMod.getInstance().universalBucket);
}
}
@@ -44,7 +45,7 @@ public class ForgeClientHandler
{
if (FluidRegistry.isUniversalBucketEnabled())
{
event.getItemColors().registerItemColorHandler(new FluidContainerColorer(), ForgeMod.getInstance().universalBucket);
event.getItemColors().func_199877_a(new FluidContainerColorer(), ForgeMod.getInstance().universalBucket);
}
}
}
@@ -67,22 +67,23 @@ public final class AnimationItemOverrideList extends ItemOverrideList
@Override
public IBakedModel func_209581_a(IBakedModel originalModel, ItemStack stack, @Nullable World world, @Nullable EntityLivingBase entity)
{
IAnimationStateMachine asm = stack.getCapability(CapabilityAnimation.ANIMATION_CAPABILITY, null);
if (asm != null)
{
// TODO: caching?
if(world == null && entity != null)
return stack.getCapability(CapabilityAnimation.ANIMATION_CAPABILITY, null)
.map(asm ->
{
world = entity.world;
}
if(world == null)
{
world = Minecraft.getMinecraft().world;
}
IModelState state = asm.apply(Animation.getWorldTime(world, Animation.getPartialTickTime())).getLeft();
World w = world;
// TODO caching?
if(w == null && entity != null)
{
w = entity.world;
}
if(world == null)
{
w = Minecraft.getMinecraft().world;
}
return asm.apply(Animation.getWorldTime(world, Animation.getPartialTickTime())).getLeft();
})
// TODO where should uvlock data come from?
return model.bake(ModelLoader.defaultModelGetter(), bakedTextureGetter, new ModelStateComposition(state, this.state), false, format);
}
return super.func_209581_a(originalModel, stack, world, entity);
.map(state -> model.bake(ModelLoader.defaultModelGetter(), bakedTextureGetter, new ModelStateComposition(state, this.state), false, format))
.orElseGet(() -> super.func_209581_a(originalModel, stack, world, entity));
}
}
@@ -42,11 +42,8 @@ import net.minecraftforge.client.model.pipeline.VertexLighterFlat;
import net.minecraftforge.client.model.pipeline.VertexBufferConsumer;
import net.minecraftforge.common.animation.Event;
import net.minecraftforge.common.animation.IEventHandler;
import net.minecraftforge.common.model.IModelState;
import net.minecraftforge.common.model.animation.CapabilityAnimation;
import net.minecraftforge.common.model.animation.IAnimationStateMachine;
import org.apache.commons.lang3.tuple.Pair;
import org.lwjgl.opengl.GL11;
/**
@@ -69,17 +66,18 @@ public class AnimationModelBase<T extends Entity> extends ModelBase implements I
@Override
public void render(Entity entity, float limbSwing, float limbSwingSpeed, float timeAlive, float yawHead, float rotationPitch, float scale)
{
IAnimationStateMachine capability = entity.getCapability(CapabilityAnimation.ANIMATION_CAPABILITY, null);
if (capability == null)
{
return;
}
Pair<IModelState, Iterable<Event>> pair = capability.apply(timeAlive / 20);
handleEvents((T)entity, timeAlive / 20, pair.getRight());
IUnbakedModel model = ModelLoaderRegistry.getModelOrMissing(modelLocation);
// TODO where should uvlock data come from?
IBakedModel bakedModel = model.bake(ModelLoader.defaultModelGetter(), ModelLoader.defaultTextureGetter(), pair.getLeft(), false, DefaultVertexFormats.ITEM);
entity.getCapability(CapabilityAnimation.ANIMATION_CAPABILITY, null)
.map(cap -> cap.apply(timeAlive / 20))
.map(pair -> {
handleEvents((T) entity, timeAlive / 20, pair.getRight());
IUnbakedModel unbaked = ModelLoaderRegistry.getModelOrMissing(modelLocation);
// TODO where should uvlock data come from?
return unbaked.bake(ModelLoader.defaultModelGetter(), ModelLoader.defaultTextureGetter(), pair.getLeft(), false, DefaultVertexFormats.ITEM);
}).ifPresent(model -> drawModel(model, entity));
}
private void drawModel(IBakedModel bakedModel, Entity entity)
{
BlockPos pos = new BlockPos(entity.posX, entity.posY + entity.height, entity.posZ);
RenderHelper.disableStandardItemLighting();
@@ -109,7 +107,7 @@ public class AnimationModelBase<T extends Entity> extends ModelBase implements I
}
for(EnumFacing side : EnumFacing.values())
{
random.setSeed(42);
random.setSeed(42);
quads = bakedModel.func_200117_a(null, side, random);
if(!quads.isEmpty())
{
@@ -30,6 +30,7 @@ import net.minecraft.world.IWorldReader;
import net.minecraftforge.client.MinecraftForgeClient;
import net.minecraftforge.common.animation.Event;
import net.minecraftforge.common.animation.IEventHandler;
import net.minecraftforge.common.capabilities.OptionalCapabilityInstance;
import net.minecraftforge.common.model.IModelState;
import net.minecraftforge.common.model.animation.CapabilityAnimation;
import net.minecraftforge.common.model.animation.IAnimationStateMachine;
@@ -50,7 +51,8 @@ public class AnimationTESR<T extends TileEntity> extends FastTESR<T> implements
@Override
public void renderTileEntityFast(T te, double x, double y, double z, float partialTick, int breakStage, BufferBuilder renderer)
{
if(!te.hasCapability(CapabilityAnimation.ANIMATION_CAPABILITY, null))
OptionalCapabilityInstance<IAnimationStateMachine> cap = te.getCapability(CapabilityAnimation.ANIMATION_CAPABILITY);
if(!cap.isPresent())
{
return;
}
@@ -60,7 +62,7 @@ public class AnimationTESR<T extends TileEntity> extends FastTESR<T> implements
IBlockState state = world.getBlockState(pos);
if(state.getBlock().getBlockState().getProperties().contains(Properties.StaticProperty))
{
state = state.withProperty(Properties.StaticProperty, false);
state = state.func_206870_a(Properties.StaticProperty, false);
}
if(state instanceof IExtendedBlockState)
{
@@ -68,20 +70,19 @@ public class AnimationTESR<T extends TileEntity> extends FastTESR<T> implements
if(exState.getUnlistedNames().contains(Properties.AnimationProperty))
{
float time = Animation.getWorldTime(getWorld(), partialTick);
IAnimationStateMachine capability = te.getCapability(CapabilityAnimation.ANIMATION_CAPABILITY, null);
if (capability != null)
{
Pair<IModelState, Iterable<Event>> pair = capability.apply(time);
handleEvents(te, time, pair.getRight());
cap
.map(asm -> asm.apply(time))
.ifPresent(pair -> {
handleEvents(te, time, pair.getRight());
// TODO: caching?
IBakedModel model = blockRenderer.getBlockModelShapes().getModelForState(exState.getClean());
exState = exState.withProperty(Properties.AnimationProperty, pair.getLeft());
// TODO: caching?
IBakedModel model = blockRenderer.getBlockModelShapes().getModelForState(exState.getClean());
IExtendedBlockState animState = exState.withProperty(Properties.AnimationProperty, pair.getLeft());
renderer.setTranslation(x - pos.getX(), y - pos.getY(), z - pos.getZ());
renderer.setTranslation(x - pos.getX(), y - pos.getY(), z - pos.getZ());
blockRenderer.getBlockModelRenderer().func_199324_a(world, model, exState, pos, renderer, false, new Random(), 42);
}
blockRenderer.getBlockModelRenderer().func_199324_a(world, model, animState, pos, renderer, false, new Random(), 42);
});
}
}
}
@@ -19,13 +19,13 @@
package net.minecraftforge.client.resource;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
/**
* Represents a generic type of reloadable resource. Used for resource reload filtering.
*/
@SideOnly(Side.CLIENT)
@OnlyIn(Dist.CLIENT)
public interface IResourceType
{
}
@@ -22,15 +22,15 @@ package net.minecraftforge.client.resource;
import java.util.Set;
import java.util.function.Predicate;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import com.google.common.collect.Sets;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
/**
* Holds methods to create standard predicates to select {@link IResourceType}s that should be reloaded.
*/
@SideOnly(Side.CLIENT)
@OnlyIn(Dist.CLIENT)
public final class ReloadRequirements
{
/**
@@ -79,6 +79,7 @@ import net.minecraft.item.ItemPotion;
import net.minecraft.item.ItemSpade;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ItemTippedArrow;
import net.minecraft.item.ItemUseContext;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
@@ -867,8 +868,11 @@ public class ForgeHooks
return event.isCanceled() ? -1 : event.getExpToDrop();
}
public static EnumActionResult onPlaceItemIntoWorld(@Nonnull ItemStack itemstack, @Nonnull EntityPlayer player, @Nonnull World world, @Nonnull BlockPos pos, @Nonnull EnumFacing side, float hitX, float hitY, float hitZ, @Nonnull EnumHand hand)
public static EnumActionResult onPlaceItemIntoWorld(@Nonnull ItemUseContext context)
{
ItemStack itemstack = context.func_195996_i();
World world = context.func_195991_k();
// handle all placement events here
int meta = itemstack.getItemDamage();
int size = itemstack.getCount();
@@ -883,7 +887,7 @@ public class ForgeHooks
world.captureBlockSnapshots = true;
}
EnumActionResult ret = itemstack.getItem().onItemUse(player, world, pos, hand, side, hitX, hitY, hitZ);
EnumActionResult ret = itemstack.getItem().func_195939_a(context);
world.captureBlockSnapshots = false;
if (ret == EnumActionResult.SUCCESS)
@@ -902,12 +906,16 @@ public class ForgeHooks
world.capturedBlockSnapshots.clear();
// make sure to set pre-placement item data for event
itemstack.setItemDamage(meta);
itemstack.func_196085_b(meta);
itemstack.setCount(size);
if (nbt != null)
{
itemstack.setTagCompound(nbt);
}
EntityPlayer player = context.func_195999_j();
EnumFacing side = context.func_196000_l();
if (blockSnapshots.size() > 1)
{
placeEvent = ForgeEventFactory.onPlayerMultiBlockPlace(player, blockSnapshots, side, hand);
@@ -931,7 +939,7 @@ public class ForgeHooks
else
{
// Change the stack to its new content
itemstack.setItemDamage(newMeta);
itemstack.func_196085_b(newMeta);
itemstack.setCount(newSize);
if (nbt != null)
{
@@ -135,17 +135,6 @@ public class Capability<T>
}
}
/**
* Use this inside ICapabilityProvider.getCapability to avoid unchecked cast warnings.
* Example: return SOME_CAPABILITY.cast(instance);
* Use with caution;
*/
@SuppressWarnings("unchecked")
public <R> R cast(T instance)
{
return (R)instance;
}
// INTERNAL
private final String name;
private final IStorage<T> storage;
@@ -20,11 +20,14 @@
package net.minecraftforge.common.capabilities;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import java.util.List;
import java.util.Map;
import com.google.common.collect.Lists;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.nbt.INBTBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.EnumFacing;
@@ -41,6 +44,8 @@ import net.minecraftforge.common.util.INBTSerializable;
* Internally the handlers are baked into arrays for fast iteration.
* The ResourceLocations will be used for the NBT Key when serializing.
*/
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
public final class CapabilityDispatcher implements INBTSerializable<NBTTagCompound>, ICapabilityProvider
{
private ICapabilityProvider[] caps;
@@ -87,7 +92,7 @@ public final class CapabilityDispatcher implements INBTSerializable<NBTTagCompou
@Override
public <T> OptionalCapabilityInstance<T> getCapability(Capability<T> cap, EnumFacing side)
public <T> OptionalCapabilityInstance<T> getCapability(Capability<T> cap, @Nullable EnumFacing side)
{
for (ICapabilityProvider c : caps)
{
@@ -122,7 +127,7 @@ public final class CapabilityDispatcher implements INBTSerializable<NBTTagCompou
}
}
public boolean areCompatible(CapabilityDispatcher other) //Called from ItemStack to compare equality.
public boolean areCompatible(@Nullable CapabilityDispatcher other) //Called from ItemStack to compare equality.
{ // Only compares serializeable caps.
if (other == null) return this.writers.length == 0; // Done this way so we can do some pre-checks before doing the costly NBT serialization and compare
if (this.writers.length == 0) return other.writers.length == 0;
@@ -0,0 +1,100 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.common.capabilities;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.EnumFacing;
import net.minecraftforge.event.ForgeEventFactory;
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
public abstract class CapabilityProvider implements ICapabilityProvider
{
private @Nullable CapabilityDispatcher capabilities;
protected final void gatherCapabilities() { gatherCapabilities(null); }
protected final void gatherCapabilities(@Nullable ICapabilityProvider parent)
{
this.capabilities = ForgeEventFactory.gatherCapabilities(getClass(), this, parent);
}
protected final @Nullable CapabilityDispatcher getCapabilities()
{
return this.capabilities;
}
public final boolean areCapsCompatible(CapabilityProvider other)
{
return areCapsCompatible(other.getCapabilities());
}
public final boolean areCapsCompatible(@Nullable CapabilityDispatcher other)
{
final CapabilityDispatcher disp = getCapabilities();
if (disp == null)
{
if (other == null)
{
return true;
}
else
{
return other.areCompatible(null);
}
}
else
{
return disp.areCompatible(other);
}
}
protected final @Nullable NBTTagCompound serializeCaps()
{
final CapabilityDispatcher disp = getCapabilities();
if (disp != null)
{
return disp.serializeNBT();
}
return null;
}
protected final void deserializeCaps(NBTTagCompound tag)
{
final CapabilityDispatcher disp = getCapabilities();
if (disp != null)
{
disp.deserializeNBT(tag);
}
}
@Override
@Nonnull
public <T> OptionalCapabilityInstance<T> getCapability(@Nonnull Capability<T> cap, @Nullable EnumFacing side)
{
final CapabilityDispatcher disp = getCapabilities();
return disp == null ? OptionalCapabilityInstance.empty() : disp.getCapability(cap, side);
}
}
@@ -0,0 +1,29 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.common.capabilities;
import javax.annotation.Nonnull;
//Exactly like Consumer, except there IS a contract that the parameter must not be null.
@FunctionalInterface
public interface NonNullConsumer<T>
{
void accept(@Nonnull T t);
}
@@ -28,6 +28,14 @@ import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import mcp.MethodsReturnNonnullByDefault;
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
public class OptionalCapabilityInstance<T>
{
private final NonNullSupplier<T> supplier;
@@ -35,7 +43,7 @@ public class OptionalCapabilityInstance<T>
private Set<Consumer<OptionalCapabilityInstance<T>>> listeners = new HashSet<>();
private boolean isValid = true;
private static final OptionalCapabilityInstance<Void> EMPTY = new OptionalCapabilityInstance<>(null);
private static final @Nonnull OptionalCapabilityInstance<Void> EMPTY = new OptionalCapabilityInstance<>(null);
@SuppressWarnings("unchecked")
public static <T> OptionalCapabilityInstance<T> empty()
@@ -49,17 +57,17 @@ public class OptionalCapabilityInstance<T>
return (OptionalCapabilityInstance<X>)this;
}
private OptionalCapabilityInstance(NonNullSupplier<T> instanceSupplier)
private OptionalCapabilityInstance(@Nullable NonNullSupplier<T> instanceSupplier)
{
this.supplier = instanceSupplier;
}
public static <T> OptionalCapabilityInstance<T> of(final NonNullSupplier<T> instanceSupplier)
public static <T> OptionalCapabilityInstance<T> of(final @Nullable NonNullSupplier<T> instanceSupplier)
{
return new OptionalCapabilityInstance<>(instanceSupplier);
return instanceSupplier == null ? EMPTY.cast() : new OptionalCapabilityInstance<>(instanceSupplier);
}
private T getValue()
private @Nullable T getValue()
{
if (!isValid)
return null;
@@ -103,10 +111,11 @@ public class OptionalCapabilityInstance<T>
* @throws NullPointerException if mod object is present and {@code consumer} is
* null
*/
public void ifPresent(Consumer<? super T> consumer)
public void ifPresent(NonNullConsumer<? super T> consumer)
{
if (isValid && getValue() != null)
consumer.accept(getValue());
T val = getValue();
if (isValid && val != null)
consumer.accept(val);
}
/**
@@ -148,30 +157,6 @@ public class OptionalCapabilityInstance<T>
return isPresent() ? OptionalCapabilityInstance.of(()->mapper.apply(getValue())) : empty();
}
/**
* If a value is present, apply the provided {@code Optional}-bearing
* mapping function to it, return that result, otherwise return an empty
* {@code Optional}. This method is similar to {@link #map(Function)},
* but the provided mapper is one whose result is already an {@code Optional},
* and if invoked, {@code flatMap} does not wrap it with an additional
* {@code Optional}.
*
* @param <U> The type parameter to the {@code Optional} returned by
* @param mapper a mapping function to apply to the mod object, if present
* the mapping function
* @return the result of applying an {@code Optional}-bearing mapping
* function to the value of this {@code Optional}, if a value is present,
* otherwise an empty {@code Optional}
* @throws NullPointerException if the mapping function is null or returns
* a null result
*/
public<U> OptionalCapabilityInstance<U> flatMap(Function<? super T, Optional<U>> mapper)
{//I am not sure this is valid, or how to handle this, it's just a copy pasta from Optional. I dont think its needed. Returning a null supplier is bad
Objects.requireNonNull(mapper);
final U value = map(mapper).orElse(Optional.empty()).orElse(null); // To keep the non-null contract we have to evaluate right now. Should we allow this function at all?
return value != null ? OptionalCapabilityInstance.of(() -> value) : OptionalCapabilityInstance.empty();
}
/**
* Return the mod object if present, otherwise return {@code other}.
*
@@ -181,7 +166,8 @@ public class OptionalCapabilityInstance<T>
*/
public T orElse(T other)
{
return getValue() != null ? getValue() : other;
T val = getValue();
return val != null ? val : other;
}
/**
@@ -194,9 +180,10 @@ public class OptionalCapabilityInstance<T>
* @throws NullPointerException if mod object is not present and {@code other} is
* null
*/
public T orElseGet(Supplier<? extends T> other)
public T orElseGet(NonNullSupplier<? extends T> other)
{
return getValue() != null ? getValue() : other.get();
T val = getValue();
return val != null ? val : other.get();
}
/**
@@ -217,8 +204,9 @@ public class OptionalCapabilityInstance<T>
*/
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X
{
if (getValue() != null)
return getValue();
T val = getValue();
if (val != null)
return val;
throw exceptionSupplier.get();
}
@@ -0,0 +1,8 @@
package net.minecraftforge.common.extensions;
import net.minecraftforge.common.capabilities.ICapabilityProvider;
public interface IForgeChunk extends ICapabilityProvider
{
}
@@ -0,0 +1,21 @@
package net.minecraftforge.common.extensions;
public interface IForgeDimension
{
/**
* Called from {@link World#initCapabilities()}, to gather capabilities for this
* world. It's safe to access world here since this is called after world is
* registered.
*
* On server, called directly after mapStorage and world data such as Scoreboard
* and VillageCollection are initialized. On client, called when world is
* constructed, just before world load event is called. Note that this method is
* always called before the world load event.
*
* @return initial holder for capabilities on the world
*/
default net.minecraftforge.common.capabilities.ICapabilityProvider initCapabilities()
{
return null;
}
}
@@ -0,0 +1,26 @@
package net.minecraftforge.common.extensions;
import net.minecraft.entity.Entity;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraftforge.common.capabilities.ICapabilitySerializable;
public interface IForgeEntity extends ICapabilitySerializable<NBTTagCompound>
{
default Entity getEntity() { return (Entity) this; }
default void deserializeNBT(NBTTagCompound nbt)
{
getEntity().readFromNBT(nbt);
}
default NBTTagCompound serializeNBT()
{
NBTTagCompound ret = new NBTTagCompound();
String id = getEntity().getEntityString();
if (id != null)
{
ret.setString("id", getEntity().getEntityString());
}
return getEntity().writeToNBT(ret);
}
}
@@ -0,0 +1,758 @@
package net.minecraftforge.common.extensions;
import javax.annotation.Nullable;
import com.google.common.collect.Multimap;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.ai.attributes.AttributeModifier;
import net.minecraft.entity.passive.EntityHorse;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.init.Items;
import net.minecraft.inventory.EntityEquipmentSlot;
import net.minecraft.item.IItemPropertyGetter;
import net.minecraft.item.Item;
import net.minecraft.item.ItemAxe;
import net.minecraft.item.ItemGroup;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ItemSword;
import net.minecraft.item.ItemUseContext;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.EnumActionResult;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.registry.RegistrySimple;
import net.minecraft.world.GameType;
import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
// TODO review most of the methods in this "patch"
public interface IForgeItem
{
// Helpers for accessing Item data
default Item getItem()
{
return (Item) this;
}
/**
* ItemStack sensitive version of getItemAttributeModifiers
*/
default Multimap<String, AttributeModifier> getAttributeModifiers(EntityEquipmentSlot slot, ItemStack stack)
{
return getItem().getItemAttributeModifiers(slot);
}
/**
* Called when a player drops the item into the world, returning false from this
* will prevent the item from being removed from the players inventory and
* spawning in the world
*
* @param player The player that dropped the item
* @param item The item stack, before the item is removed.
*/
default boolean onDroppedByPlayer(ItemStack item, EntityPlayer player)
{
return true;
}
/**
* Allow the item one last chance to modify its name used for the tool highlight
* useful for adding something extra that can't be removed by a user in the
* displayed name, such as a mode of operation.
*
* @param item the ItemStack for the item.
* @param displayName the name that will be displayed unless it is changed in
* this method.
*/
default String getHighlightTip(ItemStack item, String displayName)
{
return displayName;
}
/**
* This is called when the item is used, before the block is activated.
*
* @return Return PASS to allow vanilla handling, any other to skip normal code.
*/
default EnumActionResult onItemUseFirst(ItemUseContext context)
{
return EnumActionResult.PASS;
}
/**
* Called by CraftingManager to determine if an item is reparable.
*
* @return True if reparable
*/
boolean isRepairable();
/**
* Override this method to change the NBT data being sent to the client. You
* should ONLY override this when you have no other choice, as this might change
* behavior client side!
*
* Note that this will sometimes be applied multiple times, the following MUST
* be supported: Item item = stack.getItem(); NBTTagCompound nbtShare1 =
* item.getNBTShareTag(stack); stack.setTagCompound(nbtShare1); NBTTagCompound
* nbtShare2 = item.getNBTShareTag(stack); assert nbtShare1.equals(nbtShare2);
*
* @param stack The stack to send the NBT tag for
* @return The NBT tag
*/
@Nullable
default NBTTagCompound getNBTShareTag(ItemStack stack)
{
return stack.getTagCompound();
}
/**
* Override this method to decide what to do with the NBT data received from
* getNBTShareTag().
*
* @param stack The stack that received NBT
* @param nbt Received NBT, can be null
*/
default void readNBTShareTag(ItemStack stack, @Nullable NBTTagCompound nbt)
{
stack.setTagCompound(nbt);
}
/**
* Called before a block is broken. Return true to prevent default block
* harvesting.
*
* Note: In SMP, this is called on both client and server sides!
*
* @param itemstack The current ItemStack
* @param pos Block's position in world
* @param player The Player that is wielding the item
* @return True to prevent harvesting, false to continue as normal
*/
default boolean onBlockStartBreak(ItemStack itemstack, BlockPos pos, EntityPlayer player)
{
return false;
}
/**
* Called each tick while using an item.
*
* @param stack The Item being used
* @param player The Player using the item
* @param count The amount of time in tick the item has been used for
* continuously
*/
default void onUsingTick(ItemStack stack, EntityLivingBase player, int count)
{
}
/**
* Called when the player Left Clicks (attacks) an entity. Processed before
* damage is done, if return value is true further processing is canceled and
* the entity is not attacked.
*
* @param stack The Item being used
* @param player The player that is attacking
* @param entity The entity being attacked
* @return True to cancel the rest of the interaction.
*/
default boolean onLeftClickEntity(ItemStack stack, EntityPlayer player, Entity entity)
{
return false;
}
/**
* ItemStack sensitive version of getContainerItem. Returns a full ItemStack
* instance of the result.
*
* @param itemStack The current ItemStack
* @return The resulting ItemStack
*/
default ItemStack getContainerItem(ItemStack itemStack)
{
if (!hasContainerItem(itemStack))
{
return ItemStack.EMPTY;
}
return new ItemStack(getItem().getContainerItem());
}
/**
* ItemStack sensitive version of hasContainerItem
*
* @param stack The current item stack
* @return True if this item has a 'container'
*/
default boolean hasContainerItem(ItemStack stack)
{
return getItem().hasContainerItem();
}
/**
* Retrieves the normal 'lifespan' of this item when it is dropped on the ground
* as a EntityItem. This is in ticks, standard result is 6000, or 5 mins.
*
* @param itemStack The current ItemStack
* @param world The world the entity is in
* @return The normal lifespan in ticks.
*/
default int getEntityLifespan(ItemStack itemStack, World world)
{
return 6000;
}
/**
* Determines if this Item has a special entity for when they are in the world.
* Is called when a EntityItem is spawned in the world, if true and
* Item#createCustomEntity returns non null, the EntityItem will be destroyed
* and the new Entity will be added to the world.
*
* @param stack The current item stack
* @return True of the item has a custom entity, If true,
* Item#createCustomEntity will be called
*/
default boolean hasCustomEntity(ItemStack stack)
{
return false;
}
/**
* This function should return a new entity to replace the dropped item.
* Returning null here will not kill the EntityItem and will leave it to
* function normally. Called when the item it placed in a world.
*
* @param world The world object
* @param location The EntityItem object, useful for getting the position of
* the entity
* @param itemstack The current item stack
* @return A new Entity object to spawn or null
*/
@Nullable
default Entity createEntity(World world, Entity location, ItemStack itemstack)
{
return null;
}
/**
* Called by the default implemetation of EntityItem's onUpdate method, allowing
* for cleaner control over the update of the item without having to write a
* subclass.
*
* @param entityItem The entity Item
* @return Return true to skip any further update code.
*/
default boolean onEntityItemUpdate(net.minecraft.entity.item.EntityItem entityItem)
{
return false;
}
/**
* Gets a list of tabs that items belonging to this class can display on,
* combined properly with getSubItems allows for a single item to span many
* sub-items across many tabs.
*
* @return A list of all tabs that this item could possibly be one.
*/
default java.util.Collection<ItemGroup> getCreativeTabs()
{
return java.util.Collections.singletonList(getItem().getCreativeTab());
}
/**
* Determines the base experience for a player when they remove this item from a
* furnace slot. This number must be between 0 and 1 for it to be valid. This
* number will be multiplied by the stack size to get the total experience.
*
* @param item The item stack the player is picking up.
* @return The amount to award for each item.
*/
default float getSmeltingExperience(ItemStack item)
{
return -1; // -1 will default to the old lookups.
}
/**
*
* Should this item, when held, allow sneak-clicks to pass through to the
* underlying block?
*
* @param world The world
* @param pos Block position in world
* @param player The Player that is wielding the item
* @return
*/
default boolean doesSneakBypassUse(ItemStack stack, net.minecraft.world.IWorldReader world, BlockPos pos, EntityPlayer player)
{
return false;
}
/**
* Called to tick armor in the armor slot. Override to do something
*/
default void onArmorTick(World world, EntityPlayer player, ItemStack itemStack)
{
}
/**
* Determines if the specific ItemStack can be placed in the specified armor
* slot, for the entity.
*
* TODO: Change name to canEquip in 1.13?
*
* @param stack The ItemStack
* @param armorType Armor slot to be verified.
* @param entity The entity trying to equip the armor
* @return True if the given ItemStack can be inserted in the slot
*/
default boolean isValidArmor(ItemStack stack, EntityEquipmentSlot armorType, Entity entity)
{
return net.minecraft.entity.EntityLiving.getSlotForItemStack(stack) == armorType;
}
/**
* Override this to set a non-default armor slot for an ItemStack, but <em>do
* not use this to get the armor slot of said stack; for that, use
* {@link net.minecraft.entity.EntityLiving#getSlotForItemStack(ItemStack)}.</em>
*
* @param stack the ItemStack
* @return the armor slot of the ItemStack, or {@code null} to let the default
* vanilla logic as per {@code EntityLiving.getSlotForItemStack(stack)}
* decide
*/
@Nullable
default EntityEquipmentSlot getEquipmentSlot(ItemStack stack)
{
return null;
}
/**
* Allow or forbid the specific book/item combination as an anvil enchant
*
* @param stack The item
* @param book The book
* @return if the enchantment is allowed
*/
default boolean isBookEnchantable(ItemStack stack, ItemStack book)
{
return true;
}
/**
* Called by RenderBiped and RenderPlayer to determine the armor texture that
* should be use for the currently equipped item. This will only be called on
* instances of ItemArmor.
*
* Returning null from this function will use the default value.
*
* @param stack ItemStack for the equipped armor
* @param entity The entity wearing the armor
* @param slot The slot the armor is in
* @param type The subtype, can be null or "overlay"
* @return Path of texture to bind, or null to use default
*/
@Nullable
default String getArmorTexture(ItemStack stack, Entity entity, EntityEquipmentSlot slot, String type)
{
return null;
}
/**
* Returns the font renderer used to render tooltips and overlays for this item.
* Returning null will use the standard font renderer.
*
* @param stack The current item stack
* @return A instance of FontRenderer or null to use default
*/
@OnlyIn(Dist.CLIENT)
@Nullable
default net.minecraft.client.gui.FontRenderer getFontRenderer(ItemStack stack)
{
return null;
}
/**
* Override this method to have an item handle its own armor rendering.
*
* @param entityLiving The entity wearing the armor
* @param itemStack The itemStack to render the model of
* @param armorSlot The slot the armor is in
* @param _default Original armor model. Will have attributes set.
* @return A ModelBiped to render instead of the default
*/
@OnlyIn(Dist.CLIENT)
@Nullable
default net.minecraft.client.renderer.entity.model.ModelBiped getArmorModel(EntityLivingBase entityLiving, ItemStack itemStack,
EntityEquipmentSlot armorSlot, net.minecraft.client.renderer.entity.model.ModelBiped _default)
{
return null;
}
/**
* Called when a entity tries to play the 'swing' animation.
*
* @param entityLiving The entity swinging the item.
* @param stack The Item stack
* @return True to cancel any further processing by EntityLiving
*/
default boolean onEntitySwing(EntityLivingBase entityLiving, ItemStack stack)
{
return false;
}
/**
* Called when the client starts rendering the HUD, for whatever item the player
* currently has as a helmet. This is where pumpkins would render there overlay.
*
* @param stack The ItemStack that is equipped
* @param player Reference to the current client entity
* @param resolution Resolution information about the current viewport and
* configured GUI Scale
* @param partialTicks Partial ticks for the renderer, useful for interpolation
*/
@OnlyIn(Dist.CLIENT)
default void renderHelmetOverlay(ItemStack stack, EntityPlayer player, int width, int height, float partialTicks)
{
}
/**
* Return the itemDamage represented by this ItemStack. Defaults to the Damage
* entry in the stack NBT, but can be overridden here for other sources.
*
* @param stack The itemstack that is damaged
* @return the damage value
*/
default int getDamage(ItemStack stack)
{
return !stack.hasTagCompound() ? 0 : stack.getTagCompound().getInteger("Damage");
}
/**
* Determines if the durability bar should be rendered for this item. Defaults
* to vanilla stack.isDamaged behavior. But modders can use this for any data
* they wish.
*
* @param stack The current Item Stack
* @return True if it should render the 'durability' bar.
*/
default boolean showDurabilityBar(ItemStack stack)
{
return stack.isItemDamaged();
}
/**
* Queries the percentage of the 'Durability' bar that should be drawn.
*
* @param stack The current ItemStack
* @return 0.0 for 100% (no damage / full bar), 1.0 for 0% (fully damaged /
* empty bar)
*/
default double getDurabilityForDisplay(ItemStack stack)
{
return (double) stack.getItemDamage() / (double) stack.getMaxDamage();
}
/**
* Returns the packed int RGB value used to render the durability bar in the
* GUI. Defaults to a value based on the hue scaled based on
* {@link #getDurabilityForDisplay}, but can be overriden.
*
* @param stack Stack to get durability from
* @return A packed RGB value for the durability colour (0x00RRGGBB)
*/
default int getRGBDurabilityForDisplay(ItemStack stack)
{
return MathHelper.hsvToRGB(Math.max(0.0F, (float) (1.0F - getDurabilityForDisplay(stack))) / 3.0F, 1.0F, 1.0F);
}
/**
* Return the maxDamage for this ItemStack. Defaults to the maxDamage field in
* this item, but can be overridden here for other sources such as NBT.
*
* @param stack The itemstack that is damaged
* @return the damage value
*/
default int getMaxDamage(ItemStack stack)
{
return getItem().getMaxDamage();
}
/**
* Return if this itemstack is damaged. Note only called if
* {@link #isDamageable()} is true.
*
* @param stack the stack
* @return if the stack is damaged
*/
default boolean isDamaged(ItemStack stack)
{
return stack.getItemDamage() > 0;
}
/**
* Set the damage for this itemstack. Note, this method is responsible for zero
* checking.
*
* @param stack the stack
* @param damage the new damage value
*/
default void setDamage(ItemStack stack, int damage)
{
stack.func_196082_o().setInteger("Damage", Math.max(0, damage));
}
/**
* Checked from
* {@link net.minecraft.client.multiplayer.PlayerControllerMP#onPlayerDestroyBlock(BlockPos pos)
* PlayerControllerMP.onPlayerDestroyBlock()} when a creative player left-clicks
* a block with this item. Also checked from
* {@link net.minecraftforge.common.ForgeHooks#onBlockBreakEvent(World, GameType, EntityPlayerMP, BlockPos)
* ForgeHooks.onBlockBreakEvent()} to prevent sending an event.
*
* @return true if the given player can destroy specified block in creative mode
* with this item
*/
default boolean canDestroyBlockInCreative(World world, BlockPos pos, ItemStack stack, EntityPlayer player)
{
return !(this instanceof ItemSword);
}
/**
* ItemStack sensitive version of {@link #canHarvestBlock(IBlockState)}
*
* @param state The block trying to harvest
* @param stack The itemstack used to harvest the block
* @return true if can harvest the block
*/
default boolean canHarvestBlock(IBlockState state, ItemStack stack)
{
return getItem().canHarvestBlock(state);
}
/**
* Gets the maximum number of items that this stack should be able to hold. This
* is a ItemStack (and thus NBT) sensitive version of Item.getItemStackLimit()
*
* @param stack The ItemStack
* @return The maximum number this item can be stacked to
*/
default int getItemStackLimit(ItemStack stack)
{
return getItem().getItemStackLimit();
}
java.util.Set<String> getToolClasses(ItemStack stack);
/**
* Queries the harvest level of this item stack for the specified tool class,
* Returns -1 if this tool is not of the specified type
*
* @param stack This item stack instance
* @param toolClass Tool Class
* @param player The player trying to harvest the given blockstate
* @param blockState The block to harvest
* @return Harvest level, or -1 if not the specified tool type.
*/
int getHarvestLevel(ItemStack stack, String toolClass, @Nullable EntityPlayer player, @Nullable IBlockState blockState);
/**
* ItemStack sensitive version of getItemEnchantability
*
* @param stack The ItemStack
* @return the item echantability value
*/
default int getItemEnchantability(ItemStack stack)
{
return getItem().getItemEnchantability();
}
/**
* Checks whether an item can be enchanted with a certain enchantment. This
* applies specifically to enchanting an item in the enchanting table and is
* called when retrieving the list of possible enchantments for an item.
* Enchantments may additionally (or exclusively) be doing their own checks in
* {@link net.minecraft.enchantment.Enchantment#canApplyAtEnchantingTable(ItemStack)};
* check the individual implementation for reference. By default this will check
* if the enchantment type is valid for this item type.
*
* @param stack the item stack to be enchanted
* @param enchantment the enchantment to be applied
* @return true if the enchantment can be applied to this item
*/
default boolean canApplyAtEnchantingTable(ItemStack stack, net.minecraft.enchantment.Enchantment enchantment)
{
return enchantment.type.canEnchantItem(stack.getItem());
}
/**
* Whether this Item can be used as a payment to activate the vanilla beacon.
*
* @param stack the ItemStack
* @return true if this Item can be used
*/
default boolean isBeaconPayment(ItemStack stack)
{
return this == Items.EMERALD || this == Items.DIAMOND || this == Items.GOLD_INGOT || this == Items.IRON_INGOT;
}
/**
* Determine if the player switching between these two item stacks
*
* @param oldStack The old stack that was equipped
* @param newStack The new stack
* @param slotChanged If the current equipped slot was changed, Vanilla does not
* play the animation if you switch between two slots that
* hold the exact same item.
* @return True to play the item change animation
*/
default boolean shouldCauseReequipAnimation(ItemStack oldStack, ItemStack newStack, boolean slotChanged)
{
return !oldStack.equals(newStack); // !ItemStack.areItemStacksEqual(oldStack, newStack);
}
/**
* Called when the player is mining a block and the item in his hand changes.
* Allows to not reset blockbreaking if only NBT or similar changes.
*
* @param oldStack The old stack that was used for mining. Item in players main
* hand
* @param newStack The new stack
* @return True to reset block break progress
*/
default boolean shouldCauseBlockBreakReset(ItemStack oldStack, ItemStack newStack)
{
return !(newStack.getItem() == oldStack.getItem() && ItemStack.areItemStackTagsEqual(newStack, oldStack)
&& (newStack.isItemStackDamageable() || newStack.getItemDamage() == oldStack.getItemDamage()));
}
/**
* Called to get the Mod ID of the mod that *created* the ItemStack, instead of
* the real Mod ID that *registered* it.
*
* For example the Forge Universal Bucket creates a subitem for each modded
* fluid, and it returns the modded fluid's Mod ID here.
*
* Mods that register subitems for other mods can override this. Informational
* mods can call it to show the mod that created the item.
*
* @param itemStack the ItemStack to check
* @return the Mod ID for the ItemStack, or null when there is no specially
* associated mod and {@link #getRegistryName()} would return null.
*/
@Nullable
default String getCreatorModId(ItemStack itemStack)
{
return net.minecraftforge.common.ForgeHooks.getDefaultCreatorModId(itemStack);
}
/**
* Called from ItemStack.setItem, will hold extra data for the life of this
* ItemStack. Can be retrieved from stack.getCapabilities() The NBT can be null
* if this is not called from readNBT or if the item the stack is changing FROM
* is different then this item, or the previous item had no capabilities.
*
* This is called BEFORE the stacks item is set so you can use stack.getItem()
* to see the OLD item. Remember that getItem CAN return null.
*
* @param stack The ItemStack
* @param nbt NBT of this item serialized, or null.
* @return A holder instance associated with this ItemStack where you can hold
* capabilities for the life of this item.
*/
@Nullable
default net.minecraftforge.common.capabilities.ICapabilityProvider initCapabilities(ItemStack stack, @Nullable NBTTagCompound nbt)
{
return null;
}
default com.google.common.collect.ImmutableMap<String, net.minecraftforge.common.animation.ITimeValue> getAnimationParameters(final ItemStack stack,
final World world, final EntityLivingBase entity)
{
com.google.common.collect.ImmutableMap.Builder<String, net.minecraftforge.common.animation.ITimeValue> builder = com.google.common.collect.ImmutableMap
.builder();
for (ResourceLocation location : ((RegistrySimple<ResourceLocation, IItemPropertyGetter>) getItem().properties).getKeys())
{
final IItemPropertyGetter parameter = getItem().properties.getObject(location);
builder.put(location.toString(), input -> parameter.call(stack, world, entity));
}
return builder.build();
}
/**
* Can this Item disable a shield
*
* @param stack The ItemStack
* @param shield The shield in question
* @param entity The EntityLivingBase holding the shield
* @param attacker The EntityLivingBase holding the ItemStack
* @retrun True if this ItemStack can disable the shield in question.
*/
default boolean canDisableShield(ItemStack stack, ItemStack shield, EntityLivingBase entity, EntityLivingBase attacker)
{
return this instanceof ItemAxe;
}
/**
* Is this Item a shield
*
* @param stack The ItemStack
* @param entity The Entity holding the ItemStack
* @return True if the ItemStack is considered a shield
*/
default boolean isShield(ItemStack stack, @Nullable EntityLivingBase entity)
{
return stack.getItem() == Items.SHIELD;
}
/**
* @return the fuel burn time for this itemStack in a furnace. Return 0 to make
* it not act as a fuel. Return -1 to let the default vanilla logic
* decide.
*/
default int getItemBurnTime(ItemStack itemStack)
{
return -1;
}
/**
* Returns an enum constant of type {@code HorseArmorType}. The returned enum
* constant will be used to determine the armor value and texture of this item
* when equipped.
*
* @param stack the armor stack
* @return an enum constant of type {@code HorseArmorType}. Return
* HorseArmorType.NONE if this is not horse armor
*/
default net.minecraft.entity.passive.HorseArmorType getHorseArmorType(ItemStack stack)
{
return net.minecraft.entity.passive.HorseArmorType.getByItem(stack.getItem());
}
default String getHorseArmorTexture(net.minecraft.entity.EntityLiving wearer, ItemStack stack)
{
return getHorseArmorType(stack).getTextureName();
}
/**
* Called every tick from {@link EntityHorse#onUpdate()} on the item in the
* armor slot.
*
* @param world the world the horse is in
* @param horse the horse wearing this armor
* @param armor the armor itemstack
*/
default void onHorseArmorTick(World world, net.minecraft.entity.EntityLiving horse, ItemStack armor)
{
}
/**
* @return This Item's renderer, or the default instance if it does not have
* one.
*/
@OnlyIn(Dist.CLIENT)
net.minecraft.client.renderer.tileentity.TileEntityItemStackRenderer getTileEntityItemStackRenderer();
}
@@ -0,0 +1,24 @@
package net.minecraftforge.common.extensions;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity;
import net.minecraftforge.common.capabilities.ICapabilitySerializable;
public interface IForgeTileEntity extends ICapabilitySerializable<NBTTagCompound>
{
default TileEntity getTileEntity() { return (TileEntity) this; }
@Override
default void deserializeNBT(NBTTagCompound nbt)
{
getTileEntity().readFromNBT(nbt);
}
@Override
default NBTTagCompound serializeNBT()
{
NBTTagCompound ret = new NBTTagCompound();
getTileEntity().writeToNBT(ret);
return ret;
}
}
@@ -0,0 +1,27 @@
package net.minecraftforge.common.extensions;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.village.Village;
import net.minecraftforge.common.capabilities.ICapabilitySerializable;
public interface IForgeVillage extends ICapabilitySerializable<NBTTagCompound>
{
default Village getVillage()
{
return (Village) this;
}
@Override
default void deserializeNBT(NBTTagCompound nbt)
{
getVillage().readVillageDataFromNBT(nbt);
}
@Override
default NBTTagCompound serializeNBT()
{
NBTTagCompound ret = new NBTTagCompound();
getVillage().writeVillageDataToNBT(ret);
return ret;
}
}
@@ -0,0 +1,8 @@
package net.minecraftforge.common.extensions;
import net.minecraftforge.common.capabilities.ICapabilityProvider;
public interface IForgeWorld extends ICapabilityProvider
{
}
@@ -60,14 +60,14 @@ public class CapabilityAnimation
}
@Override
@Nullable
@Nonnull
public <T> OptionalCapabilityInstance<T> getCapability(@Nonnull Capability<T> capability, @Nullable EnumFacing facing)
{
if(capability == ANIMATION_CAPABILITY)
{
return ANIMATION_CAPABILITY.cast(asm);
return OptionalCapabilityInstance.of(() -> asm).cast();
}
return null;
return OptionalCapabilityInstance.empty();
}
}
}
@@ -19,8 +19,8 @@
package net.minecraftforge.common.property;
import net.minecraft.block.properties.IProperty;
import net.minecraft.block.properties.PropertyBool;
import net.minecraft.state.BooleanProperty;
import net.minecraft.state.IProperty;
import net.minecraftforge.common.model.IModelState;
public class Properties
@@ -28,7 +28,7 @@ public class Properties
/**
* Property indicating if the model should be rendered in the static renderer or in the TESR. AnimationTESR sets it to false.
*/
public static final PropertyBool StaticProperty = PropertyBool.create("static");
public static final BooleanProperty StaticProperty = BooleanProperty.create("static");
/**
* Property holding the IModelState used for animating the model in the TESR.
@@ -19,8 +19,10 @@
package net.minecraftforge.common.util;
import javax.annotation.Nullable;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.world.WorldProvider;
import net.minecraft.world.dimension.Dimension;
import net.minecraft.world.storage.WorldSavedData;
public class WorldCapabilityData extends WorldSavedData
@@ -35,7 +37,7 @@ public class WorldCapabilityData extends WorldSavedData
super(name);
}
public WorldCapabilityData(INBTSerializable<NBTTagCompound> serializable)
public WorldCapabilityData(@Nullable INBTSerializable<NBTTagCompound> serializable)
{
super(ID);
this.serializable = serializable;
@@ -66,7 +68,7 @@ public class WorldCapabilityData extends WorldSavedData
return true;
}
public void setCapabilities(WorldProvider provider, INBTSerializable<NBTTagCompound> capabilities)
public void setCapabilities(Dimension provider, INBTSerializable<NBTTagCompound> capabilities)
{
this.serializable = capabilities;
if (this.capNBT != null && serializable != null)
@@ -328,9 +328,9 @@ public class ForgeEventFactory
return event.getNewState();
}
public static ItemTooltipEvent onItemTooltip(ItemStack itemStack, @Nullable EntityPlayer entityPlayer, List<String> toolTip, ITooltipFlag flags)
public static ItemTooltipEvent onItemTooltip(ItemStack itemStack, @Nullable EntityPlayer entityPlayer, List<ITextComponent> list, ITooltipFlag flags)
{
ItemTooltipEvent event = new ItemTooltipEvent(itemStack, entityPlayer, toolTip, flags);
ItemTooltipEvent event = new ItemTooltipEvent(itemStack, entityPlayer, list, flags);
MinecraftForge.EVENT_BUS.post(event);
return event;
}
@@ -624,41 +624,18 @@ public class ForgeEventFactory
{
return MinecraftForge.EVENT_BUS.post(new RenderBlockOverlayEvent(player, renderPartialTicks, type, block, pos));
}
@Nullable
public static CapabilityDispatcher gatherCapabilities(TileEntity tileEntity)
public static <T extends ICapabilityProvider> CapabilityDispatcher gatherCapabilities(Class<? extends T> type, T provider)
{
return gatherCapabilities(new AttachCapabilitiesEvent<TileEntity>(TileEntity.class, tileEntity), null);
return gatherCapabilities(type, provider, null);
}
@SuppressWarnings("unchecked")
@Nullable
public static CapabilityDispatcher gatherCapabilities(Entity entity)
public static <T extends ICapabilityProvider> CapabilityDispatcher gatherCapabilities(Class<? extends T> type, T provider, @Nullable ICapabilityProvider parent)
{
return gatherCapabilities(new AttachCapabilitiesEvent<Entity>(Entity.class, entity), null);
}
@Nullable
public static CapabilityDispatcher gatherCapabilities(Village village)
{
return gatherCapabilities(new AttachCapabilitiesEvent<Village>(Village.class, village), null);
}
@Nullable
public static CapabilityDispatcher gatherCapabilities(ItemStack stack, ICapabilityProvider parent)
{
return gatherCapabilities(new AttachCapabilitiesEvent<ItemStack>(ItemStack.class, stack), parent);
}
@Nullable
public static CapabilityDispatcher gatherCapabilities(World world, ICapabilityProvider parent)
{
return gatherCapabilities(new AttachCapabilitiesEvent<World>(World.class, world), parent);
}
@Nullable
public static CapabilityDispatcher gatherCapabilities(Chunk chunk)
{
return gatherCapabilities(new AttachCapabilitiesEvent<Chunk>(Chunk.class, chunk), null);
return gatherCapabilities(new AttachCapabilitiesEvent<T>((Class<T>) type, provider), parent);
}
@Nullable
@@ -25,6 +25,7 @@ import net.minecraft.client.Minecraft;
import net.minecraft.client.util.ITooltipFlag;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.util.text.ITextComponent;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -34,17 +35,17 @@ public class ItemTooltipEvent extends PlayerEvent
private final ITooltipFlag flags;
@Nonnull
private final ItemStack itemStack;
private final List<String> toolTip;
private final List<ITextComponent> toolTip;
/**
* This event is fired in {@link ItemStack#getTooltip(EntityPlayer, ITooltipFlag)}, which in turn is called from it's respective GUIContainer.
* Tooltips are also gathered with a null entityPlayer during startup by {@link Minecraft#populateSearchTreeManager()}.
*/
public ItemTooltipEvent(@Nonnull ItemStack itemStack, @Nullable EntityPlayer entityPlayer, List<String> toolTip, ITooltipFlag flags)
public ItemTooltipEvent(@Nonnull ItemStack itemStack, @Nullable EntityPlayer entityPlayer, List<ITextComponent> list, ITooltipFlag flags)
{
super(entityPlayer);
this.itemStack = itemStack;
this.toolTip = toolTip;
this.toolTip = list;
this.flags = flags;
}
@@ -68,7 +69,7 @@ public class ItemTooltipEvent extends PlayerEvent
/**
* The {@link ItemStack} tooltip.
*/
public List<String> getToolTip()
public List<ITextComponent> getToolTip()
{
return toolTip;
}
@@ -24,7 +24,6 @@ import javax.annotation.Nullable;
import com.google.common.base.Preconditions;
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.entity.player.EntityPlayer;
@@ -40,6 +39,7 @@ import net.minecraft.util.SoundEvent;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.common.ForgeMod;
import net.minecraftforge.common.capabilities.OptionalCapabilityInstance;
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandlerItem;
@@ -74,8 +74,7 @@ public class FluidUtil
Preconditions.checkNotNull(world);
Preconditions.checkNotNull(pos);
IFluidHandler blockFluidHandler = getFluidHandler(world, pos, side);
return blockFluidHandler != null && interactWithFluidHandler(player, hand, blockFluidHandler);
return getFluidHandler(world, pos, side).map(handler -> interactWithFluidHandler(player, hand, handler)).orElse(false);
}
/**
@@ -98,21 +97,23 @@ public class FluidUtil
ItemStack heldItem = player.getHeldItem(hand);
if (!heldItem.isEmpty())
{
IItemHandler playerInventory = player.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null);
if (playerInventory != null)
{
FluidActionResult fluidActionResult = tryFillContainerAndStow(heldItem, handler, playerInventory, Integer.MAX_VALUE, player, true);
if (!fluidActionResult.isSuccess())
{
fluidActionResult = tryEmptyContainerAndStow(heldItem, handler, playerInventory, Integer.MAX_VALUE, player, true);
}
if (fluidActionResult.isSuccess())
{
player.setHeldItem(hand, fluidActionResult.getResult());
return true;
}
}
return player.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY)
.map(playerInventory -> {
FluidActionResult fluidActionResult = tryFillContainerAndStow(heldItem, handler, playerInventory, Integer.MAX_VALUE, player, true);
if (!fluidActionResult.isSuccess())
{
fluidActionResult = tryEmptyContainerAndStow(heldItem, handler, playerInventory, Integer.MAX_VALUE, player, true);
}
if (fluidActionResult.isSuccess())
{
player.setHeldItem(hand, fluidActionResult.getResult());
return true;
}
return false;
})
.orElse(false);
}
return false;
}
@@ -133,31 +134,31 @@ public class FluidUtil
public static FluidActionResult tryFillContainer(@Nonnull ItemStack container, IFluidHandler fluidSource, int maxAmount, @Nullable EntityPlayer player, boolean doFill)
{
ItemStack containerCopy = ItemHandlerHelper.copyStackWithSize(container, 1); // do not modify the input
IFluidHandlerItem containerFluidHandler = getFluidHandler(containerCopy);
if (containerFluidHandler != null)
{
FluidStack simulatedTransfer = tryFluidTransfer(containerFluidHandler, fluidSource, maxAmount, false);
if (simulatedTransfer != null)
{
if (doFill)
{
tryFluidTransfer(containerFluidHandler, fluidSource, maxAmount, true);
if (player != null)
return getFluidHandler(containerCopy)
.map(containerFluidHandler -> {
FluidStack simulatedTransfer = tryFluidTransfer(containerFluidHandler, fluidSource, maxAmount, false);
if (simulatedTransfer != null)
{
SoundEvent soundevent = simulatedTransfer.getFluid().getFillSound(simulatedTransfer);
player.world.playSound(null, player.posX, player.posY + 0.5, player.posZ, soundevent, SoundCategory.BLOCKS, 1.0F, 1.0F);
if (doFill)
{
tryFluidTransfer(containerFluidHandler, fluidSource, maxAmount, true);
if (player != null)
{
SoundEvent soundevent = simulatedTransfer.getFluid().getFillSound(simulatedTransfer);
player.world.playSound(null, player.posX, player.posY + 0.5, player.posZ, soundevent, SoundCategory.BLOCKS, 1.0F, 1.0F);
}
}
else
{
containerFluidHandler.fill(simulatedTransfer, true);
}
ItemStack resultContainer = containerFluidHandler.getContainer();
return new FluidActionResult(resultContainer);
}
}
else
{
containerFluidHandler.fill(simulatedTransfer, true);
}
ItemStack resultContainer = containerFluidHandler.getContainer();
return new FluidActionResult(resultContainer);
}
}
return FluidActionResult.FAILURE;
return FluidActionResult.FAILURE;
})
.orElse(FluidActionResult.FAILURE);
}
/**
@@ -177,35 +178,35 @@ public class FluidUtil
public static FluidActionResult tryEmptyContainer(@Nonnull ItemStack container, IFluidHandler fluidDestination, int maxAmount, @Nullable EntityPlayer player, boolean doDrain)
{
ItemStack containerCopy = ItemHandlerHelper.copyStackWithSize(container, 1); // do not modify the input
IFluidHandlerItem containerFluidHandler = getFluidHandler(containerCopy);
if (containerFluidHandler != null)
{
if (doDrain)
{
FluidStack transfer = tryFluidTransfer(fluidDestination, containerFluidHandler, maxAmount, true);
if (transfer != null)
{
if (player != null)
return getFluidHandler(containerCopy)
.map(containerFluidHandler -> {
if (doDrain)
{
SoundEvent soundevent = transfer.getFluid().getEmptySound(transfer);
player.world.playSound(null, player.posX, player.posY + 0.5, player.posZ, soundevent, SoundCategory.BLOCKS, 1.0F, 1.0F);
FluidStack transfer = tryFluidTransfer(fluidDestination, containerFluidHandler, maxAmount, true);
if (transfer != null)
{
if (player != null)
{
SoundEvent soundevent = transfer.getFluid().getEmptySound(transfer);
player.world.playSound(null, player.posX, player.posY + 0.5, player.posZ, soundevent, SoundCategory.BLOCKS, 1.0F, 1.0F);
}
ItemStack resultContainer = containerFluidHandler.getContainer();
return new FluidActionResult(resultContainer);
}
}
ItemStack resultContainer = containerFluidHandler.getContainer();
return new FluidActionResult(resultContainer);
}
}
else
{
FluidStack simulatedTransfer = tryFluidTransfer(fluidDestination, containerFluidHandler, maxAmount, false);
if (simulatedTransfer != null)
{
containerFluidHandler.drain(simulatedTransfer, true);
ItemStack resultContainer = containerFluidHandler.getContainer();
return new FluidActionResult(resultContainer);
}
}
}
return FluidActionResult.FAILURE;
else
{
FluidStack simulatedTransfer = tryFluidTransfer(fluidDestination, containerFluidHandler, maxAmount, false);
if (simulatedTransfer != null)
{
containerFluidHandler.drain(simulatedTransfer, true);
ItemStack resultContainer = containerFluidHandler.getContainer();
return new FluidActionResult(resultContainer);
}
}
return FluidActionResult.FAILURE;
})
.orElse(FluidActionResult.FAILURE);
}
/**
@@ -478,47 +479,30 @@ public class FluidUtil
* You can't fill or drain multiple items at once, if you do then liquid is multiplied or destroyed.
*
* Vanilla buckets will be converted to universal buckets if they are enabled.
*
* Returns null if the itemStack passed in does not have a fluid handler.
*/
@Nullable
public static IFluidHandlerItem getFluidHandler(@Nonnull ItemStack itemStack)
public static OptionalCapabilityInstance<IFluidHandlerItem> getFluidHandler(@Nonnull ItemStack itemStack)
{
if (itemStack.hasCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY, null))
{
return itemStack.getCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY, null);
}
else
{
return null;
}
return itemStack.getCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY);
}
/**
* Helper method to get the fluid contained in an itemStack
*/
@Nullable
public static FluidStack getFluidContained(@Nonnull ItemStack container)
public static OptionalCapabilityInstance<FluidStack> getFluidContained(@Nonnull ItemStack container)
{
if (!container.isEmpty())
{
container = ItemHandlerHelper.copyStackWithSize(container, 1);
IFluidHandlerItem fluidHandler = getFluidHandler(container);
if (fluidHandler != null)
{
return fluidHandler.drain(Integer.MAX_VALUE, false);
}
return getFluidHandler(container)
.map(handler -> handler.drain(Integer.MAX_VALUE, false));
}
return null;
return OptionalCapabilityInstance.empty();
}
/**
* Helper method to get an IFluidHandler for at a block position.
*
* Returns null if there is no valid fluid handler.
*/
@Nullable
public static IFluidHandler getFluidHandler(World world, BlockPos blockPos, @Nullable EnumFacing side)
public static OptionalCapabilityInstance<IFluidHandler> getFluidHandler(World world, BlockPos blockPos, @Nullable EnumFacing side)
{
IBlockState state = world.getBlockState(blockPos);
Block block = state.getBlock();
@@ -526,21 +510,21 @@ public class FluidUtil
if (block.hasTileEntity(state))
{
TileEntity tileEntity = world.getTileEntity(blockPos);
if (tileEntity != null && tileEntity.hasCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, side))
if (tileEntity != null)
{
return tileEntity.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, side);
}
}
}/* TODO fluids blocks?
if (block instanceof IFluidBlock)
{
return new FluidBlockWrapper((IFluidBlock) block, world, blockPos);
return OptionalCapabilityInstance.of(() -> new FluidBlockWrapper((IFluidBlock) block, world, blockPos));
}
else if (block instanceof BlockLiquid)
{
return new BlockLiquidWrapper((BlockLiquid) block, world, blockPos);
return OptionalCapabilityInstance.of(() -> new BlockLiquidWrapper((BlockLiquid) block, world, blockPos));
}
return null;
*/
return OptionalCapabilityInstance.empty();
}
/**
@@ -565,6 +549,7 @@ public class FluidUtil
IBlockState state = worldIn.getBlockState(pos);
Block block = state.getBlock();
/* TODO fluid blocks?
if (block instanceof IFluidBlock || block instanceof BlockLiquid)
{
IFluidHandler targetFluidHandler = getFluidHandler(worldIn, pos, side);
@@ -572,7 +557,7 @@ public class FluidUtil
{
return tryFillContainer(emptyContainer, targetFluidHandler, Integer.MAX_VALUE, playerIn, true);
}
}
}*/
return FluidActionResult.FAILURE;
}
@@ -591,12 +576,11 @@ public class FluidUtil
public static FluidActionResult tryPlaceFluid(@Nullable EntityPlayer player, World world, BlockPos pos, @Nonnull ItemStack container, FluidStack resource)
{
ItemStack containerCopy = ItemHandlerHelper.copyStackWithSize(container, 1); // do not modify the input
IFluidHandlerItem containerFluidHandler = getFluidHandler(containerCopy);
if (containerFluidHandler != null && tryPlaceFluid(player, world, pos, containerFluidHandler, resource))
{
return new FluidActionResult(containerFluidHandler.getContainer());
}
return FluidActionResult.FAILURE;
return getFluidHandler(containerCopy)
.filter(handler -> tryPlaceFluid(player, world, pos, handler, resource))
.map(IFluidHandlerItem::getContainer)
.map(FluidActionResult::new)
.orElse(FluidActionResult.FAILURE);
}
/**
@@ -674,7 +658,7 @@ public class FluidUtil
*/
private static IFluidHandler getFluidBlockHandler(Fluid fluid, World world, BlockPos pos)
{
Block block = fluid.getBlock();
Block block = fluid.getBlock();/* TODO fluid blocks?
if (block instanceof IFluidBlock)
{
return new FluidBlockWrapper((IFluidBlock) block, world, pos);
@@ -683,7 +667,7 @@ public class FluidUtil
{
return new BlockLiquidWrapper((BlockLiquid) block, world, pos);
}
else
else*/
{
return new BlockWrapper(block, world, pos);
}
@@ -724,7 +708,7 @@ public class FluidUtil
{
Fluid fluid = fluidStack.getFluid();
if (fluidStack.tag == null || fluidStack.tag.hasNoTags())
if (fluidStack.tag == null || fluidStack.tag.isEmpty())
{
if (fluid == FluidRegistry.WATER)
{