From 07f6610bb924ec226e55710c649c8c25df717494 Mon Sep 17 00:00:00 2001 From: Rongmario Date: Mon, 22 Jun 2020 17:49:54 +0100 Subject: [PATCH 1/5] EntityItemPickupEvent + PlayerEvent#ItemPickupEvent --- .../entity/player/EntityItemPickupEvent.java | 46 ++++++++++ .../event/entity/player/PlayerEvent.java | 28 +++++- .../impl/event/entity/EntityEvents.java | 16 ++++ .../mixin/event/entity/MixinItemEntity.java | 87 +++++++++++++++++++ .../patchwork-events-entity.mixins.json | 1 + 5 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/player/EntityItemPickupEvent.java create mode 100644 patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinItemEntity.java diff --git a/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/player/EntityItemPickupEvent.java b/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/player/EntityItemPickupEvent.java new file mode 100644 index 00000000..b51ee15a --- /dev/null +++ b/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/player/EntityItemPickupEvent.java @@ -0,0 +1,46 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * 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.event.entity.player; + +import net.minecraft.entity.ItemEntity; +import net.minecraft.entity.player.PlayerEntity; + +/** + * This event is called when a player collides with a EntityItem on the ground. + * The event can be canceled, and no further processing will be done. + * + *

You can set the result of this event to ALLOW which will trigger the + * processing of achievements, FML's event, play the sound, and kill the + * entity if all the items are picked up. + * + *

