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;
}
}
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);
}
}
}
}
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;
}
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);
}
}
});
}
}
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.
}
});
}
}
Aggregations