Search in sources :

Example 1 with ItemDropData

use of org.spongepowered.common.event.tracking.context.ItemDropData in project SpongeCommon by SpongePowered.

the class EntityUtil method playerDropItem.

@Nullable
public static EntityItem playerDropItem(IMixinEntityPlayer mixinPlayer, ItemStack droppedItem, boolean dropAround, boolean traceItem) {
    mixinPlayer.shouldRestoreInventory(false);
    final EntityPlayer player = EntityUtil.toNative(mixinPlayer);
    final double posX = player.posX;
    final double adjustedPosY = player.posY - 0.3 + player.getEyeHeight();
    final double posZ = player.posZ;
    // Now the real fun begins.
    final ItemStack item;
    try (CauseStackManager.StackFrame frame = Sponge.getCauseStackManager().pushCauseFrame()) {
        Sponge.getCauseStackManager().pushCause(player);
        // FIRST we want to throw the DropItemEvent.PRE
        final ItemStackSnapshot snapshot = ItemStackUtil.snapshotOf(droppedItem);
        final List<ItemStackSnapshot> original = new ArrayList<>();
        original.add(snapshot);
        final DropItemEvent.Pre dropEvent = SpongeEventFactory.createDropItemEventPre(Sponge.getCauseStackManager().getCurrentCause(), ImmutableList.of(snapshot), original);
        SpongeImpl.postEvent(dropEvent);
        if (dropEvent.isCancelled()) {
            mixinPlayer.shouldRestoreInventory(true);
            return null;
        }
        if (dropEvent.getDroppedItems().isEmpty()) {
            return null;
        }
        // SECOND throw the ConstructEntityEvent
        Transform<World> suggested = new Transform<>(mixinPlayer.getWorld(), new Vector3d(posX, adjustedPosY, posZ));
        Sponge.getCauseStackManager().addContext(EventContextKeys.SPAWN_TYPE, SpawnTypes.DROPPED_ITEM);
        ConstructEntityEvent.Pre event = SpongeEventFactory.createConstructEntityEventPre(Sponge.getCauseStackManager().getCurrentCause(), EntityTypes.ITEM, suggested);
        SpongeImpl.postEvent(event);
        if (event.isCancelled()) {
            mixinPlayer.shouldRestoreInventory(true);
            return null;
        }
        item = event.isCancelled() ? null : ItemStackUtil.fromSnapshotToNative(dropEvent.getDroppedItems().get(0));
        if (item == null) {
            mixinPlayer.shouldRestoreInventory(true);
            return null;
        }
        final PhaseData peek = PhaseTracker.getInstance().getCurrentPhaseData();
        final IPhaseState currentState = peek.state;
        final PhaseContext<?> phaseContext = peek.context;
        if (!currentState.ignoresItemPreMerging() && SpongeImpl.getGlobalConfig().getConfig().getOptimizations().doDropsPreMergeItemDrops()) {
            final Collection<ItemDropData> itemStacks;
            if (currentState.tracksEntitySpecificDrops()) {
                final Multimap<UUID, ItemDropData> multimap = phaseContext.getCapturedEntityDropSupplier().get();
                itemStacks = multimap.get(player.getUniqueID());
            } else {
                itemStacks = phaseContext.getCapturedItemStackSupplier().get();
            }
            SpongeImplHooks.addItemStackToListForSpawning(itemStacks, ItemDropData.Player.player(player).stack(item).trace(traceItem).motion(createDropMotion(dropAround, player, mixinPlayer.getRandom())).dropAround(dropAround).position(new Vector3d(posX, adjustedPosY, posZ)).build());
            return null;
        }
        EntityItem entityitem = new EntityItem(player.world, posX, adjustedPosY, posZ, droppedItem);
        entityitem.setPickupDelay(40);
        if (traceItem) {
            entityitem.setThrower(player.getName());
        }
        final Random random = mixinPlayer.getRandom();
        if (dropAround) {
            float f = random.nextFloat() * 0.5F;
            float f1 = random.nextFloat() * ((float) Math.PI * 2F);
            entityitem.motionX = -MathHelper.sin(f1) * f;
            entityitem.motionZ = MathHelper.cos(f1) * f;
            entityitem.motionY = 0.20000000298023224D;
        } else {
            float f2 = 0.3F;
            entityitem.motionX = -MathHelper.sin(player.rotationYaw * 0.017453292F) * MathHelper.cos(player.rotationPitch * 0.017453292F) * f2;
            entityitem.motionZ = MathHelper.cos(player.rotationYaw * 0.017453292F) * MathHelper.cos(player.rotationPitch * 0.017453292F) * f2;
            entityitem.motionY = -MathHelper.sin(player.rotationPitch * 0.017453292F) * f2 + 0.1F;
            float f3 = random.nextFloat() * ((float) Math.PI * 2F);
            f2 = 0.02F * random.nextFloat();
            entityitem.motionX += Math.cos(f3) * f2;
            entityitem.motionY += (random.nextFloat() - random.nextFloat()) * 0.1F;
            entityitem.motionZ += Math.sin(f3) * f2;
        }
        // FIFTH - Capture the entity maybe?
        if (currentState.doesCaptureEntityDrops()) {
            if (currentState.tracksEntitySpecificDrops()) {
                // We are capturing per entity drop
                phaseContext.getCapturedEntityItemDropSupplier().get().put(player.getUniqueID(), entityitem);
            } else {
                // We are adding to a general list - usually for EntityPhase.State.DEATH
                phaseContext.getCapturedItemsSupplier().get().add(entityitem);
            }
            // Return the item, even if it wasn't spawned in the world.
            return entityitem;
        }
        ItemStack itemstack = dropItemAndGetStack(player, entityitem);
        if (traceItem) {
            if (!itemstack.isEmpty()) {
                player.addStat(StatList.getDroppedObjectStats(itemstack.getItem()), droppedItem.getCount());
            }
            player.addStat(StatList.DROP);
        }
        return entityitem;
    }
}
Also used : ArrayList(java.util.ArrayList) World(org.spongepowered.api.world.World) Random(java.util.Random) CauseStackManager(org.spongepowered.api.event.CauseStackManager) UUID(java.util.UUID) EntityItem(net.minecraft.entity.item.EntityItem) DropItemEvent(org.spongepowered.api.event.item.inventory.DropItemEvent) ConstructEntityEvent(org.spongepowered.api.event.entity.ConstructEntityEvent) Vector3d(com.flowpowered.math.vector.Vector3d) IMixinEntityPlayer(org.spongepowered.common.interfaces.entity.player.IMixinEntityPlayer) EntityPlayer(net.minecraft.entity.player.EntityPlayer) ItemStackSnapshot(org.spongepowered.api.item.inventory.ItemStackSnapshot) ItemStack(net.minecraft.item.ItemStack) Transform(org.spongepowered.api.entity.Transform) ItemDropData(org.spongepowered.common.event.tracking.context.ItemDropData) Nullable(javax.annotation.Nullable)