setResult(ALLOW) is the same as the old setHandled() + */ +public class EntityItemPickupEvent extends PlayerEvent { + private final ItemEntity item; + + public EntityItemPickupEvent(PlayerEntity player, ItemEntity item) { + super(player); + this.item = item; + } + + public ItemEntity getItem() { + return item; + } +} diff --git a/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/player/PlayerEvent.java b/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/player/PlayerEvent.java index 4a0e47c9..5ab9ce78 100644 --- a/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/player/PlayerEvent.java +++ b/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/player/PlayerEvent.java @@ -21,6 +21,8 @@ import net.minecraftforge.event.entity.living.LivingEvent; +import net.minecraft.entity.ItemEntity; +import net.minecraft.item.ItemStack; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.Entity; @@ -131,6 +133,31 @@ public boolean isWasDeath() { } } + public static class ItemPickupEvent extends PlayerEvent { + /** + * Original EntityItem with current remaining stack size. + */ + private final ItemEntity originalEntity; + /** + * Clone item stack, containing the item and amount picked up. + */ + private final ItemStack stack; + + public ItemPickupEvent(PlayerEntity player, ItemEntity entPickedUp, ItemStack stack) { + super(player); + this.originalEntity = entPickedUp; + this.stack = stack; + } + + public ItemStack getStack() { + return stack; + } + + public ItemEntity getOriginalEntity() { + return originalEntity; + } + } + /*TODO Events: HarvestCheck BreakSpeed @@ -138,7 +165,6 @@ public boolean isWasDeath() { LoadFromFile SaveToFile Visibility - ItemPickupEvent ItemCraftedEvent ItemSmeltedEvent PlayerLoggedOutEvent diff --git a/patchwork-events-entity/src/main/java/net/patchworkmc/impl/event/entity/EntityEvents.java b/patchwork-events-entity/src/main/java/net/patchworkmc/impl/event/entity/EntityEvents.java index 3c753fd4..c3be6fbc 100644 --- a/patchwork-events-entity/src/main/java/net/patchworkmc/impl/event/entity/EntityEvents.java +++ b/patchwork-events-entity/src/main/java/net/patchworkmc/impl/event/entity/EntityEvents.java @@ -32,6 +32,7 @@ import net.minecraftforge.event.entity.living.LivingSetAttackTargetEvent; import net.minecraftforge.event.entity.living.LivingSpawnEvent; import net.minecraftforge.event.entity.player.AttackEntityEvent; +import net.minecraftforge.event.entity.player.EntityItemPickupEvent; import net.minecraftforge.event.entity.player.PlayerEvent; import net.minecraftforge.event.entity.player.PlayerFlyableFallEvent; import net.minecraftforge.event.entity.player.PlayerInteractEvent; @@ -43,6 +44,7 @@ import net.minecraft.entity.Entity; import net.minecraft.entity.EntityDimensions; import net.minecraft.entity.EntityPose; +import net.minecraft.entity.ItemEntity; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.SpawnType; import net.minecraft.entity.damage.DamageSource; @@ -123,6 +125,10 @@ public static float onLivingDamage(LivingEntity entity, DamageSource src, float return MinecraftForge.EVENT_BUS.post(event) ? 0 : event.getAmount(); } + public static void onPlayerItemPickup(PlayerEntity player, ItemEntity item, ItemStack clone) { + MinecraftForge.EVENT_BUS.post(new PlayerEvent.ItemPickupEvent(player, item, clone)); + } + public static float getEyeHeight(Entity entity, EntityPose pose, EntityDimensions size, float defaultHeight) { EntityEvent.EyeHeight event = new EntityEvent.EyeHeight(entity, pose, size, defaultHeight); MinecraftForge.EVENT_BUS.post(event); @@ -181,6 +187,16 @@ public static boolean attackEntity(PlayerEntity player, Entity target) { return !item.onLeftClickEntity(stack, player, target); } + public static int onItemPickup(ItemEntity entityItem, PlayerEntity player) { + EntityItemPickupEvent event = new EntityItemPickupEvent(player, entityItem); + + if (MinecraftForge.EVENT_BUS.post(event)) { + return -1; + } + + return event.getResult() == Result.ALLOW ? 1 : 0; + } + @Override public void onInitialize() { UseItemCallback.EVENT.register((player, world, hand) -> { diff --git a/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinItemEntity.java b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinItemEntity.java new file mode 100644 index 00000000..0de034ec --- /dev/null +++ b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinItemEntity.java @@ -0,0 +1,87 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * 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.patchworkmc.mixin.event.entity; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +import net.minecraft.entity.ItemEntity; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; + +import net.patchworkmc.impl.event.entity.EntityEvents; + +@Mixin(ItemEntity.class) +public class MixinItemEntity { + @Unique + private final String ON_PLAYER_COLLISION = "onPlayerCollision"; + + @Unique + private final ThreadLocal copy = new ThreadLocal<>(); + + @Unique + private final ThreadLocal result = new ThreadLocal<>(); + + @Shadow + private int pickupDelay; + + @Inject(method = ON_PLAYER_COLLISION, at = @At(value = "INVOKE", target = "net/minecraft/entity/ItemEntity.getStack()Lnet/minecraft/item/ItemStack;"), cancellable = true) + public void hookOnPlayerCollide(PlayerEntity player, CallbackInfo ci) { + if (pickupDelay > 0) { + ci.cancel(); + } + } + + @Inject(method = ON_PLAYER_COLLISION, at = @At(value = "INVOKE_ASSIGN", target = "net/minecraft/item/ItemStack.getCount()I"), locals = LocalCapture.CAPTURE_FAILHARD, cancellable = true) + public void hookOnItemPickup(PlayerEntity player, CallbackInfo ci, ItemStack stack) { + int hook = EntityEvents.onItemPickup((ItemEntity) (Object) this, player); + + if (hook < 0) { + ci.cancel(); + } + + result.set(hook); + copy.set(stack.copy()); + } + + @Redirect(method = ON_PLAYER_COLLISION, at = @At(value = "INVOKE", target = "net/minecraft/entity/player/PlayerInventory.insertStack(Lnet/minecraft/item/ItemStack;)Z")) + public boolean redirectOnInsertStack(PlayerInventory inventory, ItemStack stack) { + return result.get() == 1 || stack.getCount() <= 0 || inventory.insertStack(stack); + } + + @Redirect(method = ON_PLAYER_COLLISION, at = @At(value = "INVOKE", target = "net/minecraft/entity/player/PlayerEntity.sendPickup(Lnet/minecraft/entity/Entity;I)V")) + public void redirectOnItemPickup(PlayerEntity player, Entity entity, int quantity) { + copy.get().setCount(copy.get().getCount() - quantity); + EntityEvents.onPlayerItemPickup(player, (ItemEntity) entity, copy.get()); + } + + @Inject(method = ON_PLAYER_COLLISION, at = @At(value = "INVOKE", target = "net/minecraft/entity/ItemEntity.remove()V")) + public void hookAfterIsEmptyCheck(PlayerEntity player, CallbackInfo ci) { + player.sendPickup((ItemEntity) (Object) this, ((ItemEntity) (Object) this).getStack().getCount()); + } +} diff --git a/patchwork-events-entity/src/main/resources/patchwork-events-entity.mixins.json b/patchwork-events-entity/src/main/resources/patchwork-events-entity.mixins.json index 7b7c0180..d09d8a23 100644 --- a/patchwork-events-entity/src/main/resources/patchwork-events-entity.mixins.json +++ b/patchwork-events-entity/src/main/resources/patchwork-events-entity.mixins.json @@ -3,6 +3,7 @@ "package": "net.patchworkmc.mixin.event.entity", "compatibilityLevel": "JAVA_8", "mixins": [ + "MixinItemEntity", "MixinEntity", "MixinEntityTrackerEntry", "MixinEntityType", From cd2e462778f22c4a6996cd0f580eefdcbd586fee Mon Sep 17 00:00:00 2001 From: Rongmario Date: Mon, 22 Jun 2020 23:16:26 +0100 Subject: [PATCH 2/5] Alternate way with Overwrite implemented --- .../mixin/event/entity/MixinItemEntity.java | 94 ++++++++++--------- 1 file changed, 51 insertions(+), 43 deletions(-) diff --git a/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinItemEntity.java b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinItemEntity.java index 0de034ec..ddb6b268 100644 --- a/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinItemEntity.java +++ b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinItemEntity.java @@ -19,69 +19,77 @@ package net.patchworkmc.mixin.event.entity; +import java.util.UUID; + +import net.minecraftforge.event.entity.player.EntityItemPickupEvent; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.LocalCapture; -import net.minecraft.entity.ItemEntity; import net.minecraft.entity.Entity; -import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.ItemEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; +import net.minecraft.stat.Stats; +import net.minecraft.world.World; import net.patchworkmc.impl.event.entity.EntityEvents; @Mixin(ItemEntity.class) -public class MixinItemEntity { - @Unique - private final String ON_PLAYER_COLLISION = "onPlayerCollision"; - - @Unique - private final ThreadLocal copy = new ThreadLocal<>(); +public abstract class MixinItemEntity extends Entity { + @Shadow + private int pickupDelay, age; - @Unique - private final ThreadLocal result = new ThreadLocal<>(); + @Shadow + private UUID owner; @Shadow - private int pickupDelay; + public abstract ItemStack getStack(); - @Inject(method = ON_PLAYER_COLLISION, at = @At(value = "INVOKE", target = "net/minecraft/entity/ItemEntity.getStack()Lnet/minecraft/item/ItemStack;"), cancellable = true) - public void hookOnPlayerCollide(PlayerEntity player, CallbackInfo ci) { - if (pickupDelay > 0) { - ci.cancel(); - } + protected MixinItemEntity(EntityType entityType, World world) { + super(entityType, world); } - @Inject(method = ON_PLAYER_COLLISION, at = @At(value = "INVOKE_ASSIGN", target = "net/minecraft/item/ItemStack.getCount()I"), locals = LocalCapture.CAPTURE_FAILHARD, cancellable = true) - public void hookOnItemPickup(PlayerEntity player, CallbackInfo ci, ItemStack stack) { - int hook = EntityEvents.onItemPickup((ItemEntity) (Object) this, player); + /** + * @author Rongmario + * @reason

To fully adapt {@link ItemEntity#onPlayerCollision} to Forge's patched version, approximately + * 3 injections and 2 redirects is needed. Which at that point, Overwrite might be a better option.

+ * + *

This also eliminates the need of holding 2 references (that might have used ThreadLocal) + * for {@link EntityItemPickupEvent}'s result and a copied ItemStack.

+ * + *

Small changes to Forge's implementation: + * 1. pickupDelay is only checked once. + * 2. Variables are assigned after the the first return.

+ */ + @Overwrite + public void onPlayerCollision(PlayerEntity player) { + if (!this.world.isClient && this.pickupDelay <= 0) { + final ItemEntity entity = (ItemEntity) (Object) this; + int result = EntityEvents.onItemPickup(entity, player); - if (hook < 0) { - ci.cancel(); - } + if (result < 0) { + return; + } - result.set(hook); - copy.set(stack.copy()); - } + ItemStack itemStack = entity.getStack(); + int i = itemStack.getCount(); + ItemStack copy = itemStack.copy(); - @Redirect(method = ON_PLAYER_COLLISION, at = @At(value = "INVOKE", target = "net/minecraft/entity/player/PlayerInventory.insertStack(Lnet/minecraft/item/ItemStack;)Z")) - public boolean redirectOnInsertStack(PlayerInventory inventory, ItemStack stack) { - return result.get() == 1 || stack.getCount() <= 0 || inventory.insertStack(stack); - } + // TODO: '6000' is hardcoded right now, but Forge has exposed it through IForgeItem#getEntityLifespan + if ((this.owner == null || 6000 - this.age <= 200 || this.owner.equals(player.getUuid())) && (result == 1 || i <= 0 || player.inventory.insertStack(itemStack))) { + copy.setCount(copy.getCount() - i); + EntityEvents.onPlayerItemPickup(player, entity, copy); - @Redirect(method = ON_PLAYER_COLLISION, at = @At(value = "INVOKE", target = "net/minecraft/entity/player/PlayerEntity.sendPickup(Lnet/minecraft/entity/Entity;I)V")) - public void redirectOnItemPickup(PlayerEntity player, Entity entity, int quantity) { - copy.get().setCount(copy.get().getCount() - quantity); - EntityEvents.onPlayerItemPickup(player, (ItemEntity) entity, copy.get()); - } + if (itemStack.isEmpty()) { + player.sendPickup(entity, i); + entity.remove(); + itemStack.setCount(i); + } - @Inject(method = ON_PLAYER_COLLISION, at = @At(value = "INVOKE", target = "net/minecraft/entity/ItemEntity.remove()V")) - public void hookAfterIsEmptyCheck(PlayerEntity player, CallbackInfo ci) { - player.sendPickup((ItemEntity) (Object) this, ((ItemEntity) (Object) this).getStack().getCount()); + player.increaseStat(Stats.PICKED_UP.getOrCreateStat(itemStack.getItem()), i); + } + } } } From 2c8748e0be52365ced3da039ef689b7a04bfc08b Mon Sep 17 00:00:00 2001 From: Rongmario Date: Fri, 3 Jul 2020 14:32:33 +0100 Subject: [PATCH 3/5] Reverted Overwrite + Fixed non-Overwrite implementation - Added entity.item events, ItemExpireEvent is implemented but ItemTossEvent is a stub right now. - Implemented 'lifespan', Forge exposed this to modders via IForgeItem/IForgeItemStack. So the injections are commented out at the moment. - Another injection is commented out, as IForgeItem/IForgeItemStack#onEntityItemUpdate isn't implemented yet. -NOTE: I changed EntityItemPickupEvent's call's signature, it now returns a boolean as per request (can use Result too). As it seems like -1 and 1 is returned, and 0 is never returned anywhere. --- .../event/entity/item/ItemEvent.java | 50 ++++++ .../event/entity/item/ItemExpireEvent.java | 53 +++++++ .../event/entity/item/ItemTossEvent.java | 52 +++++++ .../impl/event/entity/EntityEvents.java | 27 +++- .../mixin/event/entity/MixinItemEntity.java | 142 +++++++++++------- 5 files changed, 264 insertions(+), 60 deletions(-) create mode 100644 patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/item/ItemEvent.java create mode 100644 patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/item/ItemExpireEvent.java create mode 100644 patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/item/ItemTossEvent.java diff --git a/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/item/ItemEvent.java b/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/item/ItemEvent.java new file mode 100644 index 00000000..de273e77 --- /dev/null +++ b/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/item/ItemEvent.java @@ -0,0 +1,50 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * 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.event.entity.item; + +import net.minecraft.entity.ItemEntity; +import net.minecraftforge.event.entity.EntityEvent; + +/** + * Base class for all ItemEntity events. Contains a reference to the + * ItemEntity of interest. For most ItemEntity events, there's little to no + * additional useful data from the firing method that isn't already contained + * within the ItemEntity instance. + */ +public class ItemEvent extends EntityEvent { + private final ItemEntity entityItem; + + /** + * Creates a new event for an EntityItem. + * + * @param itemEntity The EntityItem for this event + */ + public ItemEvent(ItemEntity itemEntity) { + super(itemEntity); + this.entityItem = itemEntity; + } + + /** + * The relevant EntityItem for this event, already cast for you. + */ + public ItemEntity getEntityItem() { + return entityItem; + } +} diff --git a/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/item/ItemExpireEvent.java b/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/item/ItemExpireEvent.java new file mode 100644 index 00000000..0b0e7431 --- /dev/null +++ b/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/item/ItemExpireEvent.java @@ -0,0 +1,53 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * 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.event.entity.item; + +import net.minecraft.entity.ItemEntity; + +/** + * Event that is fired when an ItemEntity's age has reached its maximum + * lifespan. Canceling this event will prevent the ItemEntity from being + * flagged as dead, thus staying it's removal from the world. If canceled + * it will add more time to the entities life equal to extraLife. + */ + +public class ItemExpireEvent extends ItemEvent { + + private int extraLife; + + /** + * Creates a new event for an expiring EntityItem. + * + * @param entityItem The ItemEntity being deleted. + * @param extraLife The amount of time to be added to this entities lifespan if the event is canceled. + */ + public ItemExpireEvent(ItemEntity entityItem, int extraLife) { + super(entityItem); + this.setExtraLife(extraLife); + } + + public int getExtraLife() { + return extraLife; + } + + public void setExtraLife(int extraLife) { + this.extraLife = extraLife; + } +} diff --git a/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/item/ItemTossEvent.java b/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/item/ItemTossEvent.java new file mode 100644 index 00000000..5851b7dc --- /dev/null +++ b/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/item/ItemTossEvent.java @@ -0,0 +1,52 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * 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.event.entity.item; + +import net.minecraft.entity.ItemEntity; +import net.minecraft.entity.player.PlayerEntity; + +/** + * Event that is fired whenever a player tosses (Q) an item or drag-n-drops a + * stack of items outside the inventory GUI screens. Canceling the event will + * stop the items from entering the world, but will not prevent them being + * removed from the inventory - and thus removed from the system. + */ +// TODO: Call Location - ForgeHook#onPlayerTossEvent -> PlayerEntity#dropSelectedItem + PlayerEntity#dropItem +public class ItemTossEvent extends ItemEvent { + private final PlayerEntity player; + + /** + * Creates a new event for ItemEntities tossed by a player. + * + * @param entityItem The ItemEntity being tossed. + * @param player The player tossing the item. + */ + public ItemTossEvent(ItemEntity entityItem, PlayerEntity player) { + super(entityItem); + this.player = player; + } + + /** + * The player tossing the item. + */ + public PlayerEntity getPlayer() { + return player; + } +} diff --git a/patchwork-events-entity/src/main/java/net/patchworkmc/impl/event/entity/EntityEvents.java b/patchwork-events-entity/src/main/java/net/patchworkmc/impl/event/entity/EntityEvents.java index c3be6fbc..56e15812 100644 --- a/patchwork-events-entity/src/main/java/net/patchworkmc/impl/event/entity/EntityEvents.java +++ b/patchwork-events-entity/src/main/java/net/patchworkmc/impl/event/entity/EntityEvents.java @@ -23,6 +23,7 @@ import net.minecraftforge.common.extensions.IForgeItem; import net.minecraftforge.event.entity.EntityEvent; import net.minecraftforge.event.entity.EntityJoinWorldEvent; +import net.minecraftforge.event.entity.item.ItemExpireEvent; import net.minecraftforge.event.entity.living.LivingAttackEvent; import net.minecraftforge.event.entity.living.LivingDamageEvent; import net.minecraftforge.event.entity.living.LivingDeathEvent; @@ -62,6 +63,8 @@ import net.fabricmc.fabric.api.event.player.UseBlockCallback; import net.fabricmc.fabric.api.event.player.UseItemCallback; +import javax.annotation.Nonnull; + public class EntityEvents implements ModInitializer { private static final Logger LOGGER = LogManager.getLogger("patchwork-events-entity"); @@ -125,10 +128,6 @@ public static float onLivingDamage(LivingEntity entity, DamageSource src, float return MinecraftForge.EVENT_BUS.post(event) ? 0 : event.getAmount(); } - public static void onPlayerItemPickup(PlayerEntity player, ItemEntity item, ItemStack clone) { - MinecraftForge.EVENT_BUS.post(new PlayerEvent.ItemPickupEvent(player, item, clone)); - } - public static float getEyeHeight(Entity entity, EntityPose pose, EntityDimensions size, float defaultHeight) { EntityEvent.EyeHeight event = new EntityEvent.EyeHeight(entity, pose, size, defaultHeight); MinecraftForge.EVENT_BUS.post(event); @@ -187,14 +186,26 @@ public static boolean attackEntity(PlayerEntity player, Entity target) { return !item.onLeftClickEntity(stack, player, target); } - public static int onItemPickup(ItemEntity entityItem, PlayerEntity player) { - EntityItemPickupEvent event = new EntityItemPickupEvent(player, entityItem); + public static void firePlayerItemPickupEvent(PlayerEntity player, ItemEntity item, ItemStack clone) { + MinecraftForge.EVENT_BUS.post(new PlayerEvent.ItemPickupEvent(player, item, clone)); + } + + public static boolean onItemPickup(ItemEntity entityItem, PlayerEntity player) { + return MinecraftForge.EVENT_BUS.post(new EntityItemPickupEvent(player, entityItem)); + } + + public static int onItemExpire(ItemEntity entity, @Nonnull ItemStack item) { + if (item.isEmpty()) { + return -1; + } + ItemExpireEvent event = new ItemExpireEvent(entity, 6000); + // TODO: ItemExpireEvent event = new ItemExpireEvent(entity, (item.isEmpty() ? 6000 : item.getItem().getEntityLifespan(item, entity.world))); - if (MinecraftForge.EVENT_BUS.post(event)) { + if (!MinecraftForge.EVENT_BUS.post(event)) { return -1; } - return event.getResult() == Result.ALLOW ? 1 : 0; + return event.getExtraLife(); } @Override diff --git a/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinItemEntity.java b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinItemEntity.java index ddb6b268..b3506749 100644 --- a/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinItemEntity.java +++ b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinItemEntity.java @@ -19,77 +19,115 @@ package net.patchworkmc.mixin.event.entity; -import java.util.UUID; - -import net.minecraftforge.event.entity.player.EntityItemPickupEvent; +import net.minecraft.world.World; +import org.objectweb.asm.Opcodes; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.Shadow; import net.minecraft.entity.Entity; -import net.minecraft.entity.EntityType; import net.minecraft.entity.ItemEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; -import net.minecraft.stat.Stats; -import net.minecraft.world.World; +import net.minecraft.nbt.CompoundTag; import net.patchworkmc.impl.event.entity.EntityEvents; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Constant; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.ModifyConstant; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(ItemEntity.class) -public abstract class MixinItemEntity extends Entity { - @Shadow - private int pickupDelay, age; - +public abstract class MixinItemEntity { @Shadow - private UUID owner; + private int pickupDelay; @Shadow public abstract ItemStack getStack(); - protected MixinItemEntity(EntityType entityType, World world) { - super(entityType, world); + @Unique + public int lifespan = 6000; + + // TODO -> Forge has exposed 'lifespan' (hardcoded to 6000) through IForgeItemStack/IForgeItem#getEntityLifespan + /* + @Inject(method = "(Lnet/minecraft/world/World;DDDLnet/minecraft/item/ItemStack;)V", at = @At("RETURN")) + private void modifyLifespan(World world, double x, double y, double z, ItemStack stack, CallbackInfo ci) { + this.lifespan = stack.getItem() == null ? 6000 : ((IForgeItemStack) stack).getEntityLifespan(world); } + */ - /** - * @author Rongmario - * @reason

To fully adapt {@link ItemEntity#onPlayerCollision} to Forge's patched version, approximately - * 3 injections and 2 redirects is needed. Which at that point, Overwrite might be a better option.

- * - *

This also eliminates the need of holding 2 references (that might have used ThreadLocal) - * for {@link EntityItemPickupEvent}'s result and a copied ItemStack.

- * - *

Small changes to Forge's implementation: - * 1. pickupDelay is only checked once. - * 2. Variables are assigned after the the first return.

+ // TODO -> Forge has a callback at IForgeItemStack/IForgeItem#onEntityItemUpdate + /* + @Inject(method = "tick", at = @At("HEAD"), cancellable = true) + private void onUpdate(CallbackInfo ci) { + if ((IForgeItemStack) stack).onEntityItemUpdate((ItemEntity) (Object) this) { + ci.cancel(); + } + } */ - @Overwrite - public void onPlayerCollision(PlayerEntity player) { - if (!this.world.isClient && this.pickupDelay <= 0) { - final ItemEntity entity = (ItemEntity) (Object) this; - int result = EntityEvents.onItemPickup(entity, player); - - if (result < 0) { - return; - } - - ItemStack itemStack = entity.getStack(); - int i = itemStack.getCount(); - ItemStack copy = itemStack.copy(); - - // TODO: '6000' is hardcoded right now, but Forge has exposed it through IForgeItem#getEntityLifespan - if ((this.owner == null || 6000 - this.age <= 200 || this.owner.equals(player.getUuid())) && (result == 1 || i <= 0 || player.inventory.insertStack(itemStack))) { - copy.setCount(copy.getCount() - i); - EntityEvents.onPlayerItemPickup(player, entity, copy); - - if (itemStack.isEmpty()) { - player.sendPickup(entity, i); - entity.remove(); - itemStack.setCount(i); - } - - player.increaseStat(Stats.PICKED_UP.getOrCreateStat(itemStack.getItem()), i); - } + + @ModifyConstant(method = "tick", constant = @Constant(intValue = 6000)) + private int onTickCheckLifespan(int original) { + return lifespan; + } + + @Redirect(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/ItemEntity;remove()V")) + private void fireOnItemExpire(ItemEntity item) { + int hook = EntityEvents.onItemExpire(item, getStack()); + + if (hook < 0) { + item.remove(); } + else { + this.lifespan += hook; + } + } + + @Inject(method = "writeCustomDataToTag", at = @At("HEAD")) + private void addLifespanToTag(CompoundTag tag, CallbackInfo ci) { + tag.putInt("Lifespan", lifespan); } + + @Inject(method = "readCustomDataFromTag", at = @At("HEAD")) + private void readLifespanInTag(CompoundTag tag, CallbackInfo ci) { + if (tag.contains("Lifespan")) { + this.lifespan = tag.getInt("Lifespan"); + } + } + + @Redirect(method = "onPlayerCollision", at = @At(value = "FIELD", opcode = Opcodes.GETFIELD, target = "Lnet/minecraft/world/World;isClient:Z")) + private boolean checkPickupDelay(World world) { + return world.isClient && this.pickupDelay <= 0; + } + + @Inject(method = "onPlayerCollision", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/ItemEntity;getStack()Lnet/minecraft/item/ItemStack;"), cancellable = true) + private void fireEntityItemPickup(PlayerEntity player, CallbackInfo ci) { + if (EntityEvents.onItemPickup((ItemEntity) (Object) this, player)) { + ci.cancel(); + } + } + + @ModifyConstant(method = "onPlayerCollision", constant = @Constant(intValue = 6000, ordinal = 0)) + private int onPlayerCollideCheckLifespan(int original) { + return lifespan; + } + + @Redirect(method = "onPlayerCollision", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/PlayerEntity;sendPickup(Lnet/minecraft/entity/Entity;I)V")) + private void onPlayerItemPickup(PlayerEntity entity, Entity item, int count) { + EntityEvents.firePlayerItemPickupEvent(entity, (ItemEntity) item, getStack().copy()); + } + + @Inject(method = "onPlayerCollision", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/ItemEntity;remove()V")) + private void sendPickup(PlayerEntity player, CallbackInfo ci) { + player.sendPickup((ItemEntity) (Object) this, getStack().getCount()); + } + + /* + @ModifyConstant(method = "setDespawnImmediately", constant = @Constant(intValue = 5999)) + private int setAge(World world) { + return ((IForgeItemStack) stack).getEntityLifespan(((ItemEntity) (Object) this).world) - 1; + } + */ } From 8c2332246ab53e9cedce4b487761ad96e885ef71 Mon Sep 17 00:00:00 2001 From: Rongmario Date: Fri, 3 Jul 2020 14:56:07 +0100 Subject: [PATCH 4/5] checkstyle pls --- .../event/entity/item/ItemEvent.java | 3 +- .../event/entity/item/ItemExpireEvent.java | 1 - .../impl/event/entity/EntityEvents.java | 5 +-- .../mixin/event/entity/MixinItemEntity.java | 45 +++++++++---------- 4 files changed, 26 insertions(+), 28 deletions(-) diff --git a/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/item/ItemEvent.java b/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/item/ItemEvent.java index de273e77..621958a7 100644 --- a/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/item/ItemEvent.java +++ b/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/item/ItemEvent.java @@ -19,9 +19,10 @@ package net.minecraftforge.event.entity.item; -import net.minecraft.entity.ItemEntity; import net.minecraftforge.event.entity.EntityEvent; +import net.minecraft.entity.ItemEntity; + /** * Base class for all ItemEntity events. Contains a reference to the * ItemEntity of interest. For most ItemEntity events, there's little to no diff --git a/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/item/ItemExpireEvent.java b/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/item/ItemExpireEvent.java index 0b0e7431..c1e5b2ee 100644 --- a/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/item/ItemExpireEvent.java +++ b/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/item/ItemExpireEvent.java @@ -29,7 +29,6 @@ */ public class ItemExpireEvent extends ItemEvent { - private int extraLife; /** diff --git a/patchwork-events-entity/src/main/java/net/patchworkmc/impl/event/entity/EntityEvents.java b/patchwork-events-entity/src/main/java/net/patchworkmc/impl/event/entity/EntityEvents.java index 56e15812..f580027d 100644 --- a/patchwork-events-entity/src/main/java/net/patchworkmc/impl/event/entity/EntityEvents.java +++ b/patchwork-events-entity/src/main/java/net/patchworkmc/impl/event/entity/EntityEvents.java @@ -63,8 +63,6 @@ import net.fabricmc.fabric.api.event.player.UseBlockCallback; import net.fabricmc.fabric.api.event.player.UseItemCallback; -import javax.annotation.Nonnull; - public class EntityEvents implements ModInitializer { private static final Logger LOGGER = LogManager.getLogger("patchwork-events-entity"); @@ -194,10 +192,11 @@ public static boolean onItemPickup(ItemEntity entityItem, PlayerEntity player) { return MinecraftForge.EVENT_BUS.post(new EntityItemPickupEvent(player, entityItem)); } - public static int onItemExpire(ItemEntity entity, @Nonnull ItemStack item) { + public static int onItemExpire(ItemEntity entity, ItemStack item) { if (item.isEmpty()) { return -1; } + ItemExpireEvent event = new ItemExpireEvent(entity, 6000); // TODO: ItemExpireEvent event = new ItemExpireEvent(entity, (item.isEmpty() ? 6000 : item.getItem().getEntityLifespan(item, entity.world))); diff --git a/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinItemEntity.java b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinItemEntity.java index b3506749..05e8b400 100644 --- a/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinItemEntity.java +++ b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinItemEntity.java @@ -19,25 +19,25 @@ package net.patchworkmc.mixin.event.entity; -import net.minecraft.world.World; import org.objectweb.asm.Opcodes; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Constant; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.ModifyConstant; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import net.minecraft.entity.Entity; import net.minecraft.entity.ItemEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.World; import net.patchworkmc.impl.event.entity.EntityEvents; -import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Constant; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.ModifyConstant; -import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(ItemEntity.class) public abstract class MixinItemEntity { @@ -52,20 +52,20 @@ public abstract class MixinItemEntity { // TODO -> Forge has exposed 'lifespan' (hardcoded to 6000) through IForgeItemStack/IForgeItem#getEntityLifespan /* - @Inject(method = "(Lnet/minecraft/world/World;DDDLnet/minecraft/item/ItemStack;)V", at = @At("RETURN")) - private void modifyLifespan(World world, double x, double y, double z, ItemStack stack, CallbackInfo ci) { - this.lifespan = stack.getItem() == null ? 6000 : ((IForgeItemStack) stack).getEntityLifespan(world); - } + @Inject(method = "(Lnet/minecraft/world/World;DDDLnet/minecraft/item/ItemStack;)V", at = @At("RETURN")) + private void modifyLifespan(World world, double x, double y, double z, ItemStack stack, CallbackInfo ci) { + this.lifespan = stack.getItem() == null ? 6000 : ((IForgeItemStack) stack).getEntityLifespan(world); + } */ // TODO -> Forge has a callback at IForgeItemStack/IForgeItem#onEntityItemUpdate /* - @Inject(method = "tick", at = @At("HEAD"), cancellable = true) - private void onUpdate(CallbackInfo ci) { - if ((IForgeItemStack) stack).onEntityItemUpdate((ItemEntity) (Object) this) { - ci.cancel(); + @Inject(method = "tick", at = @At("HEAD"), cancellable = true) + private void onUpdate(CallbackInfo ci) { + if ((IForgeItemStack) stack).onEntityItemUpdate((ItemEntity) (Object) this) { + ci.cancel(); + } } - } */ @ModifyConstant(method = "tick", constant = @Constant(intValue = 6000)) @@ -79,8 +79,7 @@ private void fireOnItemExpire(ItemEntity item) { if (hook < 0) { item.remove(); - } - else { + } else { this.lifespan += hook; } } @@ -125,9 +124,9 @@ private void sendPickup(PlayerEntity player, CallbackInfo ci) { } /* - @ModifyConstant(method = "setDespawnImmediately", constant = @Constant(intValue = 5999)) - private int setAge(World world) { - return ((IForgeItemStack) stack).getEntityLifespan(((ItemEntity) (Object) this).world) - 1; - } + @ModifyConstant(method = "setDespawnImmediately", constant = @Constant(intValue = 5999)) + private int setAge(World world) { + return ((IForgeItemStack) stack).getEntityLifespan(((ItemEntity) (Object) this).world) - 1; + } */ } From 297b1dd39a8601ef3c714b3797b8abb34e914fb2 Mon Sep 17 00:00:00 2001 From: Rongmario Date: Sat, 4 Jul 2020 01:20:35 +0100 Subject: [PATCH 5/5] Implemented ItemTossEvent + Start on IForgeEntity - Scope-creep'd a little out of the PR --- patchwork-events-entity/build.gradle | 1 + .../impl/event/entity/EntityEvents.java | 25 +++++++++++++++ .../mixin/event/entity/MixinEntity.java | 30 +++++++++++++++++- .../mixin/event/entity/MixinLivingEntity.java | 17 ++++++++++ .../event/entity/MixinServerPlayerEntity.java | 13 ++++++++ patchwork-extensions-entity/build.gradle | 2 ++ .../common/extensions/IForgeEntity.java | 30 ++++++++++++++++++ .../assets/patchwork-extensions-item/icon.png | Bin 0 -> 22738 bytes .../src/main/resources/fabric.mod.json | 28 ++++++++++++++++ .../patchwork-extensions-entity.mixins.json | 12 +++++++ settings.gradle | 1 + 11 files changed, 158 insertions(+), 1 deletion(-) create mode 100644 patchwork-extensions-entity/build.gradle create mode 100644 patchwork-extensions-entity/src/main/java/net/minecraftforge/common/extensions/IForgeEntity.java create mode 100644 patchwork-extensions-entity/src/main/resources/assets/patchwork-extensions-item/icon.png create mode 100644 patchwork-extensions-entity/src/main/resources/fabric.mod.json create mode 100644 patchwork-extensions-entity/src/main/resources/patchwork-extensions-entity.mixins.json diff --git a/patchwork-events-entity/build.gradle b/patchwork-events-entity/build.gradle index 057f81d7..3133e6b4 100644 --- a/patchwork-events-entity/build.gradle +++ b/patchwork-events-entity/build.gradle @@ -3,5 +3,6 @@ version = getSubprojectVersion(project, "0.4.0") dependencies { compile project(path: ':patchwork-fml', configuration: 'dev') + compile project(path: ':patchwork-extensions-entity', configuration: 'dev') compile project(path: ':patchwork-extensions-item', configuration: 'dev') } diff --git a/patchwork-events-entity/src/main/java/net/patchworkmc/impl/event/entity/EntityEvents.java b/patchwork-events-entity/src/main/java/net/patchworkmc/impl/event/entity/EntityEvents.java index f580027d..40415c77 100644 --- a/patchwork-events-entity/src/main/java/net/patchworkmc/impl/event/entity/EntityEvents.java +++ b/patchwork-events-entity/src/main/java/net/patchworkmc/impl/event/entity/EntityEvents.java @@ -19,11 +19,14 @@ package net.patchworkmc.impl.event.entity; +import com.google.common.collect.Lists; import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.common.extensions.IForgeEntity; import net.minecraftforge.common.extensions.IForgeItem; import net.minecraftforge.event.entity.EntityEvent; import net.minecraftforge.event.entity.EntityJoinWorldEvent; import net.minecraftforge.event.entity.item.ItemExpireEvent; +import net.minecraftforge.event.entity.item.ItemTossEvent; import net.minecraftforge.event.entity.living.LivingAttackEvent; import net.minecraftforge.event.entity.living.LivingDamageEvent; import net.minecraftforge.event.entity.living.LivingDeathEvent; @@ -207,6 +210,28 @@ public static int onItemExpire(ItemEntity entity, ItemStack item) { return event.getExtraLife(); } + public static ItemEntity onPlayerTossEvent(PlayerEntity player, ItemStack item, boolean includeName) { + ((IForgeEntity) player).captureDrops(Lists.newArrayList()); + ItemEntity ret = player.dropItem(item, false, includeName); + ((IForgeEntity) player).captureDrops(null); + + if (ret == null) { + return null; + } + + ItemTossEvent event = new ItemTossEvent(ret, player); + + if (MinecraftForge.EVENT_BUS.post(event)) { + return null; + } + + if (!player.world.isClient) { + player.getEntityWorld().spawnEntity(event.getEntityItem()); + } + + return event.getEntityItem(); + } + @Override public void onInitialize() { UseItemCallback.EVENT.register((player, world, hand) -> { diff --git a/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinEntity.java b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinEntity.java index 94513412..ed50dfdc 100644 --- a/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinEntity.java +++ b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinEntity.java @@ -19,22 +19,28 @@ package net.patchworkmc.mixin.event.entity; +import java.util.Collection; + +import net.minecraftforge.common.extensions.IForgeEntity; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityDimensions; import net.minecraft.entity.EntityPose; import net.minecraft.entity.EntityType; +import net.minecraft.entity.ItemEntity; import net.minecraft.world.World; import net.patchworkmc.impl.event.entity.EntityEvents; @Mixin(Entity.class) -public abstract class MixinEntity { +public abstract class MixinEntity implements IForgeEntity { @Shadow private float standingEyeHeight; @@ -44,6 +50,9 @@ public abstract class MixinEntity { @Shadow protected abstract float getEyeHeight(EntityPose pose, EntityDimensions dimensions); + @Unique + private Collection captureDrops = null; + @Inject(method = "", at = @At("RETURN")) public void hookConstructor(EntityType type, World world, CallbackInfo ci) { Entity entity = (Entity) (Object) this; @@ -52,4 +61,23 @@ public void hookConstructor(EntityType type, World world, CallbackInfo ci) { EntityEvents.onEntityConstruct(entity); } + + @Redirect(method = "dropStack(Lnet/minecraft/item/ItemStack;F)Lnet/minecraft/entity/ItemEntity;", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/ItemEntity;setToDefaultPickupDelay()V")) + private void addToCaptureDrops(ItemEntity entity) { + if (this.captureDrops != null) { + this.captureDrops.add(entity); + } + } + + @Override + public Collection captureDrops() { + return captureDrops; + } + + @Override + public Collection captureDrops(Collection replacement) { + Collection cache = this.captureDrops; + this.captureDrops = replacement; + return cache; + } } diff --git a/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinLivingEntity.java b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinLivingEntity.java index 2c38f304..f23ba83e 100644 --- a/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinLivingEntity.java +++ b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinLivingEntity.java @@ -109,4 +109,21 @@ private float hookApplyDamageForDamageEvent(float damage, DamageSource source) { return EntityEvents.onLivingDamage(entity, source, damage); } + + // TODO: Commented out until LivingDropsEvent is implemented + /* + @Inject(method = "drop", at = @At(value = "FIELD", opcode = Opcodes.GETFIELD, target = "Lnet/minecraft/entity/LivingEntity;playerHitTimer:I")) + private void beginCaptureDrops(DamageSource source, CallbackInfo ci) { + ((CaptureDropSupplier) this).captureDrops(Lists.newArrayList()); + } + + @Inject(method = "drop", at = @At("TAIL")) + private void beginCaptureDrops(CallbackInfo ci) { + Collection drops = ((CaptureDropSupplier) this).captureDrops(null); + + if (!ForgeHooks.onLivingDrops(this, p_213345_1_, drops, i, recentlyHit > 0)) { + drops.forEach(e -> world.addEntity(e)); + } + } + */ } diff --git a/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinServerPlayerEntity.java b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinServerPlayerEntity.java index 90bfc860..652af5e6 100644 --- a/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinServerPlayerEntity.java +++ b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinServerPlayerEntity.java @@ -20,14 +20,20 @@ package net.patchworkmc.mixin.event.entity; import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.common.extensions.IForgeEntity; import net.minecraftforge.event.entity.player.PlayerEvent; +import org.objectweb.asm.Opcodes; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; +import net.minecraft.entity.ItemEntity; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.damage.DamageSource; +import net.minecraft.item.ItemStack; import net.minecraft.server.network.ServerPlayerEntity; import net.patchworkmc.impl.event.entity.EntityEvents; @@ -49,4 +55,11 @@ private void hookCopyFromForCloneEvent(ServerPlayerEntity oldPlayer, boolean ali ServerPlayerEntity speThis = (ServerPlayerEntity) (Object) this; MinecraftForge.EVENT_BUS.post(new PlayerEvent.Clone(speThis, oldPlayer, !alive)); } + + @Inject(method = "dropItem", at = @At(value = "FIELD", opcode = Opcodes.GETFIELD, shift = At.Shift.BEFORE), locals = LocalCapture.CAPTURE_FAILHARD) + private void addToCaptureDrops(ItemStack stack, boolean drop, boolean trace, CallbackInfoReturnable cir, ItemEntity entity) { + if (((IForgeEntity) entity).captureDrops() != null) { + ((IForgeEntity) entity).captureDrops().add(entity); + } + } } diff --git a/patchwork-extensions-entity/build.gradle b/patchwork-extensions-entity/build.gradle new file mode 100644 index 00000000..ee5ce8bd --- /dev/null +++ b/patchwork-extensions-entity/build.gradle @@ -0,0 +1,2 @@ +archivesBaseName = "patchwork-extensions-entity" +version = getSubprojectVersion(project, "0.1.0") diff --git a/patchwork-extensions-entity/src/main/java/net/minecraftforge/common/extensions/IForgeEntity.java b/patchwork-extensions-entity/src/main/java/net/minecraftforge/common/extensions/IForgeEntity.java new file mode 100644 index 00000000..a125079f --- /dev/null +++ b/patchwork-extensions-entity/src/main/java/net/minecraftforge/common/extensions/IForgeEntity.java @@ -0,0 +1,30 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * 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.extensions; + +import java.util.Collection; + +import net.minecraft.entity.ItemEntity; + +public interface IForgeEntity { + Collection captureDrops(); + + Collection captureDrops(Collection value); +} diff --git a/patchwork-extensions-entity/src/main/resources/assets/patchwork-extensions-item/icon.png b/patchwork-extensions-entity/src/main/resources/assets/patchwork-extensions-item/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..de75d2fbe9467f986e4f3072e76fa751dbd72879 GIT binary patch literal 22738 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 literal 0 HcmV?d00001 diff --git a/patchwork-extensions-entity/src/main/resources/fabric.mod.json b/patchwork-extensions-entity/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..ef112e7a --- /dev/null +++ b/patchwork-extensions-entity/src/main/resources/fabric.mod.json @@ -0,0 +1,28 @@ +{ + "schemaVersion": 1, + "id": "patchwork-extensions-entity", + "name": "Patchwork Entity Extensions", + "version": "${version}", + "description": "Implements the Minecraft Forge extensions API for entities", + "environment": "*", + "license": "LGPL-2.1-only", + "icon": "assets/patchwork-extensions-entity/icon.png", + "contact": { + "issues": "https://github.com/PatchworkMC/patchwork-api/issues", + "sources": "https://github.com/PatchworkMC/patchwork-api" + }, + "authors": [ + "PatchworkMC" + ], + "depends": { + "fabricloader": ">=0.8.4", + "fabric": "*" + }, + "mixins": [ + "patchwork-extensions-entity.mixins.json" + ], + "custom": { + "modmenu:api": true, + "modmenu:parent": "patchwork" + } +} diff --git a/patchwork-extensions-entity/src/main/resources/patchwork-extensions-entity.mixins.json b/patchwork-extensions-entity/src/main/resources/patchwork-extensions-entity.mixins.json new file mode 100644 index 00000000..46ed1bc6 --- /dev/null +++ b/patchwork-extensions-entity/src/main/resources/patchwork-extensions-entity.mixins.json @@ -0,0 +1,12 @@ +{ + "required": true, + "package": "net.patchworkmc.mixin.extensions.entity", + "compatibilityLevel": "JAVA_8", + "mixins": [ + ], + "client": [ + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/settings.gradle b/settings.gradle index 23688998..665af8ba 100644 --- a/settings.gradle +++ b/settings.gradle @@ -24,6 +24,7 @@ include 'patchwork-events-rendering' include 'patchwork-events-world' include 'patchwork-extensions' include 'patchwork-extensions-block' +include 'patchwork-extensions-entity' include 'patchwork-extensions-item' include 'patchwork-extensions-shearing' include 'patchwork-fml'