@Inject( method = "onUpdate", at = @At( value = "INVOKE", target = "Lnet/minecraft/entity/effect/EntityLightningBolt;setDead()V")) public void onSetDead(CallbackInfo ci) { if (this.isDead) { return; } World world = (World) this.worldObj; LightningEvent.Strike strike = SpongeEventFactory.createLightningEventStrike( this.cause, this.struckEntities, this.struckEntitySnapshots, world, this.struckBlocks); Sponge.getEventManager().post(strike); if (!strike.isCancelled()) { for (Transaction<BlockSnapshot> bt : strike.getTransactions()) { if (bt.isValid()) { BlockSnapshot bs = bt.getFinal(); world.setBlock(bs.getPosition(), bs.getState()); } } for (Entity e : strike.getEntities()) { ((net.minecraft.entity.Entity) e).onStruckByLightning((EntityLightningBolt) (Object) this); } SpongeImpl.postEvent(SpongeEventFactory.createLightningEventPost(this.cause)); } }
/** * @author gabizou - February 7th, 2016 * <p>This will short circuit all other patches such that we control the entities being loaded * by chunkloading and can throw our bulk entity event. This will bypass Forge's hook for * individual entity events, but the SpongeModEventManager will still successfully throw the * appropriate event and cancel the entities otherwise contained. * @param entities The entities being loaded * @param callbackInfo The callback info */ @Final @Inject(method = "loadEntities", at = @At("HEAD"), cancellable = true) private void spongeLoadEntities( Collection<net.minecraft.entity.Entity> entities, CallbackInfo callbackInfo) { if (entities.isEmpty()) { // just return, no entities to load! callbackInfo.cancel(); return; } List<Entity> entityList = new ArrayList<>(); ImmutableList.Builder<EntitySnapshot> snapshotBuilder = ImmutableList.builder(); for (net.minecraft.entity.Entity entity : entities) { entityList.add((Entity) entity); snapshotBuilder.add(((Entity) entity).createSnapshot()); } SpawnCause cause = SpawnCause.builder().type(InternalSpawnTypes.CHUNK_LOAD).build(); List<NamedCause> causes = new ArrayList<>(); causes.add(NamedCause.source(cause)); causes.add(NamedCause.of("World", this)); SpawnEntityEvent.ChunkLoad chunkLoad = SpongeEventFactory.createSpawnEntityEventChunkLoad( Cause.of(causes), entityList, snapshotBuilder.build(), this); SpongeImpl.postEvent(chunkLoad); if (!chunkLoad.isCancelled()) { for (Entity successful : chunkLoad.getEntities()) { this.loadedEntityList.add((net.minecraft.entity.Entity) successful); this.onEntityAdded((net.minecraft.entity.Entity) successful); } } callbackInfo.cancel(); }
@Inject( method = "newExplosion", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/Explosion;doExplosionA()V"), locals = LocalCapture.CAPTURE_FAILHARD, cancellable = true) public void callWorldOnExplosionEvent( Entity entityIn, double x, double y, double z, float strength, boolean isFlaming, boolean isSmoking, CallbackInfoReturnable<Explosion> cir, Explosion explosion) { final ExplosionEvent.Pre event = SpongeEventFactory.createExplosionEventPre( Sponge.getGame(), Cause.of(this), (org.spongepowered.api.world.explosion.Explosion) explosion, (org.spongepowered.api.world.World) this); if (Sponge.getGame().getEventManager().post(event)) { cir.setReturnValue(explosion); } }
@Redirect( method = "interact", at = @At( value = "INVOKE", target = "Ljava/util/Random;nextInt(I)I", ordinal = 0, remap = false)) public int onTame(Random rand, int bound, EntityPlayer player) { int random = rand.nextInt(bound); if (random == 0 && !SpongeImpl.postEvent( SpongeEventFactory.createTameEntityEvent( Cause.of( NamedCause.source(player), NamedCause.of( TameEntityEvent.USED_ITEM, ((org.spongepowered.api.item.inventory.ItemStack) this.currentItemStack) .createSnapshot())), this))) { this.currentItemStack = null; return random; } this.currentItemStack = null; return 1; }
@Inject( method = "dropItem", at = @At( value = "FIELD", opcode = Opcodes.GETFIELD, target = "Lnet/minecraft/entity/player/EntityPlayer;posY:D"), cancellable = true) private void onDropTop( ItemStack itemStack, boolean a, boolean b, CallbackInfoReturnable<EntityItem> callbackInfoReturnable) { final double height = this.posY - 0.3D + (double) this.getEyeHeight(); Transform<org.spongepowered.api.world.World> transform = new Transform<>(this.getWorld(), new Vector3d(this.posX, height, this.posZ)); SpawnCause cause = EntitySpawnCause.builder().entity(this).type(SpawnTypes.DROPPED_ITEM).build(); ConstructEntityEvent.Pre event = SpongeEventFactory.createConstructEntityEventPre( Cause.of(NamedCause.source(cause)), EntityTypes.ITEM, transform); SpongeImpl.postEvent(event); if (event.isCancelled()) { callbackInfoReturnable.setReturnValue(null); } }
@Overwrite public static PlayerInteractEvent onPlayerInteract( EntityPlayer player, Action action, net.minecraft.world.World world, BlockPos pos, EnumFacing face) { if (world.isRemote) { PlayerInteractEvent event = new PlayerInteractEvent(player, action, pos, face, world); MinecraftForge.EVENT_BUS.post(event); return event; } InteractBlockEvent event = null; if (action == Action.LEFT_CLICK_BLOCK) { event = SpongeEventFactory.createInteractBlockEventPrimary( SpongeImpl.getGame(), Cause.of(player), Optional.empty(), ((World) world).createSnapshot(VecHelper.toVector(pos)), face == null ? Direction.NONE : DirectionFacingProvider.getInstance().getKey(face).get()); } else { event = SpongeEventFactory.createInteractBlockEventSecondary( SpongeImpl.getGame(), Cause.of(player), Optional.empty(), ((World) world).createSnapshot(VecHelper.toVector(pos)), face == null ? Direction.NONE : DirectionFacingProvider.getInstance().getKey(face).get()); } SpongeImpl.postEvent(event); return (PlayerInteractEvent) SpongeForgeEventFactory.lastForgeEvent; }
/** * @Author Zidane * * <p>Invoke before {@code System.arraycopy(packetIn.getLines(), 0, tileentitysign.signText, 0, * 4);} (line 1156 in source) to call SignChangeEvent. * * @param packetIn Injected packet param * @param ci Info to provide mixin on how to handle the callback * @param worldserver Injected world param * @param blockpos Injected blockpos param * @param tileentity Injected tilentity param * @param tileentitysign Injected tileentitysign param */ @Inject( method = "processUpdateSign", at = @At( value = "INVOKE", target = "Lnet/minecraft/network/play/client/C12PacketUpdateSign;getLines()[Lnet/minecraft/util/IChatComponent;"), cancellable = true, locals = LocalCapture.CAPTURE_FAILSOFT) public void callSignChangeEvent( C12PacketUpdateSign packetIn, CallbackInfo ci, WorldServer worldserver, BlockPos blockpos, TileEntity tileentity, TileEntitySign tileentitysign) { ci.cancel(); final Optional<SignData> existingSignData = ((Sign) tileentitysign).getData(); if (!existingSignData.isPresent()) { // TODO Unsure if this is the best to do here... throw new RuntimeException("Critical error! Sign data not present on sign!"); } final SignData changedSignData = existingSignData.get().copy(); for (int i = 0; i < packetIn.getLines().length; i++) { changedSignData.setLine(i, SpongeTexts.toText(packetIn.getLines()[i])); } // I pass changedSignData in here twice to emulate the fact that even-though the current sign // data doesn't have the lines from the packet // applied, this is what it "is" right now. If the data shown in the world is desired, it can be // fetched from Sign.getData final SignChangeEvent event = SpongeEventFactory.createSignChange( Sponge.getGame(), new Cause(null, this.playerEntity, null), (Sign) tileentitysign, changedSignData, changedSignData); if (!Sponge.getGame().getEventManager().post(event)) { ((Sign) tileentitysign).offer(event.getNewData()); } else { // If cancelled, I set the data back that was fetched from the sign. This means that if its a // new sign, the sign will be empty else // it will be the text of the sign that was showing in the world ((Sign) tileentitysign).offer(existingSignData.get()); } tileentitysign.markDirty(); worldserver.markBlockForUpdate(blockpos); }
@Inject( method = "randomTick", at = @At(value = "HEAD"), locals = LocalCapture.CAPTURE_FAILEXCEPTION, cancellable = true) public void callRandomTickEvent( World world, BlockPos pos, IBlockState state, Random rand, CallbackInfo ci) { final BlockRandomTickEvent event = SpongeEventFactory.createBlockRandomTick( Sponge.getGame(), null, new Location<org.spongepowered.api.world.World>( (org.spongepowered.api.world.World) world, VecHelper.toVector(pos))); // TODO Fix null Cause Sponge.getGame().getEventManager().post(event); if (event.isCancelled()) { ci.cancel(); } }
@Inject( method = "randomTick", at = @At(value = "HEAD"), locals = LocalCapture.CAPTURE_FAILEXCEPTION, cancellable = true) public void callRandomTickEvent( net.minecraft.world.World world, BlockPos pos, IBlockState state, Random rand, CallbackInfo ci) { BlockSnapshot blockSnapshot = ((World) world).createSnapshot(VecHelper.toVector(pos)); final TickBlockEvent event = SpongeEventFactory.createTickBlockEvent(Cause.of(NamedCause.source(world)), blockSnapshot); SpongeImpl.postEvent(event); if (event.isCancelled()) { ci.cancel(); } }
@Redirect( method = "onUpdate", at = @At( value = "INVOKE", target = "Lnet/minecraft/entity/player/EntityPlayer;isPlayerSleeping()Z")) public boolean onIsPlayerSleeping(EntityPlayer self) { if (self.isPlayerSleeping()) { if (!this.worldObj.isRemote) { SpongeImpl.postEvent( SpongeEventFactory.createSleepingEventTick( Cause.of(NamedCause.source(this)), this.getWorld().createSnapshot(VecHelper.toVector(this.playerLocation)), this)); } return true; } return false; }
@Redirect( method = "onUpdate()V", at = @At( value = "INVOKE", target = "Lnet/minecraft/entity/Entity;attackEntityFrom(Lnet/minecraft/util/DamageSource;F)Z")) public boolean onAttackEntityFrom( net.minecraft.entity.Entity entity, DamageSource damageSource, float damage) { EntitySnapshot fishHookSnapshot = this.createSnapshot(); FishingEvent.HookEntity event = SpongeEventFactory.createFishingEventHookEntity( Cause.of(NamedCause.source(this.angler)), this.createSnapshot(), this, (Entity) entity); if (!SpongeImpl.postEvent(event)) { if (this.getShooter() instanceof Entity) { DamageSource.causeThrownDamage( (net.minecraft.entity.Entity) (Object) this, (net.minecraft.entity.Entity) this.getShooter()); } return entity.attackEntityFrom(damageSource, (float) this.getDamage()); } return false; }
@Inject( method = "processPlayer", at = @At( value = "FIELD", target = "net.minecraft.network.NetHandlerPlayServer.hasMoved:Z", ordinal = 2), cancellable = true) public void proccesPlayerMoved(C03PacketPlayer packetIn, CallbackInfo ci) { if (packetIn.isMoving() || packetIn.getRotating() && !this.playerEntity.isDead) { Player player = (Player) this.playerEntity; Vector3d fromrot = player.getRotation(); // If Sponge used the player's current location, the delta might never be triggered which // could be exploited Location from = player.getLocation(); if (this.lastMoveLocation != null) { from = this.lastMoveLocation; } Vector3d torot = new Vector3d(packetIn.getPitch(), packetIn.getYaw(), 0); Location to = new Location( player.getWorld(), packetIn.getPositionX(), packetIn.getPositionY(), packetIn.getPositionZ()); // Minecraft sends a 0, 0, 0 position when rotation only update occurs, this needs to be // recognized and corrected boolean rotationOnly = !packetIn.isMoving() && packetIn.getRotating(); if (rotationOnly) { // Correct the to location so it's not misrepresented to plugins, only when player rotates // without moving // In this case it's only a rotation update, which isn't related to the to location from = player.getLocation(); to = from; } // Minecraft does the same with rotation when it's only a positional update boolean positionOnly = packetIn.isMoving() && !packetIn.getRotating(); if (positionOnly) { // Correct the new rotation to match the old rotation torot = fromrot; } double deltaSquared = to.getPosition().distanceSquared(from.getPosition()); double deltaAngleSquared = fromrot.distanceSquared(torot); // These magic numbers are sad but help prevent excessive lag from this event. // eventually it would be nice to not have them if (deltaSquared > ((1f / 16) * (1f / 16)) || deltaAngleSquared > (.15f * .15f)) { PlayerMoveEvent event = SpongeEventFactory.createPlayerMove(Sponge.getGame(), player, from, to, torot); Sponge.getGame().getEventManager().post(event); if (event.isCancelled()) { player.setLocationAndRotation(from, fromrot); this.lastMoveLocation = from; ci.cancel(); } else if (!event.getNewLocation().equals(to)) { player.setLocationAndRotation(event.getNewLocation(), event.getRotation()); this.lastMoveLocation = event.getNewLocation(); ci.cancel(); } else if (!from.equals(player.getLocation()) && this.justTeleported) { this.lastMoveLocation = player.getLocation(); // Prevent teleports during the move event from causing odd behaviors this.justTeleported = false; ci.cancel(); } else { this.lastMoveLocation = event.getNewLocation(); } } } }
/** * @author Aaron1011 - February 6th, 2015 * @reason This needs to handle for both cases where a fish and/or an entity is being caught. * There's no real good way to do this with an injection. */ @Overwrite public int handleHookRetraction() { if (this.worldObj.isRemote) { return 0; } // Sponge start byte b0 = 0; net.minecraft.item.ItemStack itemStack = null; int exp = 0; if (this.ticksCatchable > 0) { itemStack = this.getFishingResult(); exp = this.rand.nextInt(6) + 1; } EntitySnapshot fishHookSnapshot = this.createSnapshot(); Transaction<ItemStackSnapshot> transaction = null; if (itemStack != null) { ItemStackSnapshot original = ((ItemStack) itemStack).createSnapshot(); ItemStackSnapshot replacement = ((ItemStack) itemStack).createSnapshot(); transaction = new Transaction<>(original, replacement); } else { transaction = new Transaction<>(ItemStackSnapshot.NONE, ItemStackSnapshot.NONE); } FishingEvent.Stop event = SpongeEventFactory.createFishingEventStop( Cause.of(NamedCause.source(this.angler)), exp, exp, fishHookSnapshot, this, transaction, (Player) this.angler); if (!SpongeImpl.postEvent(event)) { // Sponge end if (this.caughtEntity != null) { double d0 = this.angler.posX - this.posX; double d2 = this.angler.posY - this.posY; double d4 = this.angler.posZ - this.posZ; double d6 = (double) MathHelper.sqrt_double(d0 * d0 + d2 * d2 + d4 * d4); double d8 = 0.1D; this.caughtEntity.motionX += d0 * d8; this.caughtEntity.motionY += d2 * d8 + (double) MathHelper.sqrt_double(d6) * 0.08D; this.caughtEntity.motionZ += d4 * d8; b0 = 3; } // Sponge Start if (!event.getItemStackTransaction().getFinal().getType().equals(ItemTypes.NONE)) { ItemStackSnapshot itemSnapshot = event.getItemStackTransaction().getFinal(); EntityItem entityitem1 = new EntityItem( this.worldObj, this.posX, this.posY, this.posZ, (net.minecraft.item.ItemStack) itemSnapshot.createStack()); double d1 = this.angler.posX - this.posX; double d3 = this.angler.posY - this.posY; double d5 = this.angler.posZ - this.posZ; double d7 = MathHelper.sqrt_double(d1 * d1 + d3 * d3 + d5 * d5); double d9 = 0.1D; entityitem1.motionX = d1 * d9; entityitem1.motionY = d3 * d9 + MathHelper.sqrt_double(d7) * 0.08D; entityitem1.motionZ = d5 * d9; this.worldObj.spawnEntityInWorld(entityitem1); this.angler.worldObj.spawnEntityInWorld( new EntityXPOrb( this.angler.worldObj, this.angler.posX, this.angler.posY + 0.5D, this.angler.posZ + 0.5D, event.getExperience())); // Sponge End b0 = 1; } if (this.inGround) { b0 = 2; } this.setDead(); this.angler.fishEntity = null; // Sponge Start if (this.fishingRod != null) { this.fishingRod.damageItem(b0, this.angler); this.angler.swingItem(); this.fishingRod = null; } // Sponge End } return b0; }
/** * @author gabizou - April 8th, 2016 * @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. * @param targetEntity The target entity */ @Overwrite public void attackTargetEntityWithCurrentItem(net.minecraft.entity.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.canAttackWithItem()) { if (!targetEntity.hitByEntity((EntityPlayer) (Object) this)) { // Sponge Start - Prepare our event values // float baseDamage = // this.getEntityAttribute(SharedMonsterAttributes.attackDamage).getAttributeValue(); final double originalBaseDamage = this.getEntityAttribute(SharedMonsterAttributes.attackDamage).getAttributeValue(); float baseDamage = (float) originalBaseDamage; // Sponge End int knockbackModifier = 0; float enchantmentModifierAmount = 0.0F; // Sponge Start - gather the attack modifiers final List<Tuple<DamageModifier, Function<? super Double, Double>>> originalFunctions = new ArrayList<>(); final EnumCreatureAttribute creatureAttribute = targetEntity instanceof EntityLivingBase ? ((EntityLivingBase) targetEntity).getCreatureAttribute() : EnumCreatureAttribute.UNDEFINED; final List<Tuple<DamageModifier, Function<? super Double, Double>>> enchantmentModifierFunctions = DamageEventHandler.createAttackEnchamntmentFunction( this.getHeldItem(), creatureAttribute); // if (targetEntity instanceof EntityLivingBase) { // enchantmentModifierAmount = // EnchantmentHelper.getModifierForCreature(this.getHeldItem(), creatureAttribute); // } else { // enchantmentModifierAmount = // EnchantmentHelper.getModifierForCreature(this.getHeldItem(), // EnumCreatureAttribute.UNDEFINED); // } enchantmentModifierAmount = (float) enchantmentModifierFunctions .stream() .map(Tuple::getSecond) .mapToDouble(function -> function.apply(originalBaseDamage)) .sum(); originalFunctions.addAll(enchantmentModifierFunctions); // Sponge End knockbackModifier = knockbackModifier + EnchantmentHelper.getKnockbackModifier((EntityPlayer) (Object) this); if (this.isSprinting()) { ++knockbackModifier; } if (baseDamage > 0.0F || enchantmentModifierAmount > 0.0F) { boolean fallingCriticalHit = this.fallDistance > 0.0F && !this.onGround && !this.isOnLadder() && !this.isInWater() && !this.isPotionActive(Potion.blindness) && this.ridingEntity == null && targetEntity instanceof EntityLivingBase; if (fallingCriticalHit && baseDamage > 0.0F) { // Sponge - Add the function for critical attacking originalFunctions.add( DamageEventHandler.provideCriticalAttackTuple((EntityPlayer) (Object) this)); // baseDamage *= 1.5F; Sponge - remove since it's handled in the event } // baseDamage = baseDamage + enchantmentModifierAmount; // Sponge - remove since it is // delegated through the event. boolean targetLitOnFire = false; int fireAspectLevel = EnchantmentHelper.getFireAspectModifier((EntityPlayer) (Object) this); if (targetEntity instanceof EntityLivingBase && fireAspectLevel > 0 && !targetEntity.isBurning()) { targetLitOnFire = true; targetEntity.setFire(1); } double targetMotionX = targetEntity.motionX; double targetMotionY = targetEntity.motionY; double targetMotionZ = targetEntity.motionZ; // Sponge Start - Create the event and throw it final DamageSource damageSource = DamageSource.causePlayerDamage((EntityPlayer) (Object) this); final AttackEntityEvent event = SpongeEventFactory.createAttackEntityEvent( Cause.source(damageSource).build(), originalFunctions, EntityUtil.fromNative(targetEntity), knockbackModifier, originalBaseDamage); SpongeImpl.postEvent(event); if (event.isCancelled()) { if (targetLitOnFire) { targetEntity.extinguish(); } return; } baseDamage = (float) event.getFinalOutputDamage(); knockbackModifier = event.getKnockbackModifier(); boolean attackSucceded = targetEntity.attackEntityFrom(damageSource, (float) event.getFinalOutputDamage()); // Sponge End if (attackSucceded) { if (knockbackModifier > 0) { targetEntity.addVelocity( (double) (-MathHelper.sin(this.rotationYaw * (float) Math.PI / 180.0F) * (float) knockbackModifier * 0.5F), 0.1D, (double) (MathHelper.cos(this.rotationYaw * (float) Math.PI / 180.0F) * (float) knockbackModifier * 0.5F)); this.motionX *= 0.6D; this.motionZ *= 0.6D; this.setSprinting(false); } if (targetEntity instanceof EntityPlayerMP && targetEntity.velocityChanged) { ((EntityPlayerMP) targetEntity) .playerNetServerHandler.sendPacket(new S12PacketEntityVelocity(targetEntity)); targetEntity.velocityChanged = false; targetEntity.motionX = targetMotionX; targetEntity.motionY = targetMotionY; targetEntity.motionZ = targetMotionZ; } if (fallingCriticalHit) { this.onCriticalHit(targetEntity); } if (enchantmentModifierAmount > 0.0F) { this.onEnchantmentCritical(targetEntity); } if (baseDamage >= 18.0F) { this.triggerAchievement(AchievementList.overkill); } this.setLastAttacker(targetEntity); if (targetEntity instanceof EntityLivingBase) { EnchantmentHelper.applyThornEnchantments( (EntityLivingBase) targetEntity, (EntityPlayer) (Object) this); } EnchantmentHelper.applyArthropodEnchantments( (EntityPlayer) (Object) this, targetEntity); ItemStack itemstack = this.getCurrentEquippedItem(); net.minecraft.entity.Entity entity = targetEntity; if (targetEntity instanceof EntityDragonPart) { IEntityMultiPart ientitymultipart = ((EntityDragonPart) targetEntity).entityDragonObj; if (ientitymultipart instanceof EntityLivingBase) { entity = (EntityLivingBase) ientitymultipart; } } if (itemstack != null && entity instanceof EntityLivingBase) { itemstack.hitEntity((EntityLivingBase) entity, (EntityPlayer) (Object) this); if (itemstack.stackSize <= 0) { this.destroyCurrentEquippedItem(); } } if (targetEntity instanceof EntityLivingBase) { this.addStat(StatList.damageDealtStat, Math.round(baseDamage * 10.0F)); if (fireAspectLevel > 0) { targetEntity.setFire(fireAspectLevel * 4); } } this.addExhaustion(0.3F); } else if (targetLitOnFire) { targetEntity.extinguish(); } } } } }
public void postState(Class<? extends StateEvent> type) { this.game.getEventManager().post(SpongeEventFactory.createState(type, this.game)); }