Example 2 with ItemDropData

use of org.spongepowered.common.event.tracking.context.ItemDropData in project SpongeCommon by SpongePowered.

the class TrackingUtil method spawnItemDataForBlockDrops.

public static void spawnItemDataForBlockDrops(Collection<ItemDropData> itemStacks, BlockSnapshot oldBlockSnapshot, PhaseContext<?> phaseContext, IPhaseState<?> state) {
    final Vector3i position = oldBlockSnapshot.getPosition();
    final List<ItemStackSnapshot> itemSnapshots = itemStacks.stream().map(ItemDropData::getStack).map(ItemStackUtil::snapshotOf).collect(Collectors.toList());
    final ImmutableList<ItemStackSnapshot> originalSnapshots = ImmutableList.copyOf(itemSnapshots);
    Sponge.getCauseStackManager().pushCause(oldBlockSnapshot);
    final DropItemEvent.Pre dropItemEventPre = SpongeEventFactory.createDropItemEventPre(Sponge.getCauseStackManager().getCurrentCause(), originalSnapshots, itemSnapshots);
    Sponge.getCauseStackManager().popCause();
    SpongeImpl.postEvent(dropItemEventPre);
    if (dropItemEventPre.isCancelled()) {
        return;
    }
    final World world = oldBlockSnapshot.getLocation().get().getExtent();
    final WorldServer worldServer = (WorldServer) world;
    // Now we can spawn the entity items appropriately
    final List<Entity> itemDrops = itemStacks.stream().map(itemStack -> {
        final net.minecraft.item.ItemStack minecraftStack = itemStack.getStack();
        float f = 0.5F;
        double offsetX = worldServer.rand.nextFloat() * f + (1.0F - f) * 0.5D;
        double offsetY = worldServer.rand.nextFloat() * f + (1.0F - f) * 0.5D;
        double offsetZ = worldServer.rand.nextFloat() * f + (1.0F - f) * 0.5D;
        final double x = position.getX() + offsetX;
        final double y = position.getY() + offsetY;
        final double z = position.getZ() + offsetZ;
        EntityItem entityitem = new EntityItem(worldServer, x, y, z, minecraftStack);
        entityitem.setDefaultPickupDelay();
        return entityitem;
    }).map(EntityUtil::fromNative).collect(Collectors.toList());
    try (CauseStackManager.StackFrame frame = Sponge.getCauseStackManager().pushCauseFrame()) {
        Sponge.getCauseStackManager().pushCause(oldBlockSnapshot);
        Sponge.getCauseStackManager().addContext(EventContextKeys.SPAWN_TYPE, InternalSpawnTypes.DROPPED_ITEM);
        if (phaseContext.getNotifier().isPresent()) {
            Sponge.getCauseStackManager().addContext(EventContextKeys.NOTIFIER, phaseContext.getNotifier().get());
        }
        final User entityCreator = phaseContext.getNotifier().orElseGet(() -> phaseContext.getOwner().orElse(null));
        final DropItemEvent.Destruct destruct = SpongeEventFactory.createDropItemEventDestruct(Sponge.getCauseStackManager().getCurrentCause(), itemDrops);
        SpongeImpl.postEvent(destruct);
        if (!destruct.isCancelled()) {
            for (Entity entity : destruct.getEntities()) {
                if (entityCreator != null) {
                    EntityUtil.toMixin(entity).setCreator(entityCreator.getUniqueId());
                }
                EntityUtil.getMixinWorld(entity).forceSpawnEntity(entity);
            }
        }
    }
}
Also used : DropItemEvent(org.spongepowered.api.event.item.inventory.DropItemEvent) IMixinEntity(org.spongepowered.common.interfaces.entity.IMixinEntity) TileEntity(org.spongepowered.api.block.tileentity.TileEntity) IMixinTileEntity(org.spongepowered.common.interfaces.block.tile.IMixinTileEntity) Entity(org.spongepowered.api.entity.Entity) User(org.spongepowered.api.entity.living.player.User) StackFrame(org.spongepowered.api.event.CauseStackManager.StackFrame) IMixinWorldServer(org.spongepowered.common.interfaces.world.IMixinWorldServer) WorldServer(net.minecraft.world.WorldServer) World(org.spongepowered.api.world.World) CauseStackManager(org.spongepowered.api.event.CauseStackManager) Vector3i(com.flowpowered.math.vector.Vector3i) ItemStackSnapshot(org.spongepowered.api.item.inventory.ItemStackSnapshot) ItemDropData(org.spongepowered.common.event.tracking.context.ItemDropData) EntityItem(net.minecraft.entity.item.EntityItem)

