Yet further finely-tuned

- Fixed blocks not updating their neighbors when being assembled into a contraption
- Fixed Stockpile Switch not updating its redstone output when its signal is inverted
- Reduced lag caused by many active nearby soul particles
- The Wither's shield is now immune to potato projectiles
- Fixed potato projectile potion effect weirdness
- Golden Apple potato projectiles can now cure Zombie Villagers
- Potato Recovery will no longer drop Golden Apples that successfully hit an entity
This commit is contained in:
reidbhuntley 2021-07-11 21:25:00 -04:00
parent 4306d076db
commit b70608b030
9 changed files with 99 additions and 40 deletions

View file

@ -84,6 +84,7 @@ import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.INBT; import net.minecraft.nbt.INBT;
import net.minecraft.nbt.ListNBT; import net.minecraft.nbt.ListNBT;
import net.minecraft.nbt.NBTUtil; import net.minecraft.nbt.NBTUtil;
import net.minecraft.network.DebugPacketSender;
import net.minecraft.state.properties.BlockStateProperties; import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.state.properties.ChestType; import net.minecraft.state.properties.ChestType;
import net.minecraft.state.properties.PistonType; import net.minecraft.state.properties.PistonType;
@ -102,6 +103,7 @@ import net.minecraft.village.PointOfInterestType;
import net.minecraft.world.IWorld; import net.minecraft.world.IWorld;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraft.world.gen.feature.template.Template.BlockInfo; import net.minecraft.world.gen.feature.template.Template.BlockInfo;
import net.minecraft.world.server.ServerWorld;
import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.util.Constants.BlockFlags; import net.minecraftforge.common.util.Constants.BlockFlags;
@ -949,8 +951,7 @@ public abstract class Contraption {
int flags = BlockFlags.IS_MOVING | BlockFlags.NO_NEIGHBOR_DROPS | BlockFlags.UPDATE_NEIGHBORS int flags = BlockFlags.IS_MOVING | BlockFlags.NO_NEIGHBOR_DROPS | BlockFlags.UPDATE_NEIGHBORS
| BlockFlags.BLOCK_UPDATE | BlockFlags.RERENDER_MAIN_THREAD; | BlockFlags.BLOCK_UPDATE | BlockFlags.RERENDER_MAIN_THREAD;
if (blockIn instanceof IWaterLoggable && oldState.contains(BlockStateProperties.WATERLOGGED) if (blockIn instanceof IWaterLoggable && oldState.contains(BlockStateProperties.WATERLOGGED)
&& oldState.get(BlockStateProperties.WATERLOGGED) && oldState.get(BlockStateProperties.WATERLOGGED)) {
.booleanValue()) {
world.setBlockState(add, Blocks.WATER.getDefaultState(), flags); world.setBlockState(add, Blocks.WATER.getDefaultState(), flags);
continue; continue;
} }
@ -962,8 +963,22 @@ public abstract class Contraption {
.add(offset); .add(offset);
// if (!shouldUpdateAfterMovement(block)) // if (!shouldUpdateAfterMovement(block))
// continue; // continue;
int flags = BlockFlags.IS_MOVING | BlockFlags.DEFAULT; int flags = BlockFlags.IS_MOVING | BlockFlags.DEFAULT;
world.notifyBlockUpdate(add, block.state, Blocks.AIR.getDefaultState(), flags); world.notifyBlockUpdate(add, block.state, Blocks.AIR.getDefaultState(), flags);
// when the blockstate is set to air, the block's POI data is removed, but markAndNotifyBlock tries to
// remove it again, so to prevent an error from being logged by double-removal we add the POI data back now
// (code copied from ServerWorld.onBlockStateChange)
ServerWorld serverWorld = (ServerWorld) world;
PointOfInterestType.forState(block.state).ifPresent(poiType -> {
world.getServer().execute(() -> {
serverWorld.getPointOfInterestManager().func_219135_a(add, poiType);
DebugPacketSender.func_218799_a(serverWorld, add);
});
});
world.markAndNotifyBlock(add, world.getChunkAt(add), block.state, Blocks.AIR.getDefaultState(), flags, 512);
block.state.updateDiagonalNeighbors(world, add, flags & -2); block.state.updateDiagonalNeighbors(world, add, flags & -2);
} }
} }

View file

