From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: William Blake Galbreath Date: Thu, 3 Dec 2020 17:56:18 -0600 Subject: [PATCH] Lobotomize stuck villagers diff --git a/src/main/java/net/minecraft/world/entity/npc/Villager.java b/src/main/java/net/minecraft/world/entity/npc/Villager.java index 598f03c777c729b13a97d328ffb4a450415b66d6..45579a592f519b54df5761a22f4abb0deff7feb6 100644 --- a/src/main/java/net/minecraft/world/entity/npc/Villager.java +++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java @@ -139,6 +139,8 @@ public class Villager extends AbstractVillager implements ReputationEventHandler }, MemoryModuleType.MEETING_POINT, (entityvillager, holder) -> { return holder.is(PoiTypes.MEETING); }); + private boolean isLobotomized = false; public boolean isLobotomized() { return this.isLobotomized; } // Purpur + private int notLobotomizedCount = 0; // Purpur public Villager(EntityType entityType, Level world) { this(entityType, world, VillagerType.PLAINS); @@ -194,6 +196,47 @@ public class Villager extends AbstractVillager implements ReputationEventHandler protected boolean isAlwaysExperienceDropper() { return this.level.purpurConfig.villagerAlwaysDropExp; } + + private boolean checkLobotomized() { + int interval = this.level.purpurConfig.villagerLobotomizeCheckInterval; + if (this.notLobotomizedCount > 3) { + // check half as often if not lobotomized for the last 3+ consecutive checks + interval *= 2; + } + if (this.level.getGameTime() % interval == 0) { + // offset Y for short blocks like dirt_path/farmland TODO: check that it works + this.isLobotomized = !canTravelFrom(this.getBlockPosBelowThatAffectsMyMovement()); + + if (this.isLobotomized) { + this.notLobotomizedCount = 0; + } else { + this.notLobotomizedCount++; + } + } + return this.isLobotomized; + } + + private boolean canTravelFrom(BlockPos pos) { + return canTravelTo(pos.east()) || canTravelTo(pos.west()) || canTravelTo(pos.north()) || canTravelTo(pos.south()); + } + + private boolean canTravelTo(BlockPos pos) { + net.minecraft.world.level.block.state.BlockState state = this.level.getBlockStateIfLoaded(pos); + if (state == null) { + // chunk not loaded + return false; + } + net.minecraft.world.level.block.Block bottom = state.getBlock(); + if (bottom instanceof net.minecraft.world.level.block.FenceBlock || + bottom instanceof net.minecraft.world.level.block.FenceGateBlock || + bottom instanceof net.minecraft.world.level.block.WallBlock) { + // bottom block is too tall to get over + return false; + } + net.minecraft.world.level.block.Block top = level.getBlockState(pos.above()).getBlock(); + // only if both blocks have no collision + return !bottom.hasCollision && !top.hasCollision; + } // Purpur end @Override @@ -290,10 +333,25 @@ public class Villager extends AbstractVillager implements ReputationEventHandler protected void customServerAiStep() { mobTick(false); } protected void mobTick(boolean inactive) { this.level.getProfiler().push("villagerBrain"); + // Purpur start + if (this.level.purpurConfig.villagerLobotomizeEnabled) { + // treat as inactive if lobotomized + inactive = inactive || checkLobotomized(); + } else { + // clean up state for API + this.isLobotomized = false; + } + // Purpur end if (!inactive) { if ((getRider() == null || !this.isControllable())/* && this.behaviorTick++ % this.activatedPriority == 0*/) // Pufferfish // Purpur - only use brain if no rider TODO: uncomment when including Pufferfish this.getBrain().tick((ServerLevel) this.level, this); // Paper } + // Purpur start + else if (this.isLobotomized && shouldRestock()) { + // make sure we restock if needed when lobotomized + restock(); + } + // Purpur end this.level.getProfiler().pop(); if (this.assignProfessionWhenSpawned) { this.assignProfessionWhenSpawned = false; diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java index a1a8ac55e572156671e47317ba061855be79e5ac..ec3fb8865211bd7625103c37af7b96df37163a07 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java @@ -222,4 +222,11 @@ public class CraftVillager extends CraftAbstractVillager implements Villager { getHandle().getGossips().gossips.clear(); } // Paper end + + // Purpur start + @Override + public boolean isLobotomized() { + return getHandle().isLobotomized(); + } + // Purpur end } diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java index 12aac9a21d8ded7429bcda4dc62fcd52787982a8..da09ff6ee051cc811fdcbbe134126bc98b9964fa 100644 --- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java +++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java @@ -2684,6 +2684,8 @@ public class PurpurWorldConfig { public boolean villagerAllowTrading = true; public boolean villagerAlwaysDropExp = false; public int villagerMinimumDemand = 0; + public boolean villagerLobotomizeEnabled = false; + public int villagerLobotomizeCheckInterval = 100; private void villagerSettings() { villagerRidable = getBoolean("mobs.villager.ridable", villagerRidable); villagerRidableInWater = getBoolean("mobs.villager.ridable-in-water", villagerRidableInWater); @@ -2705,6 +2707,17 @@ public class PurpurWorldConfig { villagerAllowTrading = getBoolean("mobs.villager.allow-trading", villagerAllowTrading); villagerAlwaysDropExp = getBoolean("mobs.villager.always-drop-exp", villagerAlwaysDropExp); villagerMinimumDemand = getInt("mobs.villager.minimum-demand", villagerMinimumDemand); + if (PurpurConfig.version < 9) { + boolean oldValue = getBoolean("mobs.villager.lobotomize-1x1", villagerLobotomizeEnabled); + set("mobs.villager.lobotomize.enabled", oldValue); + set("mobs.villager.lobotomize-1x1", null); + } + if (PurpurConfig.version < 27) { + int oldValue = getInt("mobs.villager.lobotomize.check-interval", villagerLobotomizeCheckInterval); + set("mobs.villager.lobotomize.check-interval", oldValue == 60 ? 100 : oldValue); + } + villagerLobotomizeEnabled = getBoolean("mobs.villager.lobotomize.enabled", villagerLobotomizeEnabled); + villagerLobotomizeCheckInterval = getInt("mobs.villager.lobotomize.check-interval", villagerLobotomizeCheckInterval); } public boolean vindicatorRidable = false;