Example 3 with ItemDropData

use of org.spongepowered.common.event.tracking.context.ItemDropData in project SpongeCommon by SpongePowered.

the class TrackingUtil method performBlockAdditions.

@SuppressWarnings("rawtypes")
public static boolean performBlockAdditions(List<Transaction<BlockSnapshot>> transactions, IPhaseState<?> phaseState, PhaseContext<?> phaseContext, boolean noCancelledTransactions) {
    // We have to use a proxy so that our pending changes are notified such that any accessors from block
    // classes do not fail on getting the incorrect block state from the IBlockAccess
    // final SpongeProxyBlockAccess proxyBlockAccess = new SpongeProxyBlockAccess(transactions);
    final CapturedMultiMapSupplier<BlockPos, ItemDropData> capturedBlockDrops = phaseContext.getBlockDropSupplier();
    final CapturedMultiMapSupplier<BlockPos, EntityItem> capturedBlockItemEntityDrops = phaseContext.getBlockItemDropSupplier();
    final CapturedMultiMapSupplier<BlockPos, net.minecraft.entity.Entity> capturedBlockEntitySpawns = phaseContext.getBlockEntitySpawnSupplier();
    for (Transaction<BlockSnapshot> transaction : transactions) {
        if (!transaction.isValid()) {
            // Rememver that this value needs to be set to false to return because of the fact that
            // a transaction was marked as invalid or cancelled. This is used primarily for
            // things like portal creation, and if false, removes the portal from the cache
            noCancelledTransactions = false;
            // Don't use invalidated block transactions during notifications, these only need to be restored
            continue;
        }
        // Handle custom replacements
        if (transaction.getCustom().isPresent()) {
            transaction.getFinal().restore(true, BlockChangeFlags.NONE);
        }
        final SpongeBlockSnapshot oldBlockSnapshot = (SpongeBlockSnapshot) transaction.getOriginal();
        final SpongeBlockSnapshot newBlockSnapshot = (SpongeBlockSnapshot) transaction.getFinal();
        final Location<World> worldLocation = oldBlockSnapshot.getLocation().get();
        final IMixinWorldServer mixinWorldServer = (IMixinWorldServer) worldLocation.getExtent();
        // Handle item drops captured
        final BlockPos pos = ((IMixinLocation) (Object) oldBlockSnapshot.getLocation().get()).getBlockPos();
        // This is for pre-merged items
        capturedBlockDrops.acceptAndRemoveIfPresent(pos, items -> spawnItemDataForBlockDrops(items, oldBlockSnapshot, phaseContext, phaseState));
        // And this is for un-pre-merged items, these will be EntityItems, not ItemDropDatas.
        capturedBlockItemEntityDrops.acceptAndRemoveIfPresent(pos, items -> spawnItemEntitiesForBlockDrops(items, oldBlockSnapshot, phaseContext, phaseState));
        // This is for entities actually spawned
        capturedBlockEntitySpawns.acceptAndRemoveIfPresent(pos, items -> spawnEntitiesForBlock(items, oldBlockSnapshot, phaseContext, phaseState));
        final WorldServer worldServer = mixinWorldServer.asMinecraftWorld();
        SpongeHooks.logBlockAction(worldServer, oldBlockSnapshot.blockChange, transaction);
        final SpongeBlockChangeFlag changeFlag = oldBlockSnapshot.getChangeFlag();
        final IBlockState originalState = (IBlockState) oldBlockSnapshot.getState();
        final IBlockState newState = (IBlockState) newBlockSnapshot.getState();
        // We call onBlockAdded here for blocks without a TileEntity.
        // MixinChunk#setBlockState will call onBlockAdded for blocks
        // with a TileEntity or when capturing is not being done.
        final PhaseTracker phaseTracker = PhaseTracker.getInstance();
        if (!SpongeImplHooks.hasBlockTileEntity(newState.getBlock(), newState) && changeFlag.performBlockPhysics() && originalState.getBlock() != newState.getBlock()) {
            newState.getBlock().onBlockAdded(worldServer, pos, newState);
            final PhaseData peek = phaseTracker.getCurrentPhaseData();
            if (peek.state == GeneralPhase.Post.UNWINDING) {
                ((IPhaseState) peek.state).unwind(peek.context);
            }
        }
        // proxyBlockAccess.proceed();
        ((IPhaseState) phaseState).handleBlockChangeWithUser(oldBlockSnapshot.blockChange, transaction, phaseContext);
        if (changeFlag.isNotifyClients()) {
            // Always try to notify clients of the change.
            worldServer.notifyBlockUpdate(pos, originalState, newState, changeFlag.getRawFlag());
        }
        if (changeFlag.updateNeighbors()) {
            // Notify neighbors only if the change flag allowed it.
            mixinWorldServer.spongeNotifyNeighborsPostBlockChange(pos, originalState, newState, changeFlag);
        } else if (changeFlag.notifyObservers()) {
            worldServer.updateObservingBlocksAt(pos, newState.getBlock());
        }
        final PhaseData peek = phaseTracker.getCurrentPhaseData();
        if (peek.state == GeneralPhase.Post.UNWINDING) {
            ((IPhaseState) peek.state).unwind(peek.context);
        }
    }
    return noCancelledTransactions;
}
Also used : IMixinEntity(org.spongepowered.common.interfaces.entity.IMixinEntity) TileEntity(org.spongepowered.api.block.tileentity.TileEntity) IMixinTileEntity(org.spongepowered.common.interfaces.block.tile.IMixinTileEntity) Entity(org.spongepowered.api.entity.Entity) SpongeBlockChangeFlag(org.spongepowered.common.world.SpongeBlockChangeFlag) IBlockState(net.minecraft.block.state.IBlockState) IMixinLocation(org.spongepowered.common.interfaces.world.IMixinLocation) SpongeBlockSnapshot(org.spongepowered.common.block.SpongeBlockSnapshot) BlockSnapshot(org.spongepowered.api.block.BlockSnapshot) IMixinWorldServer(org.spongepowered.common.interfaces.world.IMixinWorldServer) IMixinWorldServer(org.spongepowered.common.interfaces.world.IMixinWorldServer) WorldServer(net.minecraft.world.WorldServer) World(org.spongepowered.api.world.World) SpongeBlockSnapshot(org.spongepowered.common.block.SpongeBlockSnapshot) BlockPos(net.minecraft.util.math.BlockPos) ItemDropData(org.spongepowered.common.event.tracking.context.ItemDropData) EntityItem(net.minecraft.entity.item.EntityItem)

