use of org.spongepowered.api.event.CauseStackManager.StackFrame in project SpongeCommon by SpongePowered.
the class MixinEntityPlayer method attackTargetEntityWithCurrentItem.
/**
* @author gabizou - April 8th, 2016
* @author gabizou - April 11th, 2016 - Update for 1.9 - This enitre method was rewritten
*
* @reason Rewrites the attackTargetEntityWithCurrentItem to throw an {@link AttackEntityEvent} prior
* to the ensuing {@link DamageEntityEvent}. This should cover all cases where players are
* attacking entities and those entities override {@link EntityLivingBase#attackEntityFrom(DamageSource, float)}
* and effectively bypass our damage event hooks.
*
* LVT Rename Table:
* float f | damage |
* float f1 | enchantmentDamage |
* float f2 | attackStrength |
* boolean flag | isStrongAttack |
* boolean flag1 | isSprintingAttack |
* boolean flag2 | isCriticalAttack | Whether critical particles will spawn and of course, multiply the output damage
* boolean flag3 | isSweapingAttack | Whether the player is sweaping an attack and will deal AoE damage
* int i | knockbackModifier | The knockback modifier, must be set from the event after it has been thrown
* float f4 | targetOriginalHealth | This is initially set as the entity original health
* boolean flag4 | litEntityOnFire | This is an internal flag to where if the attack failed, the entity is no longer set on fire
* int j | fireAspectModifier | Literally just to check that the weapon used has fire aspect enchantments
* double d0 | distanceWalkedDelta | This checks that the distance walked delta is more than the normal walking speed to evaluate if you're making a sweaping attack
* double d1 | targetMotionX | Current target entity motion x vector
* double d2 | targetMotionY | Current target entity motion y vector
* double d3 | targetMotionZ | Current target entity motion z vector
* boolean flag5 | attackSucceeded | Whether the attack event succeeded
*
* @param targetEntity The target entity
*/
@Overwrite
public void attackTargetEntityWithCurrentItem(Entity targetEntity) {
// Sponge Start - Add SpongeImpl hook to override in forge as necessary
if (!SpongeImplHooks.checkAttackEntity((EntityPlayer) (Object) this, targetEntity)) {
return;
}
// Sponge End
if (targetEntity.canBeAttackedWithItem()) {
if (!targetEntity.hitByEntity((EntityPlayer) (Object) this)) {
// Sponge Start - Prepare our event values
// float damage = (float) this.getEntityAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).getAttributeValue();
final double originalBaseDamage = this.getEntityAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).getAttributeValue();
float damage = (float) originalBaseDamage;
// Sponge End
float enchantmentDamage = 0.0F;
// Spogne Start - Redirect getting enchantments for our damage event handlers
// if (targetEntity instanceof EntityLivingBase) {
// enchantmentDamage = EnchantmentHelper.getModifierForCreature(this.getHeldItemMainhand(), ((EntityLivingBase) targetEntity).getCreatureAttribute());
// } else {
// enchantmentDamage = EnchantmentHelper.getModifierForCreature(this.getHeldItemMainhand(), EnumCreatureAttribute.UNDEFINED);
// }
float attackStrength = this.getCooledAttackStrength(0.5F);
final List<ModifierFunction<DamageModifier>> originalFunctions = new ArrayList<>();
final EnumCreatureAttribute creatureAttribute = targetEntity instanceof EntityLivingBase ? ((EntityLivingBase) targetEntity).getCreatureAttribute() : EnumCreatureAttribute.UNDEFINED;
final List<DamageFunction> enchantmentModifierFunctions = DamageEventHandler.createAttackEnchantmentFunction(this.getHeldItemMainhand(), creatureAttribute, attackStrength);
// This is kept for the post-damage event handling
final List<DamageModifier> enchantmentModifiers = enchantmentModifierFunctions.stream().map(ModifierFunction::getModifier).collect(Collectors.toList());
enchantmentDamage = (float) enchantmentModifierFunctions.stream().map(ModifierFunction::getFunction).mapToDouble(function -> function.applyAsDouble(originalBaseDamage)).sum();
originalFunctions.addAll(enchantmentModifierFunctions);
// Sponge End
originalFunctions.add(DamageEventHandler.provideCooldownAttackStrengthFunction((EntityPlayer) (Object) this, attackStrength));
damage = damage * (0.2F + attackStrength * attackStrength * 0.8F);
enchantmentDamage = enchantmentDamage * attackStrength;
this.resetCooldown();
if (damage > 0.0F || enchantmentDamage > 0.0F) {
boolean isStrongAttack = attackStrength > 0.9F;
boolean isSprintingAttack = false;
boolean isCriticalAttack = false;
boolean isSweapingAttack = false;
int knockbackModifier = 0;
knockbackModifier = knockbackModifier + EnchantmentHelper.getKnockbackModifier((EntityPlayer) (Object) this);
if (this.isSprinting() && isStrongAttack) {
// Sponge - Only play sound after the event has be thrown and not cancelled.
// this.world.playSound((EntityPlayer) null, this.posX, this.posY, this.posZ, SoundEvents.entity_player_attack_knockback, this.getSoundCategory(), 1.0F, 1.0F);
++knockbackModifier;
isSprintingAttack = true;
}
isCriticalAttack = isStrongAttack && this.fallDistance > 0.0F && !this.onGround && !this.isOnLadder() && !this.isInWater() && !this.isPotionActive(MobEffects.BLINDNESS) && !this.isRiding() && targetEntity instanceof EntityLivingBase;
isCriticalAttack = isCriticalAttack && !this.isSprinting();
if (isCriticalAttack) {
// Sponge Start - add critical attack tuple
// damage *= 1.5F; // Sponge - This is handled in the event
originalFunctions.add(DamageEventHandler.provideCriticalAttackTuple((EntityPlayer) (Object) this));
// Sponge End
}
// damage = damage + enchantmentDamage; // Sponge - We don't need this since our event will re-assign the damage to deal
double distanceWalkedDelta = (double) (this.distanceWalkedModified - this.prevDistanceWalkedModified);
final ItemStack heldItem = this.getHeldItem(EnumHand.MAIN_HAND);
if (isStrongAttack && !isCriticalAttack && !isSprintingAttack && this.onGround && distanceWalkedDelta < (double) this.getAIMoveSpeed()) {
ItemStack itemstack = heldItem;
if (itemstack.getItem() instanceof ItemSword) {
isSweapingAttack = true;
}
}
// Sponge Start - Create the event and throw it
final DamageSource damageSource = DamageSource.causePlayerDamage((EntityPlayer) (Object) this);
final boolean isMainthread = !this.world.isRemote;
if (isMainthread) {
Sponge.getCauseStackManager().pushCause(damageSource);
}
final Cause currentCause = isMainthread ? Sponge.getCauseStackManager().getCurrentCause() : Cause.of(EventContext.empty(), damageSource);
final AttackEntityEvent event = SpongeEventFactory.createAttackEntityEvent(currentCause, originalFunctions, EntityUtil.fromNative(targetEntity), knockbackModifier, originalBaseDamage);
SpongeImpl.postEvent(event);
if (isMainthread) {
Sponge.getCauseStackManager().popCause();
}
if (event.isCancelled()) {
return;
}
damage = (float) event.getFinalOutputDamage();
// sponge - need final for later events
final double attackDamage = damage;
knockbackModifier = event.getKnockbackModifier();
enchantmentDamage = (float) enchantmentModifiers.stream().mapToDouble(event::getOutputDamage).sum();
// Sponge End
float targetOriginalHealth = 0.0F;
boolean litEntityOnFire = false;
int fireAspectModifier = EnchantmentHelper.getFireAspectModifier((EntityPlayer) (Object) this);
if (targetEntity instanceof EntityLivingBase) {
targetOriginalHealth = ((EntityLivingBase) targetEntity).getHealth();
if (fireAspectModifier > 0 && !targetEntity.isBurning()) {
litEntityOnFire = true;
targetEntity.setFire(1);
}
}
double targetMotionX = targetEntity.motionX;
double targetMotionY = targetEntity.motionY;
double targetMotionZ = targetEntity.motionZ;
boolean attackSucceeded = targetEntity.attackEntityFrom(DamageSource.causePlayerDamage((EntityPlayer) (Object) this), damage);
if (attackSucceeded) {
if (knockbackModifier > 0) {
if (targetEntity instanceof EntityLivingBase) {
((EntityLivingBase) targetEntity).knockBack((EntityPlayer) (Object) this, (float) knockbackModifier * 0.5F, (double) MathHelper.sin(this.rotationYaw * 0.017453292F), (double) (-MathHelper.cos(this.rotationYaw * 0.017453292F)));
} else {
targetEntity.addVelocity((double) (-MathHelper.sin(this.rotationYaw * 0.017453292F) * (float) knockbackModifier * 0.5F), 0.1D, (double) (MathHelper.cos(this.rotationYaw * 0.017453292F) * (float) knockbackModifier * 0.5F));
}
this.motionX *= 0.6D;
this.motionZ *= 0.6D;
this.setSprinting(false);
}
if (isSweapingAttack) {
for (EntityLivingBase entitylivingbase : this.world.getEntitiesWithinAABB(EntityLivingBase.class, targetEntity.getEntityBoundingBox().grow(1.0D, 0.25D, 1.0D))) {
if (entitylivingbase != (EntityPlayer) (Object) this && entitylivingbase != targetEntity && !this.isOnSameTeam(entitylivingbase) && this.getDistanceSq(entitylivingbase) < 9.0D) {
// Sponge Start - Do a small event for these entities
// entitylivingbase.knockBack(this, 0.4F, (double)MathHelper.sin(this.rotationYaw * 0.017453292F), (double)(-MathHelper.cos(this.rotationYaw * 0.017453292F)));
// entitylivingbase.attackEntityFrom(DamageSource.causePlayerDamage(this), 1.0F);
final EntityDamageSource sweepingAttackSource = EntityDamageSource.builder().entity(this).type(DamageTypes.SWEEPING_ATTACK).build();
try (final StackFrame frame = isMainthread ? Sponge.getCauseStackManager().pushCauseFrame() : null) {
if (isMainthread) {
Sponge.getCauseStackManager().pushCause(sweepingAttackSource);
}
final ItemStackSnapshot heldSnapshot = ItemStackUtil.snapshotOf(heldItem);
if (isMainthread) {
Sponge.getCauseStackManager().addContext(EventContextKeys.WEAPON, heldSnapshot);
}
final DamageFunction sweapingFunction = DamageFunction.of(DamageModifier.builder().cause(Cause.of(EventContext.empty(), heldSnapshot)).item(heldSnapshot).type(DamageModifierTypes.SWEEPING).build(), (incoming) -> EnchantmentHelper.getSweepingDamageRatio((EntityPlayer) (Object) this) * attackDamage);
final List<DamageFunction> sweapingFunctions = new ArrayList<>();
sweapingFunctions.add(sweapingFunction);
AttackEntityEvent sweepingAttackEvent = SpongeEventFactory.createAttackEntityEvent(currentCause, sweapingFunctions, EntityUtil.fromNative(entitylivingbase), 1, 1.0D);
SpongeImpl.postEvent(sweepingAttackEvent);
if (!sweepingAttackEvent.isCancelled()) {
entitylivingbase.knockBack((EntityPlayer) (Object) this, sweepingAttackEvent.getKnockbackModifier() * 0.4F, (double) MathHelper.sin(this.rotationYaw * 0.017453292F), (double) (-MathHelper.cos(this.rotationYaw * 0.017453292F)));
entitylivingbase.attackEntityFrom(DamageSource.causePlayerDamage((EntityPlayer) (Object) this), (float) sweepingAttackEvent.getFinalOutputDamage());
}
}
// Sponge End
}
}
this.world.playSound((EntityPlayer) null, this.posX, this.posY, this.posZ, SoundEvents.ENTITY_PLAYER_ATTACK_SWEEP, this.getSoundCategory(), 1.0F, 1.0F);
this.spawnSweepParticles();
}
if (targetEntity instanceof EntityPlayerMP && targetEntity.velocityChanged) {
((EntityPlayerMP) targetEntity).connection.sendPacket(new SPacketEntityVelocity(targetEntity));
targetEntity.velocityChanged = false;
targetEntity.motionX = targetMotionX;
targetEntity.motionY = targetMotionY;
targetEntity.motionZ = targetMotionZ;
}
if (isCriticalAttack) {
this.world.playSound((EntityPlayer) null, this.posX, this.posY, this.posZ, SoundEvents.ENTITY_PLAYER_ATTACK_CRIT, this.getSoundCategory(), 1.0F, 1.0F);
this.onCriticalHit(targetEntity);
}
if (!isCriticalAttack && !isSweapingAttack) {
if (isStrongAttack) {
this.world.playSound((EntityPlayer) null, this.posX, this.posY, this.posZ, SoundEvents.ENTITY_PLAYER_ATTACK_STRONG, this.getSoundCategory(), 1.0F, 1.0F);
} else {
this.world.playSound((EntityPlayer) null, this.posX, this.posY, this.posZ, SoundEvents.ENTITY_PLAYER_ATTACK_WEAK, this.getSoundCategory(), 1.0F, 1.0F);
}
}
if (enchantmentDamage > 0.0F) {
this.onEnchantmentCritical(targetEntity);
}
this.setLastAttackedEntity(targetEntity);
if (targetEntity instanceof EntityLivingBase) {
EnchantmentHelper.applyThornEnchantments((EntityLivingBase) targetEntity, (EntityPlayer) (Object) this);
}
EnchantmentHelper.applyArthropodEnchantments((EntityPlayer) (Object) this, targetEntity);
ItemStack itemstack1 = this.getHeldItemMainhand();
Entity entity = targetEntity;
if (targetEntity instanceof MultiPartEntityPart) {
IEntityMultiPart ientitymultipart = ((MultiPartEntityPart) targetEntity).parent;
if (ientitymultipart instanceof EntityLivingBase) {
entity = (EntityLivingBase) ientitymultipart;
}
}
if (!itemstack1.isEmpty() && targetEntity instanceof EntityLivingBase) {
itemstack1.hitEntity((EntityLivingBase) targetEntity, (EntityPlayer) (Object) this);
if (itemstack1.isEmpty()) {
this.setHeldItem(EnumHand.MAIN_HAND, ItemStack.EMPTY);
}
}
if (targetEntity instanceof EntityLivingBase) {
float f5 = targetOriginalHealth - ((EntityLivingBase) targetEntity).getHealth();
this.addStat(StatList.DAMAGE_DEALT, Math.round(f5 * 10.0F));
if (fireAspectModifier > 0) {
targetEntity.setFire(fireAspectModifier * 4);
}
if (this.world instanceof WorldServer && f5 > 2.0F) {
int k = (int) ((double) f5 * 0.5D);
((WorldServer) this.world).spawnParticle(EnumParticleTypes.DAMAGE_INDICATOR, targetEntity.posX, targetEntity.posY + (double) (targetEntity.height * 0.5F), targetEntity.posZ, k, 0.1D, 0.0D, 0.1D, 0.2D, new int[0]);
}
}
this.addExhaustion(0.3F);
} else {
this.world.playSound((EntityPlayer) null, this.posX, this.posY, this.posZ, SoundEvents.ENTITY_PLAYER_ATTACK_NODAMAGE, this.getSoundCategory(), 1.0F, 1.0F);
if (litEntityOnFire) {
targetEntity.extinguish();
}
}
}
}
}
}
use of org.spongepowered.api.event.CauseStackManager.StackFrame in project SpongeCommon by SpongePowered.
the class SpongeCommandManager method process.
@Override
public CommandResult process(CommandSource source, String commandLine) {
final String[] argSplit = commandLine.split(" ", 2);
Sponge.getCauseStackManager().pushCause(source);
final SendCommandEvent event = SpongeEventFactory.createSendCommandEvent(Sponge.getCauseStackManager().getCurrentCause(), argSplit.length > 1 ? argSplit[1] : "", argSplit[0], CommandResult.empty());
Sponge.getGame().getEventManager().post(event);
Sponge.getCauseStackManager().popCause();
if (event.isCancelled()) {
return event.getResult();
}
// Only the first part of argSplit is used at the moment, do the other in the future if needed.
argSplit[0] = event.getCommand();
commandLine = event.getCommand();
if (!event.getArguments().isEmpty()) {
commandLine = commandLine + ' ' + event.getArguments();
}
try {
try (StackFrame frame = Sponge.getCauseStackManager().pushCauseFrame();
// Since we know we are in the main thread, this is safe to do without a thread check
CommandPhaseContext context = GeneralPhase.State.COMMAND.createPhaseContext().source(source).addCaptures().addEntityDropCaptures().buildAndSwitch()) {
if (source instanceof EntityPlayer) {
// Enable player inventory capture
((IMixinInventoryPlayer) ((EntityPlayer) source).inventory).setCapture(true);
}
Sponge.getCauseStackManager().pushCause(source);
final CommandResult result = this.dispatcher.process(source, commandLine);
return result;
} catch (InvocationCommandException ex) {
if (ex.getCause() != null) {
throw ex.getCause();
}
} catch (CommandPermissionException ex) {
Text text = ex.getText();
if (text != null) {
source.sendMessage(error(text));
}
} catch (CommandException ex) {
Text text = ex.getText();
if (text != null) {
source.sendMessage(error(text));
}
if (ex.shouldIncludeUsage()) {
final Optional<CommandMapping> mapping = this.dispatcher.get(argSplit[0], source);
if (mapping.isPresent()) {
Text usage;
if (ex instanceof ArgumentParseException.WithUsage) {
usage = ((ArgumentParseException.WithUsage) ex).getUsage();
} else {
usage = mapping.get().getCallable().getUsage(source);
}
source.sendMessage(error(t("Usage: /%s %s", argSplit[0], usage)));
}
}
}
} catch (Throwable thr) {
Text.Builder excBuilder;
if (thr instanceof TextMessageException) {
Text text = ((TextMessageException) thr).getText();
excBuilder = text == null ? Text.builder("null") : Text.builder();
} else {
excBuilder = Text.builder(String.valueOf(thr.getMessage()));
}
if (source.hasPermission("sponge.debug.hover-stacktrace")) {
final StringWriter writer = new StringWriter();
thr.printStackTrace(new PrintWriter(writer));
excBuilder.onHover(TextActions.showText(Text.of(writer.toString().replace("\t", " ").replace("\r\n", "\n").replace("\r", // I mean I guess somebody could be running this on like OS 9?
"\n"))));
}
source.sendMessage(error(t("Error occurred while executing command: %s", excBuilder.build())));
this.logger.error(TextSerializers.PLAIN.serialize(t("Error occurred while executing command '%s' for source %s: %s", commandLine, source.toString(), String.valueOf(thr.getMessage()))), thr);
}
return CommandResult.empty();
}
use of org.spongepowered.api.event.CauseStackManager.StackFrame in project SpongeCommon by SpongePowered.
the class MixinEntityParrot method onTame.
@Redirect(method = "processInteract", at = @At(value = "INVOKE", target = "Ljava/util/Random;nextInt(I)I", ordinal = 0, remap = false))
public int onTame(Random rand, int bound, EntityPlayer player, EnumHand hand) {
ItemStack stack = player.getHeldItem(hand);
int random = rand.nextInt(bound);
if (random == 0) {
stack.setCount(stack.getCount() + 1);
try (StackFrame frame = Sponge.getCauseStackManager().pushCauseFrame()) {
Sponge.getCauseStackManager().pushCause(player);
if (!SpongeImpl.postEvent(SpongeEventFactory.createTameEntityEvent(Sponge.getCauseStackManager().getCurrentCause(), this))) {
stack.setCount(stack.getCount() - 1);
return random;
}
}
}
return 1;
}
use of org.spongepowered.api.event.CauseStackManager.StackFrame in project SpongeCommon by SpongePowered.
the class MixinEntityPlayerMP method onSetGameType.
@Inject(method = "setGameType(Lnet/minecraft/world/GameType;)V", at = @At("HEAD"), cancellable = true)
private void onSetGameType(GameType gameType, CallbackInfo ci) {
try (StackFrame frame = Sponge.getCauseStackManager().pushCauseFrame()) {
Sponge.getCauseStackManager().pushCause(this);
ChangeGameModeEvent.TargetPlayer event = SpongeEventFactory.createChangeGameModeEventTargetPlayer(Sponge.getCauseStackManager().getCurrentCause(), (GameMode) (Object) this.interactionManager.getGameType(), (GameMode) (Object) gameType, this);
SpongeImpl.postEvent(event);
if (event.isCancelled()) {
ci.cancel();
}
this.pendingGameType = (GameType) (Object) event.getGameMode();
}
}
use of org.spongepowered.api.event.CauseStackManager.StackFrame in project SpongeCommon by SpongePowered.
the class CommandState method unwind.
@Override
public void unwind(CommandPhaseContext phaseContext) {
Optional<EntityPlayer> playerSource = phaseContext.getSource(EntityPlayer.class);
if (playerSource.isPresent()) {
// Post event for inventory changes
((IMixinInventoryPlayer) playerSource.get().inventory).setCapture(false);
List<SlotTransaction> list = ((IMixinInventoryPlayer) playerSource.get().inventory).getCapturedTransactions();
if (!list.isEmpty()) {
ChangeInventoryEvent event = SpongeEventFactory.createChangeInventoryEvent(Sponge.getCauseStackManager().getCurrentCause(), ((Inventory) playerSource.get().inventory), list);
SpongeImpl.postEvent(event);
PacketPhaseUtil.handleSlotRestore(playerSource.get(), null, list, event.isCancelled());
list.clear();
}
}
final CommandSource sender = phaseContext.getSource(CommandSource.class).orElseThrow(TrackingUtil.throwWithContext("Expected to be capturing a Command Sender, but none found!", phaseContext));
phaseContext.getCapturedBlockSupplier().acceptAndClearIfNotEmpty(list -> TrackingUtil.processBlockCaptures(list, this, phaseContext));
try (StackFrame frame = Sponge.getCauseStackManager().pushCauseFrame()) {
Sponge.getCauseStackManager().pushCause(sender);
Sponge.getCauseStackManager().addContext(EventContextKeys.SPAWN_TYPE, InternalSpawnTypes.PLACEMENT);
phaseContext.getCapturedEntitySupplier().acceptAndClearIfNotEmpty(entities -> {
// TODO the entity spawn causes are not likely valid,
// need to investigate further.
final SpawnEntityEvent spawnEntityEvent = SpongeEventFactory.createSpawnEntityEvent(Sponge.getCauseStackManager().getCurrentCause(), entities);
SpongeImpl.postEvent(spawnEntityEvent);
if (!spawnEntityEvent.isCancelled()) {
final boolean isPlayer = sender instanceof Player;
final Player player = isPlayer ? (Player) sender : null;
for (Entity entity : spawnEntityEvent.getEntities()) {
if (isPlayer) {
EntityUtil.toMixin(entity).setCreator(player.getUniqueId());
}
EntityUtil.getMixinWorld(entity).forceSpawnEntity(entity);
}
}
});
}
try (CauseStackManager.StackFrame frame = Sponge.getCauseStackManager().pushCauseFrame()) {
Sponge.getCauseStackManager().pushCause(sender);
Sponge.getCauseStackManager().addContext(EventContextKeys.SPAWN_TYPE, InternalSpawnTypes.DROPPED_ITEM);
phaseContext.getCapturedEntityDropSupplier().acceptIfNotEmpty(uuidItemStackMultimap -> {
for (Map.Entry<UUID, Collection<ItemDropData>> entry : uuidItemStackMultimap.asMap().entrySet()) {
final UUID key = entry.getKey();
@Nullable net.minecraft.entity.Entity foundEntity = null;
for (WorldServer worldServer : WorldManager.getWorlds()) {
final net.minecraft.entity.Entity entityFromUuid = worldServer.getEntityFromUuid(key);
if (entityFromUuid != null) {
foundEntity = entityFromUuid;
break;
}
}
final Optional<Entity> affectedEntity = Optional.ofNullable((Entity) foundEntity);
if (!affectedEntity.isPresent()) {
continue;
}
final Collection<ItemDropData> itemStacks = entry.getValue();
if (itemStacks.isEmpty()) {
return;
}
final List<ItemDropData> items = new ArrayList<>();
items.addAll(itemStacks);
itemStacks.clear();
final WorldServer minecraftWorld = EntityUtil.getMinecraftWorld(affectedEntity.get());
if (!items.isEmpty()) {
final List<Entity> itemEntities = items.stream().map(data -> data.create(minecraftWorld)).map(EntityUtil::fromNative).collect(Collectors.toList());
Sponge.getCauseStackManager().pushCause(affectedEntity.get());
final DropItemEvent.Destruct destruct = SpongeEventFactory.createDropItemEventDestruct(Sponge.getCauseStackManager().getCurrentCause(), itemEntities);
SpongeImpl.postEvent(destruct);
Sponge.getCauseStackManager().popCause();
if (!destruct.isCancelled()) {
final boolean isPlayer = sender instanceof Player;
final Player player = isPlayer ? (Player) sender : null;
for (Entity entity : destruct.getEntities()) {
if (isPlayer) {
EntityUtil.toMixin(entity).setCreator(player.getUniqueId());
}
EntityUtil.getMixinWorld(entity).forceSpawnEntity(entity);
}
}
}
}
});
}
}
Aggregations