@ -19,7 +19,7 @@ public class HauntedBellMovementBehaviour extends BellMovementBehaviour {
@Override @Override
public void visitNewPosition(MovementContext context, BlockPos pos) { public void visitNewPosition(MovementContext context, BlockPos pos) {
if (!context.world.isRemote && getRecharge(context) == 0) { if (!context.world.isRemote && getRecharge(context) == 0) {
HauntedBellPulser.sendPulse(context.world, pos, DISTANCE, true); HauntedBellPulser.sendPulse(context.world, pos, DISTANCE, false);
setRecharge(context, HauntedBellTileEntity.RECHARGE_TICKS); setRecharge(context, HauntedBellTileEntity.RECHARGE_TICKS);
playSound(context); playSound(context);
} }

View file

@ -46,7 +46,7 @@ public class HauntedBellTileEntity extends AbstractBellTileEntity {
return false; return false;
if (!world.isRemote) if (!world.isRemote)
HauntedBellPulser.sendPulse(world, pos, DISTANCE, true); HauntedBellPulser.sendPulse(world, pos, DISTANCE, false);
startEffect(); startEffect();

View file

@ -31,7 +31,7 @@ public class SoulBaseParticle extends CustomRotationParticle {
selectSpriteLoopingWithAge(animatedSprite); selectSpriteLoopingWithAge(animatedSprite);
BlockPos pos = new BlockPos(posX, posY, posZ); BlockPos pos = new BlockPos(posX, posY, posZ);
if (age++ >= maxAge || !SoulPulseEffect.canSpawnSoulAt(world, pos, false)) if (age++ >= maxAge || !SoulPulseEffect.isDark(world, pos))
setExpired(); setExpired();
} }

View file

@ -73,7 +73,7 @@ public class SoulParticle extends CustomRotationParticle {
BlockPos pos = new BlockPos(posX, posY, posZ); BlockPos pos = new BlockPos(posX, posY, posZ);
if (animationStage == null) if (animationStage == null)
setExpired(); setExpired();
if (!SoulPulseEffect.canSpawnSoulAt(world, pos, false)) { if (!SoulPulseEffect.isDark(world, pos)) {
isVisible = true; isVisible = true;
if (!isPerimeter) if (!isPerimeter)
setExpired(); setExpired();

View file

@ -74,6 +74,10 @@ public class SoulPulseEffect {
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
public static boolean isDark(World world, BlockPos at) {
return world.getLightLevel(LightType.BLOCK, at) < 8;
}
public static boolean canSpawnSoulAt(World world, BlockPos at, boolean ignoreLight) { public static boolean canSpawnSoulAt(World world, BlockPos at, boolean ignoreLight) {
EntityType<?> dummy = EntityType.ZOMBIE; EntityType<?> dummy = EntityType.ZOMBIE;
double dummyWidth = 0.2, dummyHeight = 0.75; double dummyWidth = 0.2, dummyHeight = 0.75;
@ -82,7 +86,7 @@ public class SoulPulseEffect {
return world != null return world != null
&& WorldEntitySpawner && WorldEntitySpawner
.canCreatureTypeSpawnAtLocation(EntitySpawnPlacementRegistry.PlacementType.ON_GROUND, world, at, dummy) .canCreatureTypeSpawnAtLocation(EntitySpawnPlacementRegistry.PlacementType.ON_GROUND, world, at, dummy)
&& (ignoreLight || world.getLightLevel(LightType.BLOCK, at) < 8) && (ignoreLight || isDark(world, at))
&& world && world
.getBlockCollisions(null, .getBlockCollisions(null,
new AxisAlignedBB(at.getX() + 0.5 - w2, at.getY(), at.getZ() + 0.5 - w2, at.getX() + 0.5 + w2, new AxisAlignedBB(at.getX() + 0.5 - w2, at.getY(), at.getZ() + 0.5 - w2, at.getX() + 0.5 + w2,

View file

@ -4,7 +4,6 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.function.BiPredicate; import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Predicate; import java.util.function.Predicate;
import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.util.Pair;
@ -17,7 +16,9 @@ import net.minecraft.block.Blocks;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.item.FallingBlockEntity; import net.minecraft.entity.item.FallingBlockEntity;
import net.minecraft.entity.monster.ZombieVillagerEntity;
import net.minecraft.entity.passive.FoxEntity; import net.minecraft.entity.passive.FoxEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.Food; import net.minecraft.item.Food;
import net.minecraft.item.Foods; import net.minecraft.item.Foods;
import net.minecraft.item.Item; import net.minecraft.item.Item;
@ -27,6 +28,7 @@ import net.minecraft.potion.Effect;
import net.minecraft.potion.EffectInstance; import net.minecraft.potion.EffectInstance;
import net.minecraft.potion.Effects; import net.minecraft.potion.Effects;
import net.minecraft.util.Direction; import net.minecraft.util.Direction;
import net.minecraft.util.Hand;
import net.minecraft.util.IItemProvider; import net.minecraft.util.IItemProvider;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraft.util.SoundCategory; import net.minecraft.util.SoundCategory;
@ -39,7 +41,9 @@ import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.vector.Vector3d; import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.IWorld; import net.minecraft.world.IWorld;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraft.world.server.ServerWorld;
import net.minecraftforge.common.IPlantable; import net.minecraftforge.common.IPlantable;
import net.minecraftforge.common.util.FakePlayerFactory;
import net.minecraftforge.registries.IRegistryDelegate; import net.minecraftforge.registries.IRegistryDelegate;
public class PotatoCannonProjectileTypes { public class PotatoCannonProjectileTypes {
@ -64,7 +68,7 @@ public class PotatoCannonProjectileTypes {
.velocity(1.25f) .velocity(1.25f)
.knockback(0.5f) .knockback(0.5f)
.renderTumbling() .renderTumbling()
.onEntityHit(ray -> ray.getEntity().setFire(3)) .onEntityHit(setFire(3))
.registerAndAssign(Items.BAKED_POTATO), .registerAndAssign(Items.BAKED_POTATO),
CARROT = create("carrot").damage(4) CARROT = create("carrot").damage(4)
@ -107,7 +111,7 @@ public class PotatoCannonProjectileTypes {
.knockback(0.05f) .knockback(0.05f)
.velocity(1.25f) .velocity(1.25f)
.renderTumbling() .renderTumbling()
.onEntityHit(potion(Effects.POISON, 1,160)) .onEntityHit(potion(Effects.POISON, 1,160, true))
.registerAndAssign(Items.POISONOUS_POTATO), .registerAndAssign(Items.POISONOUS_POTATO),
CHORUS_FRUIT = create("chorus_fruit").damage(3) CHORUS_FRUIT = create("chorus_fruit").damage(3)
@ -115,7 +119,7 @@ public class PotatoCannonProjectileTypes {
.velocity(1.20f) .velocity(1.20f)
.knockback(0.05f) .knockback(0.05f)
.renderTumbling() .renderTumbling()
.onEntityHitRecoveryCancelable(chorusTeleport(20)) .onEntityHit(chorusTeleport(20))
.registerAndAssign(Items.CHORUS_FRUIT), .registerAndAssign(Items.CHORUS_FRUIT),
APPLE = create("apple").damage(5) APPLE = create("apple").damage(5)
@ -132,7 +136,7 @@ public class PotatoCannonProjectileTypes {
.knockback(0.1f) .knockback(0.1f)
.renderTumbling() .renderTumbling()
.soundPitch(1.1f) .soundPitch(1.1f)
.onEntityHit(potion(Effects.SLOWNESS, 2,160)) .onEntityHit(potion(Effects.SLOWNESS, 2,160, true))
.registerAndAssign(AllItems.HONEYED_APPLE.get()), .registerAndAssign(AllItems.HONEYED_APPLE.get()),
GOLDEN_APPLE = create("golden_apple").damage(1) GOLDEN_APPLE = create("golden_apple").damage(1)
@ -141,7 +145,21 @@ public class PotatoCannonProjectileTypes {
.knockback(0.05f) .knockback(0.05f)
.renderTumbling() .renderTumbling()
.soundPitch(1.1f) .soundPitch(1.1f)
.onEntityHitRecoveryCancelable(foodEffects(Foods.GOLDEN_APPLE)) .onEntityHit(ray -> {
Entity entity = ray.getEntity();
World world = entity.world;
if (!(entity instanceof ZombieVillagerEntity)
|| !((ZombieVillagerEntity) entity).isPotionActive(Effects.WEAKNESS))
return foodEffects(Foods.GOLDEN_APPLE, false).test(ray);
if (world.isRemote)
return false;
PlayerEntity dummy = FakePlayerFactory.getMinecraft((ServerWorld) world);
dummy.setHeldItem(Hand.MAIN_HAND, new ItemStack(Items.GOLDEN_APPLE));
((ZombieVillagerEntity) entity).interactMob(dummy, Hand.MAIN_HAND);
return true;
})
.registerAndAssign(Items.GOLDEN_APPLE), .registerAndAssign(Items.GOLDEN_APPLE),
ENCHANTED_GOLDEN_APPLE = create("enchanted_golden_apple").damage(1) ENCHANTED_GOLDEN_APPLE = create("enchanted_golden_apple").damage(1)
@ -150,7 +168,7 @@ public class PotatoCannonProjectileTypes {
.knockback(0.05f) .knockback(0.05f)
.renderTumbling() .renderTumbling()
.soundPitch(1.1f) .soundPitch(1.1f)
.onEntityHitRecoveryCancelable(foodEffects(Foods.ENCHANTED_GOLDEN_APPLE)) .onEntityHit(foodEffects(Foods.ENCHANTED_GOLDEN_APPLE, false))
.registerAndAssign(Items.ENCHANTED_GOLDEN_APPLE), .registerAndAssign(Items.ENCHANTED_GOLDEN_APPLE),
BEETROOT = create("beetroot").damage(2) BEETROOT = create("beetroot").damage(2)
@ -175,7 +193,7 @@ public class PotatoCannonProjectileTypes {
.velocity(1.45f) .velocity(1.45f)
.renderTumbling() .renderTumbling()
.soundPitch(1.5f) .soundPitch(1.5f)
.onEntityHit(potion(Effects.GLOWING, 1, 100)) .onEntityHit(potion(Effects.GLOWING, 1, 100, true))
.registerAndAssign(Items.GLISTERING_MELON_SLICE), .registerAndAssign(Items.GLISTERING_MELON_SLICE),
MELON_BLOCK = create("melon_block").damage(8) MELON_BLOCK = create("melon_block").damage(8)
@ -214,13 +232,13 @@ public class PotatoCannonProjectileTypes {
.soundPitch(1.0f) .soundPitch(1.0f)
.registerAndAssign(Items.CAKE), .registerAndAssign(Items.CAKE),
BLAZE_CAKE = create("blaze_cake").damage(12) BLAZE_CAKE = create("blaze_cake").damage(15)
.reloadTicks(20) .reloadTicks(20)
.knockback(0.3f) .knockback(0.3f)
.velocity(1.1f) .velocity(1.1f)
.renderTumbling() .renderTumbling()
.sticky() .sticky()
.onEntityHit(ray -> ray.getEntity().setFire(12)) .onEntityHit(setFire(12))
.soundPitch(1.0f) .soundPitch(1.0f)
.registerAndAssign(AllItems.BLAZE_CAKE.get()) .registerAndAssign(AllItems.BLAZE_CAKE.get())
; ;
@ -304,29 +322,52 @@ public class PotatoCannonProjectileTypes {
return onBlockHit.test(world, ray); return onBlockHit.test(world, ray);
} }
private static Consumer<EntityRayTraceResult> potion(Effect effect, int level, int ticks) { private static Predicate<EntityRayTraceResult> setFire(int seconds) {
return ray -> { return ray -> {
Entity entity = ray.getEntity(); ray.getEntity().setFire(seconds);
if (entity instanceof LivingEntity) return false;
((LivingEntity) entity).addPotionEffect(new EffectInstance(effect, ticks, level - 1));
}; };
} }
private static Predicate<EntityRayTraceResult> foodEffects(Food food) { private static Predicate<EntityRayTraceResult> potion(Effect effect, int level, int ticks, boolean recoverable) {
return ray -> { return ray -> {
Entity entity = ray.getEntity(); Entity entity = ray.getEntity();
if (entity.world.isRemote)
return true;
if (entity instanceof LivingEntity)
applyEffect((LivingEntity) entity, new EffectInstance(effect, ticks, level - 1));
return !recoverable;
};
}
private static Predicate<EntityRayTraceResult> foodEffects(Food food, boolean recoverable) {
return ray -> {
Entity entity = ray.getEntity();
if (entity.world.isRemote)
return true;
if (entity instanceof LivingEntity) { if (entity instanceof LivingEntity) {
for (Pair<EffectInstance, Float> effect : food.getEffects()) { for (Pair<EffectInstance, Float> effect : food.getEffects()) {
if (Create.RANDOM.nextFloat() < effect.getSecond()) if (Create.RANDOM.nextFloat() < effect.getSecond())
((LivingEntity) entity).addPotionEffect(effect.getFirst()); applyEffect((LivingEntity) entity, new EffectInstance(effect.getFirst()));
} }
} }
return true; return !recoverable;
}; };
} }
public static void applyEffect(LivingEntity entity, EffectInstance effect) {
if (effect.getPotion().isInstant())
effect.getPotion().affectEntity(null, null, entity, effect.getDuration(), 1.0);
else
entity.addPotionEffect(effect);
}
private static BiPredicate<IWorld, BlockRayTraceResult> plantCrop(IRegistryDelegate<? extends Block> cropBlock) { private static BiPredicate<IWorld, BlockRayTraceResult> plantCrop(IRegistryDelegate<? extends Block> cropBlock) {
return (world, ray) -> { return (world, ray) -> {
if (world.isRemote())
return true;
BlockPos hitPos = ray.getPos(); BlockPos hitPos = ray.getPos();
if (!world.isAreaLoaded(hitPos, 1)) if (!world.isAreaLoaded(hitPos, 1))
return true; return true;
@ -348,6 +389,9 @@ public class PotatoCannonProjectileTypes {
private static BiPredicate<IWorld, BlockRayTraceResult> placeBlockOnGround(IRegistryDelegate<? extends Block> block) { private static BiPredicate<IWorld, BlockRayTraceResult> placeBlockOnGround(IRegistryDelegate<? extends Block> block) {
return (world, ray) -> { return (world, ray) -> {
if (world.isRemote())
return true;
BlockPos hitPos = ray.getPos(); BlockPos hitPos = ray.getPos();
if (!world.isAreaLoaded(hitPos, 1)) if (!world.isAreaLoaded(hitPos, 1))
return true; return true;
@ -489,19 +533,11 @@ public class PotatoCannonProjectileTypes {
return this; return this;
} }
public Builder onEntityHitRecoveryCancelable(Predicate<EntityRayTraceResult> callback) { public Builder onEntityHit(Predicate<EntityRayTraceResult> callback) {
result.onEntityHit = callback; result.onEntityHit = callback;
return this; return this;
} }
public Builder onEntityHit(Consumer<EntityRayTraceResult> callback) {
result.onEntityHit = ray -> {
callback.accept(ray);
return false;
};
return this;
}
public Builder onBlockHit(BiPredicate<IWorld, BlockRayTraceResult> callback) { public Builder onBlockHit(BiPredicate<IWorld, BlockRayTraceResult> callback) {
result.onBlockHit = callback; result.onBlockHit = callback;
return this; return this;

View file

@ -14,6 +14,7 @@ import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityClassification; import net.minecraft.entity.EntityClassification;
import net.minecraft.entity.EntityType; import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.boss.WitherEntity;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.ServerPlayerEntity; import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.entity.projectile.DamagingProjectileEntity; import net.minecraft.entity.projectile.DamagingProjectileEntity;
@ -188,6 +189,9 @@ public class PotatoProjectileEntity extends DamagingProjectileEntity implements
pop(hit); pop(hit);
if (target instanceof WitherEntity && ((WitherEntity) target).shouldRenderOverlay())
return;
boolean targetIsEnderman = target.getType() == EntityType.ENDERMAN; boolean targetIsEnderman = target.getType() == EntityType.ENDERMAN;
int k = target.getFireTimer(); int k = target.getFireTimer();
if (this.isBurning() && !targetIsEnderman) if (this.isBurning() && !targetIsEnderman)
@ -203,7 +207,7 @@ public class PotatoProjectileEntity extends DamagingProjectileEntity implements
if (targetIsEnderman) if (targetIsEnderman)
return; return;
if (!projectileType.onEntityHit(ray)) if (!projectileType.onEntityHit(ray) && onServer)
if (rand.nextDouble() <= recoveryChance) if (rand.nextDouble() <= recoveryChance)
recoverItem(); recoverItem();
@ -271,7 +275,7 @@ public class PotatoProjectileEntity extends DamagingProjectileEntity implements
protected void onBlockHit(BlockRayTraceResult ray) { protected void onBlockHit(BlockRayTraceResult ray) {
Vector3d hit = ray.getHitVec(); Vector3d hit = ray.getHitVec();
pop(hit); pop(hit);
if (!getProjectileType().onBlockHit(world, ray)) if (!getProjectileType().onBlockHit(world, ray) && !world.isRemote)
if (rand.nextDouble() <= recoveryChance) if (rand.nextDouble() <= recoveryChance)
recoverItem(); recoverItem();
super.onBlockHit(ray); super.onBlockHit(ray);

View file

@ -170,6 +170,6 @@ public class StockpileSwitchTileEntity extends SmartTileEntity {
if (inverted == this.inverted) if (inverted == this.inverted)
return; return;
this.inverted = inverted; this.inverted = inverted;
world.updateNeighbors(pos, getBlockState().getBlock()); updatePowerAfterDelay();
} }
} }