Example 4 with ItemDropData

use of org.spongepowered.common.event.tracking.context.ItemDropData in project SpongeCommon by SpongePowered.

the class BlockDropItemsPhaseState method unwind.

@SuppressWarnings("unchecked")
@Override
public void unwind(GeneralizedContext phaseContext) {
    final BlockSnapshot blockSnapshot = phaseContext.getSource(BlockSnapshot.class).orElseThrow(TrackingUtil.throwWithContext("Could not find a block dropping items!", phaseContext));
    try (CauseStackManager.StackFrame frame = Sponge.getCauseStackManager().pushCauseFrame()) {
        Sponge.getCauseStackManager().pushCause(blockSnapshot);
        Sponge.getCauseStackManager().addContext(EventContextKeys.SPAWN_TYPE, InternalSpawnTypes.DROPPED_ITEM);
        if (phaseContext.getNotifier().isPresent()) {
            Sponge.getCauseStackManager().addContext(EventContextKeys.NOTIFIER, phaseContext.getNotifier().get());
        }
        if (phaseContext.getOwner().isPresent()) {
            Sponge.getCauseStackManager().addContext(EventContextKeys.OWNER, phaseContext.getOwner().get());
        }
        phaseContext.getCapturedItemsSupplier().acceptAndClearIfNotEmpty(items -> {
            final ArrayList<Entity> entities = new ArrayList<>();
            for (EntityItem item : items) {
                entities.add(EntityUtil.fromNative(item));
            }
            final DropItemEvent.Destruct event = SpongeEventFactory.createDropItemEventDestruct(Sponge.getCauseStackManager().getCurrentCause(), entities);
            SpongeImpl.postEvent(event);
            if (!event.isCancelled()) {
                for (Entity entity : event.getEntities()) {
                    EntityUtil.getMixinWorld(entity).forceSpawnEntity(entity);
                }
            }
        });
        phaseContext.getCapturedEntitySupplier().acceptAndClearIfNotEmpty(entities -> {
            final SpawnEntityEvent event = SpongeEventFactory.createSpawnEntityEvent(Sponge.getCauseStackManager().getCurrentCause(), entities);
            SpongeImpl.postEvent(event);
            if (!event.isCancelled()) {
                for (Entity entity : event.getEntities()) {
                    EntityUtil.getMixinWorld(entity).forceSpawnEntity(entity);
                }
            }
            entities.clear();
        });
        final Location<World> worldLocation = blockSnapshot.getLocation().get();
        final IMixinWorldServer mixinWorld = ((IMixinWorldServer) worldLocation.getExtent());
        Sponge.getCauseStackManager().addContext(EventContextKeys.SPAWN_TYPE, SpawnTypes.BLOCK_SPAWNING);
        phaseContext.getCapturedBlockSupplier().acceptAndClearIfNotEmpty(blocks -> TrackingUtil.processBlockCaptures(blocks, this, phaseContext));
        phaseContext.getCapturedItemStackSupplier().acceptAndClearIfNotEmpty(drops -> {
            final List<EntityItem> items = drops.stream().map(drop -> drop.create(mixinWorld.asMinecraftWorld())).collect(Collectors.toList());
            final List<Entity> entities = (List<Entity>) (List<?>) items;
            if (!entities.isEmpty()) {
                DropItemEvent.Custom event = SpongeEventFactory.createDropItemEventCustom(Sponge.getCauseStackManager().getCurrentCause(), entities);
                SpongeImpl.postEvent(event);
                if (!event.isCancelled()) {
                    for (Entity droppedItem : event.getEntities()) {
                        mixinWorld.forceSpawnEntity(droppedItem);
                    }
                }
            }
            drops.clear();
        });
        phaseContext.getBlockDropSupplier().acceptAndClearIfNotEmpty(drops -> {
            for (BlockPos key : drops.asMap().keySet()) {
                final List<ItemDropData> values = drops.get(key);
                if (!values.isEmpty()) {
                    TrackingUtil.spawnItemDataForBlockDrops(values, blockSnapshot, phaseContext, this);
                }
            }
        });
    }
}
Also used : EntityItem(net.minecraft.entity.item.EntityItem) SpongeImpl(org.spongepowered.common.SpongeImpl) Location(org.spongepowered.api.world.Location) EventContextKeys(org.spongepowered.api.event.cause.EventContextKeys) SpongeEventFactory(org.spongepowered.api.event.SpongeEventFactory) Sponge(org.spongepowered.api.Sponge) BlockPos(net.minecraft.util.math.BlockPos) SpawnEntityEvent(org.spongepowered.api.event.entity.SpawnEntityEvent) IMixinWorldServer(org.spongepowered.common.interfaces.world.IMixinWorldServer) DropItemEvent(org.spongepowered.api.event.item.inventory.DropItemEvent) InternalSpawnTypes(org.spongepowered.common.registry.type.event.InternalSpawnTypes) Entity(org.spongepowered.api.entity.Entity) Collectors(java.util.stream.Collectors) EntityUtil(org.spongepowered.common.entity.EntityUtil) ArrayList(java.util.ArrayList) SpawnTypes(org.spongepowered.api.event.cause.entity.spawn.SpawnTypes) List(java.util.List) TrackingUtil(org.spongepowered.common.event.tracking.TrackingUtil) GeneralizedContext(org.spongepowered.common.event.tracking.context.GeneralizedContext) World(org.spongepowered.api.world.World) ItemDropData(org.spongepowered.common.event.tracking.context.ItemDropData) CauseStackManager(org.spongepowered.api.event.CauseStackManager) BlockSnapshot(org.spongepowered.api.block.BlockSnapshot) Entity(org.spongepowered.api.entity.Entity) DropItemEvent(org.spongepowered.api.event.item.inventory.DropItemEvent) BlockSnapshot(org.spongepowered.api.block.BlockSnapshot) ArrayList(java.util.ArrayList) IMixinWorldServer(org.spongepowered.common.interfaces.world.IMixinWorldServer) World(org.spongepowered.api.world.World) SpawnEntityEvent(org.spongepowered.api.event.entity.SpawnEntityEvent) CauseStackManager(org.spongepowered.api.event.CauseStackManager) ArrayList(java.util.ArrayList) List(java.util.List) BlockPos(net.minecraft.util.math.BlockPos) ItemDropData(org.spongepowered.common.event.tracking.context.ItemDropData) EntityItem(net.minecraft.entity.item.EntityItem)

