From 8ed83d66296cecc39ce72470b7c85623b89c0ecc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emmanuel=20Beno=C3=AEt?= Date: Sat, 18 Jun 2016 14:48:27 +0200 Subject: [PATCH] Milking can't be done too often Period depends on animal type. --- src/java/mmm/food/FMilkType.java | 25 ++- src/java/mmm/food/FMilkable.java | 273 +++++++++++++++++++++++++ src/java/mmm/food/FMilkingHandler.java | 65 ------ src/java/mmm/food/Food.java | 7 +- src/java/mmm/utils/I_UMessage.java | 23 +++ src/java/mmm/utils/URegistry.java | 32 +++ 6 files changed, 350 insertions(+), 75 deletions(-) create mode 100644 src/java/mmm/food/FMilkable.java delete mode 100644 src/java/mmm/food/FMilkingHandler.java create mode 100644 src/java/mmm/utils/I_UMessage.java diff --git a/src/java/mmm/food/FMilkType.java b/src/java/mmm/food/FMilkType.java index d562c58..4221dec 100644 --- a/src/java/mmm/food/FMilkType.java +++ b/src/java/mmm/food/FMilkType.java @@ -32,11 +32,14 @@ public class FMilkType COW = new FMilkType( ); SHEEP = new FMilkType( "sheep" , EntitySheep.class ); - PIG = new FMilkType( "pig" , EntityPig.class ); - HORSE = new FMilkType( "horse" , EntityHorse.class ) - .setExtraCheck( a -> ( (EntityHorse) a ).getType( ) == HorseType.HORSE ); + PIG = new FMilkType( "pig" , EntityPig.class ) // + .setPeriod( 48000 ); + HORSE = new FMilkType( "horse" , EntityHorse.class ) // + .setExtraCheck( a -> ( (EntityHorse) a ).getType( ) == HorseType.HORSE ) // + .setPeriod( 24000 ); DONKEY = new FMilkType( "donkey" , EntityHorse.class ) - .setExtraCheck( a -> ( (EntityHorse) a ).getType( ) == HorseType.DONKEY ); + .setExtraCheck( a -> ( (EntityHorse) a ).getType( ) == HorseType.DONKEY ) // + .setPeriod( 36000 ); } @@ -49,6 +52,7 @@ public class FMilkType public final boolean isVanilla; public final Class< ? extends EntityAnimal > animal; public final Item bucket; + private int period = 12000; private Predicate< EntityAnimal > extraCheck; @@ -86,6 +90,19 @@ public class FMilkType } + public int getPeriod( ) + { + return period; + } + + + public FMilkType setPeriod( int period ) + { + this.period = period; + return this; + } + + public boolean check( final EntityAnimal animal ) { return this.extraCheck == null || this.extraCheck.test( animal ); diff --git a/src/java/mmm/food/FMilkable.java b/src/java/mmm/food/FMilkable.java new file mode 100644 index 0000000..cb2b671 --- /dev/null +++ b/src/java/mmm/food/FMilkable.java @@ -0,0 +1,273 @@ +package mmm.food; + + +import java.util.Set; + +import io.netty.buffer.ByteBuf; +import mmm.Mmm; +import mmm.utils.I_UMessage; +import mmm.utils.URegistry; +import net.minecraft.client.Minecraft; +import net.minecraft.entity.Entity; +import net.minecraft.entity.passive.EntityAnimal; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.init.Items; +import net.minecraft.init.SoundEvents; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTBase; +import net.minecraft.nbt.NBTTagLong; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.capabilities.CapabilityInject; +import net.minecraftforge.common.capabilities.CapabilityManager; +import net.minecraftforge.common.capabilities.ICapabilitySerializable; +import net.minecraftforge.event.AttachCapabilitiesEvent; +import net.minecraftforge.event.entity.player.PlayerEvent; +import net.minecraftforge.event.entity.player.PlayerInteractEvent; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + + + +public class FMilkable +{ + + @CapabilityInject( FMilkable.class ) + public static final Capability< FMilkable > CAPABILITY = null; + + public static final ResourceLocation ID = new ResourceLocation( Mmm.ID , "Milkable" ); + + // ************************************************************************************************************* + + public static class Storage + implements Capability.IStorage< FMilkable > + { + + @Override + public NBTBase writeNBT( final Capability< FMilkable > capability , final FMilkable instance , + final EnumFacing side ) + { + return new NBTTagLong( instance.lastMilking ); + } + + + @Override + public void readNBT( final Capability< FMilkable > capability , final FMilkable instance , + final EnumFacing side , final NBTBase nbt ) + { + instance.lastMilking = ( (NBTTagLong) nbt ).getLong( ); + } + + } + + // ************************************************************************************************************* + + public static class Provider + implements ICapabilitySerializable< NBTBase > + { + + private final FMilkable instance = FMilkable.CAPABILITY.getDefaultInstance( ); + + + @Override + public boolean hasCapability( final Capability< ? > capability , final EnumFacing facing ) + { + return capability == FMilkable.CAPABILITY; + } + + + @Override + public < T > T getCapability( final Capability< T > capability , final EnumFacing facing ) + { + return capability == FMilkable.CAPABILITY ? FMilkable.CAPABILITY.cast( this.instance ) : null; + } + + + @Override + public NBTBase serializeNBT( ) + { + return FMilkable.CAPABILITY.writeNBT( this.instance , null ); + } + + + @Override + public void deserializeNBT( final NBTBase nbt ) + { + FMilkable.CAPABILITY.readNBT( this.instance , null , nbt ); + } + + } + + // ************************************************************************************************************* + + public static class Message + implements I_UMessage + { + private int entityId; + private long lastMilking; + + + public Message( ) + { + // EMPTY + } + + + public Message( final Entity entity ) + { + this.entityId = entity.getEntityId( ); + this.lastMilking = entity.getCapability( FMilkable.CAPABILITY , null ).getLastMilking( ); + } + + + @Override + public void fromBytes( final ByteBuf buf ) + { + this.entityId = buf.readInt( ); + this.lastMilking = buf.readLong( ); + } + + + @Override + public void toBytes( final ByteBuf buf ) + { + buf.writeInt( this.entityId ); + buf.writeLong( this.lastMilking ); + } + + + @Override + @SideOnly( Side.CLIENT ) + public void handleOnClient( ) + { + final Entity entity = Minecraft.getMinecraft( ).theWorld.getEntityByID( this.entityId ); + if ( entity == null || !entity.hasCapability( FMilkable.CAPABILITY , null ) ) { + return; + } + entity.getCapability( FMilkable.CAPABILITY , null ).lastMilking = this.lastMilking; + } + + } + + // ************************************************************************************************************* + + public static class EventHandler + { + @SubscribeEvent + public void attachCapability( final AttachCapabilitiesEvent.Entity event ) + { + if ( FMilkType.MILK_TYPES.containsKey( event.getEntity( ).getClass( ) ) ) { + event.addCapability( FMilkable.ID , new Provider( ) ); + } + } + + + @SubscribeEvent + public void startTracking( final PlayerEvent.StartTracking event ) + { + final EntityPlayer player = event.getEntityPlayer( ); + if ( player.getEntityWorld( ).isRemote ) { + return; + } + final Entity entity = event.getTarget( ); + if ( entity.hasCapability( FMilkable.CAPABILITY , null ) ) { + URegistry.network.sendTo( new Message( entity ) , (EntityPlayerMP) player ); + } + } + + + @SubscribeEvent + public void handleMilking( final PlayerInteractEvent.EntityInteract event ) + { + // We must be holding a bucket + final ItemStack heldStack = event.getItemStack( ); + if ( heldStack == null || heldStack.getItem( ) != Items.BUCKET ) { + return; + } + + // Are we targeting a potentially milk-producing animal? + final Entity target = event.getTarget( ); + if ( !target.hasCapability( CAPABILITY , null ) ) { + return; + } + + // We can't milk baby animals + final EntityAnimal animal = (EntityAnimal) target; + if ( animal.isChild( ) ) { + return; + } + + // Find actual milk type + final Set< FMilkType > milkTypes = FMilkType.MILK_TYPES.get( animal.getClass( ) ); + FMilkType milkType = null; + for ( final FMilkType type : milkTypes ) { + if ( type.check( animal ) ) { + milkType = type; + break; + } + } + if ( milkType == null ) { + return; + } + + // So yeah, we're definitely trying to milk something. Don't process any further, even + // if milking fails. + event.setCanceled( true ); + + // Make sure it's been long enough since the animal's last milking + final EntityPlayer player = event.getEntityPlayer( ); + FMilkable milkable = animal.getCapability( CAPABILITY , null ); + long curTime = player.worldObj.getTotalWorldTime( ); + if ( curTime - milkable.lastMilking < milkType.getPeriod( ) ) { + return; + } + milkable.lastMilking = curTime; + + // Send updates to players + if ( !player.worldObj.isRemote ) { + URegistry.network.sendToAll( new Message( animal ) ); + } + + // Actually milk the animal + if ( !milkType.isVanilla || player.capabilities.isCreativeMode ) { + final ItemStack bucket = new ItemStack( milkType.bucket ); + if ( --heldStack.stackSize == 0 ) { + player.setHeldItem( event.getHand( ) , bucket ); + } else if ( !player.inventory.addItemStackToInventory( bucket ) ) { + player.dropItem( bucket , false ); + } + player.playSound( SoundEvents.ENTITY_COW_MILK , 1.0F , 1.0F ); + } + } + } + + + // ************************************************************************************************************* + + public static void register( ) + { + CapabilityManager.INSTANCE.register( FMilkable.class , new Storage( ) , FMilkable::new ); + MinecraftForge.EVENT_BUS.register( new EventHandler( ) ); + URegistry.addClientMessage( Message.class ); + } + + // ************************************************************************************************************* + + private long lastMilking = -500000; + + + public long getLastMilking( ) + { + return this.lastMilking; + } + + + public void setLastMilking( final long lastMilking ) + { + this.lastMilking = lastMilking; + } +} diff --git a/src/java/mmm/food/FMilkingHandler.java b/src/java/mmm/food/FMilkingHandler.java deleted file mode 100644 index 2373d3b..0000000 --- a/src/java/mmm/food/FMilkingHandler.java +++ /dev/null @@ -1,65 +0,0 @@ -package mmm.food; - - -import java.util.Set; - -import net.minecraft.entity.Entity; -import net.minecraft.entity.passive.EntityAnimal; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.Items; -import net.minecraft.init.SoundEvents; -import net.minecraft.item.ItemStack; -import net.minecraftforge.event.entity.player.PlayerInteractEvent; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; - - - -public class FMilkingHandler -{ - - @SubscribeEvent - public void handleMilking( final PlayerInteractEvent.EntityInteract event ) - { - final ItemStack heldStack = event.getItemStack( ); - if ( heldStack == null || heldStack.getItem( ) != Items.BUCKET ) { - return; - } - final Entity target = event.getTarget( ); - if ( ! ( target instanceof EntityAnimal ) ) { - return; - } - - final EntityAnimal animal = (EntityAnimal) target; - if ( animal.isChild( ) ) { - return; - } - - if ( !FMilkType.MILK_TYPES.containsKey( animal.getClass( ) ) ) { - return; - } - - final Set< FMilkType > milkTypes = FMilkType.MILK_TYPES.get( animal.getClass( ) ); - FMilkType milkType = null; - for ( final FMilkType type : milkTypes ) { - if ( type.check( animal ) ) { - milkType = type; - break; - } - } - if ( milkType == null ) { - return; - } - - final EntityPlayer player = event.getEntityPlayer( ); - if ( !milkType.isVanilla || player.capabilities.isCreativeMode ) { - final ItemStack bucket = new ItemStack( milkType.bucket ); - if ( --heldStack.stackSize == 0 ) { - player.setHeldItem( event.getHand( ) , bucket ); - } else if ( !player.inventory.addItemStackToInventory( bucket ) ) { - player.dropItem( bucket , false ); - } - player.playSound( SoundEvents.ENTITY_COW_MILK , 1.0F , 1.0F ); - } - } - -} diff --git a/src/java/mmm/food/Food.java b/src/java/mmm/food/Food.java index 91ec215..3dbb5fb 100644 --- a/src/java/mmm/food/Food.java +++ b/src/java/mmm/food/Food.java @@ -1,16 +1,11 @@ package mmm.food; - -import net.minecraftforge.common.MinecraftForge; - - - public class Food { static { FMilkType.preInit( ); - MinecraftForge.EVENT_BUS.register( new FMilkingHandler( ) ); + FMilkable.register( ); } diff --git a/src/java/mmm/utils/I_UMessage.java b/src/java/mmm/utils/I_UMessage.java new file mode 100644 index 0000000..2c62562 --- /dev/null +++ b/src/java/mmm/utils/I_UMessage.java @@ -0,0 +1,23 @@ +package mmm.utils; + + +import net.minecraftforge.fml.common.network.simpleimpl.IMessage; + + + +public interface I_UMessage + extends IMessage +{ + + default void handleOnClient( ) + { + // EMPTY + } + + + default void handleOnServer( ) + { + // EMPTY + } + +} diff --git a/src/java/mmm/utils/URegistry.java b/src/java/mmm/utils/URegistry.java index a87f2fd..33ceb39 100644 --- a/src/java/mmm/utils/URegistry.java +++ b/src/java/mmm/utils/URegistry.java @@ -9,18 +9,26 @@ import java.util.Map; import mmm.Mmm; import net.minecraft.block.Block; +import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.block.model.ModelResourceLocation; import net.minecraft.item.Item; import net.minecraft.item.ItemBlock; +import net.minecraft.util.IThreadListener; import net.minecraft.util.ResourceLocation; +import net.minecraft.world.WorldServer; import net.minecraftforge.client.model.ModelLoader; +import net.minecraftforge.fml.common.network.simpleimpl.MessageContext; +import net.minecraftforge.fml.common.network.simpleimpl.SimpleNetworkWrapper; import net.minecraftforge.fml.common.registry.GameRegistry; import net.minecraftforge.fml.common.registry.IForgeRegistryEntry; +import net.minecraftforge.fml.relauncher.Side; public class URegistry { + public static final SimpleNetworkWrapper network = new SimpleNetworkWrapper( Mmm.ID ); + private static int nextPacketDiscriminator = 0; private static final HashSet< I_URecipeRegistrar > RECIPE_REGISTRARS = new HashSet<>( ); private static final HashSet< I_UOreGenerationRegistrar > ORE_GEN_REGISTRARS = new HashSet<>( ); @@ -135,6 +143,30 @@ public class URegistry } + public static void addClientMessage( final Class< ? extends I_UMessage > message ) + { + URegistry.network.registerMessage( // + ( final I_UMessage m , final MessageContext ctx ) -> { + final IThreadListener main = Minecraft.getMinecraft( ); + main.addScheduledTask( ( ) -> m.handleOnClient( ) ); + return null; + } , // + message , URegistry.nextPacketDiscriminator++ , Side.CLIENT ); + } + + + public static void addServerMessage( final Class< ? extends I_UMessage > message ) + { + URegistry.network.registerMessage( // + ( final I_UMessage m , final MessageContext ctx ) -> { + final IThreadListener main = (WorldServer) ctx.getServerHandler( ).playerEntity.worldObj; + main.addScheduledTask( ( ) -> m.handleOnServer( ) ); + return null; + } , // + message , URegistry.nextPacketDiscriminator++ , Side.SERVER ); + } + + public static void setupItemModels( ) { for ( final Map.Entry< Item , Boolean > entry : URegistry.ITEMS.entrySet( ) ) {