Example 5 with ItemDropData

use of org.spongepowered.common.event.tracking.context.ItemDropData in project SpongeCommon by SpongePowered.

the class DeathPhase method unwind.

@Override
public void unwind(BasicEntityContext context) {
    final Entity dyingEntity = context.getSource(Entity.class).orElseThrow(TrackingUtil.throwWithContext("Dying entity not found!", context));
    final DamageSource damageSource = context.getDamageSource();
    try (StackFrame frame = Sponge.getCauseStackManager().pushCauseFrame()) {
        Sponge.getCauseStackManager().pushCause(damageSource);
        Sponge.getCauseStackManager().pushCause(dyingEntity);
        final boolean isPlayer = dyingEntity instanceof EntityPlayer;
        final EntityPlayer entityPlayer = isPlayer ? (EntityPlayer) dyingEntity : null;
        final Optional<User> notifier = context.getNotifier();
        final Optional<User> owner = context.getOwner();
        final User entityCreator = notifier.orElseGet(() -> owner.orElse(null));
        context.getCapturedEntitySupplier().acceptAndClearIfNotEmpty(entities -> {
            // Separate experience orbs from other entity drops
            final List<Entity> experience = entities.stream().filter(entity -> entity instanceof ExperienceOrb).collect(Collectors.toList());
            if (!experience.isEmpty()) {
                Sponge.getCauseStackManager().addContext(EventContextKeys.SPAWN_TYPE, InternalSpawnTypes.EXPERIENCE);
                final SpawnEntityEvent spawnEntityEvent = SpongeEventFactory.createSpawnEntityEvent(Sponge.getCauseStackManager().getCurrentCause(), experience);
                SpongeImpl.postEvent(spawnEntityEvent);
                if (!spawnEntityEvent.isCancelled()) {
                    for (Entity entity : spawnEntityEvent.getEntities()) {
                        EntityUtil.getMixinWorld(entity).forceSpawnEntity(entity);
                    }
                }
            }
            // Now process other entities, this is separate from item drops specifically
            final List<Entity> other = entities.stream().filter(entity -> !(entity instanceof ExperienceOrb)).collect(Collectors.toList());
            if (!other.isEmpty()) {
                Sponge.getCauseStackManager().addContext(EventContextKeys.SPAWN_TYPE, InternalSpawnTypes.ENTITY_DEATH);
                final SpawnEntityEvent spawnEntityEvent = SpongeEventFactory.createSpawnEntityEvent(Sponge.getCauseStackManager().getCurrentCause(), experience);
                SpongeImpl.postEvent(spawnEntityEvent);
                if (!spawnEntityEvent.isCancelled()) {
                    for (Entity entity : spawnEntityEvent.getEntities()) {
                        EntityUtil.getMixinWorld(entity).forceSpawnEntity(entity);
                    }
                }
            }
        });
        Sponge.getCauseStackManager().addContext(EventContextKeys.SPAWN_TYPE, InternalSpawnTypes.DROPPED_ITEM);
        // This allows mods such as Draconic Evolution to add items to the drop list
        if (context.getCapturedEntityItemDropSupplier().isEmpty() && context.getCapturedEntityDropSupplier().isEmpty()) {
            final ArrayList<Entity> entities = new ArrayList<>();
            final DropItemEvent.Destruct destruct = SpongeEventFactory.createDropItemEventDestruct(frame.getCurrentCause(), entities);
            SpongeImpl.postEvent(destruct);
            if (!destruct.isCancelled()) {
                for (Entity entity : destruct.getEntities()) {
                    EntityUtil.getMixinWorld(entity).forceSpawnEntity(entity);
                }
            }
            return;
        }
        context.getCapturedEntityItemDropSupplier().acceptAndRemoveIfPresent(dyingEntity.getUniqueId(), items -> {
            final ArrayList<Entity> entities = new ArrayList<>();
            for (EntityItem item : items) {
                entities.add(EntityUtil.fromNative(item));
            }
            if (isPlayer) {
                // Forge and Vanilla always clear items on player death BEFORE drops occur
                // This will also provide the highest compatibility with mods such as Tinkers Construct
                entityPlayer.inventory.clear();
            }
            final DropItemEvent.Destruct destruct = SpongeEventFactory.createDropItemEventDestruct(Sponge.getCauseStackManager().getCurrentCause(), entities);
            SpongeImpl.postEvent(destruct);
            if (!destruct.isCancelled()) {
                for (Entity entity : destruct.getEntities()) {
                    EntityUtil.getMixinWorld(entity).forceSpawnEntity(entity);
                }
            }
        // Note: If cancelled, the items do not spawn in the world and are NOT copied back to player inventory.
        // This avoids many issues with mods such as Tinkers Construct's soulbound items.
        });
        // Note that this is only used if and when item pre-merging is enabled. Which is never enabled in forge.
        context.getCapturedEntityDropSupplier().acceptAndRemoveIfPresent(dyingEntity.getUniqueId(), itemStacks -> {
            final List<ItemDropData> items = new ArrayList<>();
            items.addAll(itemStacks);
            if (!items.isEmpty()) {
                final net.minecraft.entity.Entity minecraftEntity = EntityUtil.toNative(dyingEntity);
                final List<Entity> itemEntities = items.stream().map(data -> data.create((WorldServer) minecraftEntity.world)).map(EntityUtil::fromNative).collect(Collectors.toList());
                if (isPlayer) {
                    // Forge and Vanilla always clear items on player death BEFORE drops occur
                    // This will also provide the highest compatibility with mods such as Tinkers Construct
                    entityPlayer.inventory.clear();
                }
                final DropItemEvent.Destruct destruct = SpongeEventFactory.createDropItemEventDestruct(Sponge.getCauseStackManager().getCurrentCause(), itemEntities);
                SpongeImpl.postEvent(destruct);
                if (!destruct.isCancelled()) {
                    for (Entity entity : destruct.getEntities()) {
                        if (entityCreator != null) {
                            EntityUtil.toMixin(entity).setCreator(entityCreator.getUniqueId());
                        }
                        EntityUtil.getMixinWorld(entity).forceSpawnEntity(entity);
                    }
                }
            // Note: If cancelled, the items do not spawn in the world and are NOT copied back to player inventory.
            // This avoids many issues with mods such as Tinkers Construct's soulbound items.
            }
        });
    }
}
Also used : EntityItem(net.minecraft.entity.item.EntityItem) SpongeImpl(org.spongepowered.common.SpongeImpl) User(org.spongepowered.api.entity.living.player.User) StackFrame(org.spongepowered.api.event.CauseStackManager.StackFrame) EventContextKeys(org.spongepowered.api.event.cause.EventContextKeys) SpongeEventFactory(org.spongepowered.api.event.SpongeEventFactory) Sponge(org.spongepowered.api.Sponge) SpawnEntityEvent(org.spongepowered.api.event.entity.SpawnEntityEvent) DropItemEvent(org.spongepowered.api.event.item.inventory.DropItemEvent) DamageSource(org.spongepowered.api.event.cause.entity.damage.source.DamageSource) InternalSpawnTypes(org.spongepowered.common.registry.type.event.InternalSpawnTypes) Entity(org.spongepowered.api.entity.Entity) Collectors(java.util.stream.Collectors) EntityUtil(org.spongepowered.common.entity.EntityUtil) ArrayList(java.util.ArrayList) List(java.util.List) TrackingUtil(org.spongepowered.common.event.tracking.TrackingUtil) EntityPlayer(net.minecraft.entity.player.EntityPlayer) ExperienceOrb(org.spongepowered.api.entity.ExperienceOrb) ItemDropData(org.spongepowered.common.event.tracking.context.ItemDropData) Optional(java.util.Optional) WorldServer(net.minecraft.world.WorldServer) Entity(org.spongepowered.api.entity.Entity) DropItemEvent(org.spongepowered.api.event.item.inventory.DropItemEvent) User(org.spongepowered.api.entity.living.player.User) DamageSource(org.spongepowered.api.event.cause.entity.damage.source.DamageSource) ArrayList(java.util.ArrayList) ExperienceOrb(org.spongepowered.api.entity.ExperienceOrb) WorldServer(net.minecraft.world.WorldServer) SpawnEntityEvent(org.spongepowered.api.event.entity.SpawnEntityEvent) StackFrame(org.spongepowered.api.event.CauseStackManager.StackFrame) EntityPlayer(net.minecraft.entity.player.EntityPlayer) ItemDropData(org.spongepowered.common.event.tracking.context.ItemDropData) EntityItem(net.minecraft.entity.item.EntityItem)

Aggregations

ItemDropData (org.spongepowered.common.event.tracking.context.ItemDropData)17 EntityItem (net.minecraft.entity.item.EntityItem)13 Entity (org.spongepowered.api.entity.Entity)12 CauseStackManager (org.spongepowered.api.event.CauseStackManager)12 DropItemEvent (org.spongepowered.api.event.item.inventory.DropItemEvent)12 World (org.spongepowered.api.world.World)11 ArrayList (java.util.ArrayList)10 UUID (java.util.UUID)10 SpawnEntityEvent (org.spongepowered.api.event.entity.SpawnEntityEvent)8 Collection (java.util.Collection)7 Map (java.util.Map)7 BlockPos (net.minecraft.util.math.BlockPos)6 PrettyPrinter (org.spongepowered.asm.util.PrettyPrinter)6 List (java.util.List)5 Collectors (java.util.stream.Collectors)5 EntityPlayerMP (net.minecraft.entity.player.EntityPlayerMP)5 WorldServer (net.minecraft.world.WorldServer)5 Sponge (org.spongepowered.api.Sponge)5 BlockSnapshot (org.spongepowered.api.block.BlockSnapshot)5 SpongeEventFactory (org.spongepowered.api.event.SpongeEventFactory)5