net/minecraft/world/entity/npc

This commit is contained in:
Shane Freeder 2024-12-14 16:44:13 +00:00
parent 6002fbc152
commit 3672a7d70f
No known key found for this signature in database
GPG Key ID: A3F61EA5A085289C
9 changed files with 375 additions and 404 deletions

View File

@ -1,6 +1,6 @@
--- a/net/minecraft/world/entity/npc/AbstractVillager.java
+++ b/net/minecraft/world/entity/npc/AbstractVillager.java
@@ -40,8 +40,21 @@
@@ -37,7 +_,20 @@
import net.minecraft.world.phys.Vec3;
import org.slf4j.Logger;
@ -12,7 +12,6 @@
+// CraftBukkit end
+
public abstract class AbstractVillager extends AgeableMob implements InventoryCarrier, Npc, Merchant {
+ // CraftBukkit start
+ @Override
+ public CraftMerchant getCraftMerchant() {
@ -22,16 +21,16 @@
private static final EntityDataAccessor<Integer> DATA_UNHAPPY_COUNTER = SynchedEntityData.defineId(AbstractVillager.class, EntityDataSerializers.INT);
private static final Logger LOGGER = LogUtils.getLogger();
public static final int VILLAGER_SLOT_OFFSET = 300;
@@ -50,7 +63,7 @@
@@ -46,7 +_,7 @@
private Player tradingPlayer;
@Nullable
protected MerchantOffers offers;
- private final SimpleContainer inventory = new SimpleContainer(8);
+ private final SimpleContainer inventory = new SimpleContainer(8, (org.bukkit.craftbukkit.entity.CraftAbstractVillager) this.getBukkitEntity()); // CraftBukkit add argument
+ private final SimpleContainer inventory = new SimpleContainer(8, (org.bukkit.craftbukkit.entity.CraftAbstractVillager) this.getBukkitEntity()); // CraftBukkit - add argument
public AbstractVillager(EntityType<? extends AbstractVillager> type, Level world) {
super(type, world);
@@ -101,6 +114,13 @@
public AbstractVillager(EntityType<? extends AbstractVillager> entityType, Level level) {
super(entityType, level);
@@ -99,6 +_,13 @@
return this.tradingPlayer != null;
}
@ -45,50 +44,40 @@
@Override
public MerchantOffers getOffers() {
if (this.level().isClientSide) {
@@ -121,11 +141,24 @@
@Override
public void overrideXp(int experience) {}
@@ -121,11 +_,24 @@
public void overrideXp(int xp) {
}
+ // Paper start - Add PlayerTradeEvent and PlayerPurchaseEvent
@Override
- public void notifyTrade(MerchantOffer offer) {
- offer.increaseUses();
+ public void processTrade(MerchantOffer recipe, @Nullable io.papermc.paper.event.player.PlayerPurchaseEvent event) { // The MerchantRecipe passed in here is the one set by the PlayerPurchaseEvent
+ @Override
+ public void processTrade(MerchantOffer offer, @Nullable io.papermc.paper.event.player.PlayerPurchaseEvent event) { // The MerchantRecipe passed in here is the one set by the PlayerPurchaseEvent
+ if (event == null || event.willIncreaseTradeUses()) {
+ recipe.increaseUses();
+ offer.increaseUses();
+ }
+ if (event == null || event.isRewardingExp()) {
+ this.rewardTradeXp(recipe);
+ this.rewardTradeXp(offer);
+ }
+ this.notifyTrade(recipe);
+ this.notifyTrade(offer);
+ }
+ // Paper end - Add PlayerTradeEvent and PlayerPurchaseEvent
+
+ @Override
+ public void notifyTrade(MerchantOffer offer) {
@Override
public void notifyTrade(MerchantOffer offer) {
- offer.increaseUses();
+ // offer.increaseUses(); // Paper - Add PlayerTradeEvent and PlayerPurchaseEvent
this.ambientSoundTime = -this.getAmbientSoundInterval();
- this.rewardTradeXp(offer);
+ // this.rewardTradeXp(offer); // Paper - Add PlayerTradeEvent and PlayerPurchaseEvent
if (this.tradingPlayer instanceof ServerPlayer) {
CriteriaTriggers.TRADE.trigger((ServerPlayer) this.tradingPlayer, this, offer.getResult());
CriteriaTriggers.TRADE.trigger((ServerPlayer)this.tradingPlayer, this, offer.getResult());
}
@@ -179,7 +212,7 @@
public void readAdditionalSaveData(CompoundTag nbt) {
super.readAdditionalSaveData(nbt);
if (nbt.contains("Offers")) {
- DataResult dataresult = MerchantOffers.CODEC.parse(this.registryAccess().createSerializationContext(NbtOps.INSTANCE), nbt.get("Offers"));
+ DataResult<MerchantOffers> dataresult = MerchantOffers.CODEC.parse(this.registryAccess().createSerializationContext(NbtOps.INSTANCE), nbt.get("Offers")); // CraftBukkit - decompile error
Logger logger = AbstractVillager.LOGGER;
Objects.requireNonNull(logger);
@@ -246,7 +279,20 @@
MerchantOffer merchantrecipe = ((VillagerTrades.ItemListing) arraylist.remove(this.random.nextInt(arraylist.size()))).getOffer(this, this.random);
if (merchantrecipe != null) {
- recipeList.add(merchantrecipe);
@@ -236,7 +_,20 @@
while (i < maxNumbers && !list.isEmpty()) {
MerchantOffer offer = list.remove(this.random.nextInt(list.size())).getOffer(this, this.random);
if (offer != null) {
- givenMerchantOffers.add(offer);
+ // CraftBukkit start
+ VillagerAcquireTradeEvent event = new VillagerAcquireTradeEvent((org.bukkit.entity.AbstractVillager) this.getBukkitEntity(), merchantrecipe.asBukkit());
+ VillagerAcquireTradeEvent event = new VillagerAcquireTradeEvent((org.bukkit.entity.AbstractVillager) this.getBukkitEntity(), offer.asBukkit());
+ // Suppress during worldgen
+ if (this.valid) {
+ Bukkit.getPluginManager().callEvent(event);
@ -97,10 +86,10 @@
+ // Paper start - Fix crash from invalid ingredient list
+ final CraftMerchantRecipe craftMerchantRecipe = CraftMerchantRecipe.fromBukkit(event.getRecipe());
+ if (craftMerchantRecipe.getIngredients().isEmpty()) return;
+ recipeList.add(craftMerchantRecipe.toMinecraft());
+ givenMerchantOffers.add(craftMerchantRecipe.toMinecraft());
+ // Paper end - Fix crash from invalid ingredient list
+ }
+ // CraftBukkit end
++j;
i++;
}
}

View File

@ -1,12 +1,12 @@
--- a/net/minecraft/world/entity/npc/CatSpawner.java
+++ b/net/minecraft/world/entity/npc/CatSpawner.java
@@ -82,8 +82,8 @@
@@ -82,8 +_,8 @@
if (cat == null) {
return 0;
} else {
+ cat.moveTo(pos, 0.0F, 0.0F); // Paper - move up - Fix MC-147659
cat.finalizeSpawn(world, world.getCurrentDifficultyAt(pos), EntitySpawnReason.NATURAL, null);
cat.finalizeSpawn(serverLevel, serverLevel.getCurrentDifficultyAt(pos), EntitySpawnReason.NATURAL, null);
- cat.moveTo(pos, 0.0F, 0.0F);
world.addFreshEntityWithPassengers(cat);
serverLevel.addFreshEntityWithPassengers(cat);
return 1;
}

View File

@ -1,6 +1,6 @@
--- a/net/minecraft/world/entity/npc/InventoryCarrier.java
+++ b/net/minecraft/world/entity/npc/InventoryCarrier.java
@@ -8,6 +8,10 @@
@@ -8,6 +_,10 @@
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.item.ItemStack;
@ -9,27 +9,26 @@
+// CraftBukkit end
+
public interface InventoryCarrier {
String TAG_INVENTORY = "Inventory";
@@ -25,13 +29,20 @@
@@ -22,12 +_,19 @@
return;
}
+ // CraftBukkit start
+ ItemStack remaining = new SimpleContainer(inventorysubcontainer).addItem(itemstack);
+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityPickupItemEvent(entity, item, remaining.getCount(), false).isCancelled()) {
+ ItemStack remaining = new SimpleContainer(inventory).addItem(item);
+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityPickupItemEvent(mob, itemEntity, remaining.getCount(), false).isCancelled()) {
+ return;
+ }
+ // CraftBukkit end
+
entity.onItemPickup(item);
int i = itemstack.getCount();
ItemStack itemstack1 = inventorysubcontainer.addItem(itemstack);
entity.take(item, i - itemstack1.getCount());
if (itemstack1.isEmpty()) {
- item.discard();
+ item.discard(EntityRemoveEvent.Cause.PICKUP); // CraftBukkit - add Bukkit remove cause
mob.onItemPickup(itemEntity);
int count = item.getCount();
ItemStack itemStack = inventory.addItem(item);
mob.take(itemEntity, count - itemStack.getCount());
if (itemStack.isEmpty()) {
- itemEntity.discard();
+ itemEntity.discard(EntityRemoveEvent.Cause.PICKUP); // CraftBukkit - add Bukkit remove cause
} else {
itemstack.setCount(itemstack1.getCount());
item.setCount(itemStack.getCount());
}

View File

@ -0,0 +1,188 @@
--- a/net/minecraft/world/entity/npc/Villager.java
+++ b/net/minecraft/world/entity/npc/Villager.java
@@ -90,6 +_,14 @@
import net.minecraft.world.phys.AABB;
import org.slf4j.Logger;
+// CraftBukkit start
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.event.CraftEventFactory;
+import org.bukkit.event.entity.EntityRemoveEvent;
+import org.bukkit.event.entity.EntityTransformEvent;
+import org.bukkit.event.entity.VillagerReplenishTradeEvent;
+// CraftBukkit end
+
public class Villager extends AbstractVillager implements ReputationEventHandler, VillagerDataHolder {
private static final Logger LOGGER = LogUtils.getLogger();
private static final EntityDataAccessor<VillagerData> DATA_VILLAGER_DATA = SynchedEntityData.defineId(Villager.class, EntityDataSerializers.VILLAGER_DATA);
@@ -257,6 +_,17 @@
return this.assignProfessionWhenSpawned;
}
+ // Spigot Start
+ @Override
+ public void inactiveTick() {
+ // SPIGOT-3874, SPIGOT-3894, SPIGOT-3846, SPIGOT-5286 :(
+ if (this.level().spigotConfig.tickInactiveVillagers && this.isEffectiveAi()) {
+ this.customServerAiStep((ServerLevel) this.level());
+ }
+ super.inactiveTick();
+ }
+ // Spigot End
+
@Override
protected void customServerAiStep(ServerLevel level) {
ProfilerFiller profilerFiller = Profiler.get();
@@ -275,7 +_,7 @@
this.increaseProfessionLevelOnUpdate = false;
}
- this.addEffect(new MobEffectInstance(MobEffects.REGENERATION, 200, 0));
+ this.addEffect(new MobEffectInstance(MobEffects.REGENERATION, 200, 0), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.VILLAGER_TRADE); // CraftBukkit
}
}
@@ -384,7 +_,13 @@
this.updateDemand();
for (MerchantOffer merchantOffer : this.getOffers()) {
- merchantOffer.resetUses();
+ // CraftBukkit start
+ VillagerReplenishTradeEvent event = new VillagerReplenishTradeEvent((org.bukkit.entity.Villager) this.getBukkitEntity(), merchantOffer.asBukkit());
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ merchantOffer.resetUses();
+ }
+ // CraftBukkit end
}
this.resendOffersToTradingPlayer();
@@ -445,7 +_,13 @@
int i = 2 - this.numberOfRestocksToday;
if (i > 0) {
for (MerchantOffer merchantOffer : this.getOffers()) {
- merchantOffer.resetUses();
+ // CraftBukkit start
+ VillagerReplenishTradeEvent event = new VillagerReplenishTradeEvent((org.bukkit.entity.Villager) this.getBukkitEntity(), merchantOffer.asBukkit());
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ merchantOffer.resetUses();
+ }
+ // CraftBukkit end
}
}
@@ -466,6 +_,7 @@
int playerReputation = this.getPlayerReputation(player);
if (playerReputation != 0) {
for (MerchantOffer merchantOffer : this.getOffers()) {
+ if (merchantOffer.ignoreDiscounts) continue; // Paper - Add ignore discounts API
merchantOffer.addToSpecialPriceDiff(-Mth.floor(playerReputation * merchantOffer.getPriceMultiplier()));
}
}
@@ -475,6 +_,7 @@
int amplifier = effect.getAmplifier();
for (MerchantOffer merchantOffer1 : this.getOffers()) {
+ if (merchantOffer1.ignoreDiscounts) continue; // Paper - Add ignore discounts API
double d = 0.3 + 0.0625 * amplifier;
int i = (int)Math.floor(d * merchantOffer1.getBaseCostA().getCount());
merchantOffer1.addToSpecialPriceDiff(-Math.max(i, 1));
@@ -559,7 +_,7 @@
}
@Override
- protected SoundEvent getDeathSound() {
+ public SoundEvent getDeathSound() {
return SoundEvents.VILLAGER_DEATH;
}
@@ -594,7 +_,7 @@
}
if (offer.shouldRewardExp()) {
- this.level().addFreshEntity(new ExperienceOrb(this.level(), this.getX(), this.getY() + 0.5, this.getZ(), i));
+ this.level().addFreshEntity(new ExperienceOrb(this.level(), this.getX(), this.getY() + 0.5, this.getZ(), i, org.bukkit.entity.ExperienceOrb.SpawnReason.VILLAGER_TRADE, this.getTradingPlayer(), this)); // Paper
}
}
@@ -612,7 +_,7 @@
@Override
public void die(DamageSource cause) {
- LOGGER.info("Villager {} died, message: '{}'", this, cause.getLocalizedDeathMessage(this).getString());
+ if (org.spigotmc.SpigotConfig.logVillagerDeaths) LOGGER.info("Villager {} died, message: '{}'", this, cause.getLocalizedDeathMessage(this).getString()); // Spigot
Entity entity = cause.getEntity();
if (entity != null) {
this.tellWitnessesThatIWasMurdered(entity);
@@ -782,12 +_,19 @@
@Override
public void thunderHit(ServerLevel level, LightningBolt lightning) {
if (level.getDifficulty() != Difficulty.PEACEFUL) {
- LOGGER.info("Villager {} was struck by lightning {}.", this, lightning);
+ // Paper - Add EntityZapEvent; move log down, event can cancel
Witch witch = this.convertTo(EntityType.WITCH, ConversionParams.single(this, false, false), mob -> {
+ // Paper start - Add EntityZapEvent
+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityZapEvent(this, lightning, mob).isCancelled()) {
+ return false;
+ }
+ if (org.spigotmc.SpigotConfig.logVillagerDeaths) Villager.LOGGER.info("Villager {} was struck by lightning {}.", this, lightning); // Move down
+ // Paper end - Add EntityZapEvent
mob.finalizeSpawn(level, level.getCurrentDifficultyAt(mob.blockPosition()), EntitySpawnReason.CONVERSION, null);
mob.setPersistenceRequired();
this.releaseAllPois();
- });
+ return true; // Paper start - Add EntityZapEvent
+ }, EntityTransformEvent.TransformReason.LIGHTNING, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.LIGHTNING); // CraftBukkit
if (witch == null) {
super.thunderHit(level, lightning);
}
@@ -827,6 +_,12 @@
@Override
protected void updateTrades() {
+ // Paper start - More vanilla friendly methods to update trades
+ updateTrades(TRADES_PER_LEVEL);
+ }
+
+ public boolean updateTrades(int amount) {
+ // Paper end - More vanilla friendly methods to update trades
VillagerData villagerData = this.getVillagerData();
Int2ObjectMap<VillagerTrades.ItemListing[]> map1;
if (this.level().enabledFeatures().contains(FeatureFlags.TRADE_REBALANCE)) {
@@ -840,9 +_,11 @@
VillagerTrades.ItemListing[] itemListings = map1.get(villagerData.getLevel());
if (itemListings != null) {
MerchantOffers offers = this.getOffers();
- this.addOffersFromItemListings(offers, itemListings, 2);
+ this.addOffersFromItemListings(offers, itemListings, amount); // Paper - More vanilla friendly methods to update trades
+ return true; // Paper - More vanilla friendly methods to update trades
}
}
+ return false; // Paper - More vanilla friendly methods to update trades
}
public void gossip(ServerLevel serverLevel, Villager target, long gameTime) {
@@ -871,7 +_,7 @@
List<Villager> entitiesOfClass = serverLevel.getEntitiesOfClass(Villager.class, aabb);
List<Villager> list = entitiesOfClass.stream().filter(villager -> villager.wantsToSpawnGolem(gameTime)).limit(5L).toList();
if (list.size() >= minVillagerAmount) {
- if (!SpawnUtil.trySpawnMob(
+ if (SpawnUtil.trySpawnMob( // Paper - Set Golem Last Seen to stop it from spawning another one - switch to isPresent
EntityType.IRON_GOLEM,
EntitySpawnReason.MOB_SUMMONED,
serverLevel,
@@ -880,9 +_,11 @@
8,
6,
SpawnUtil.Strategy.LEGACY_IRON_GOLEM,
- false
+ false,
+ org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.VILLAGE_DEFENSE, // CraftBukkit,
+ () -> {GolemSensor.golemDetected(this);} // Paper - Set Golem Last Seen to stop it from spawning another one
)
- .isEmpty()) {
+ .isPresent()) { // Paper - Set Golem Last Seen to stop it from spawning another one - switch to isPresent
entitiesOfClass.forEach(GolemSensor::golemDetected);
}
}

View File

@ -1,12 +1,12 @@
--- a/net/minecraft/world/entity/npc/VillagerTrades.java
+++ b/net/minecraft/world/entity/npc/VillagerTrades.java
@@ -1834,7 +1834,8 @@
@@ -1844,7 +_,8 @@
return null;
} else {
ServerLevel serverLevel = (ServerLevel)entity.level();
- BlockPos blockPos = serverLevel.findNearestMapStructure(this.destination, entity.blockPosition(), 100, true);
ServerLevel serverLevel = (ServerLevel)trader.level();
- BlockPos blockPos = serverLevel.findNearestMapStructure(this.destination, trader.blockPosition(), 100, true);
+ if (!serverLevel.paperConfig().environment.treasureMaps.enabled) return null; // Paper - Configurable cartographer treasure maps
+ BlockPos blockPos = serverLevel.findNearestMapStructure(this.destination, entity.blockPosition(), 100, !serverLevel.paperConfig().environment.treasureMaps.findAlreadyDiscoveredVillager); // Paper - Configurable cartographer treasure maps
+ BlockPos blockPos = serverLevel.findNearestMapStructure(this.destination, trader.blockPosition(), 100, !serverLevel.paperConfig().environment.treasureMaps.findAlreadyDiscoveredVillager); // Paper - Configurable cartographer treasure maps
if (blockPos != null) {
ItemStack itemStack = MapItem.create(serverLevel, blockPos.getX(), blockPos.getZ(), (byte)2, true, true);
MapItem.renderBiomePreviewMap(serverLevel, itemStack);

View File

@ -1,6 +1,6 @@
--- a/net/minecraft/world/entity/npc/WanderingTrader.java
+++ b/net/minecraft/world/entity/npc/WanderingTrader.java
@@ -48,25 +48,38 @@
@@ -47,11 +_,23 @@
import net.minecraft.world.phys.Vec3;
import org.apache.commons.lang3.tuple.Pair;
@ -12,9 +12,8 @@
+import org.bukkit.event.entity.EntityRemoveEvent;
+import org.bukkit.event.entity.VillagerAcquireTradeEvent;
+// CraftBukkit end
+public class WanderingTrader extends net.minecraft.world.entity.npc.AbstractVillager implements Consumable.OverrideConsumeSound {
+
+public class WanderingTrader extends net.minecraft.world.entity.npc.AbstractVillager implements Consumable.OverrideConsumeSound {
private static final int NUMBER_OF_TRADE_OFFERS = 5;
@Nullable
private BlockPos wanderTarget;
@ -24,57 +23,68 @@
+ public boolean canDrinkMilk = true;
+ // Paper end - Add more WanderingTrader API
public WanderingTrader(EntityType<? extends WanderingTrader> type, Level world) {
super(type, world);
+ //this.setDespawnDelay(48000); // CraftBukkit - set default from MobSpawnerTrader // Paper - move back to MobSpawnerTrader - Vanilla behavior is that only traders spawned by it have this value set.
}
@Override
protected void registerGoals() {
this.goalSelector.addGoal(0, new FloatGoal(this));
this.goalSelector.addGoal(0, new UseItemGoal<>(this, PotionContents.createItemStack(Items.POTION, Potions.INVISIBILITY), SoundEvents.WANDERING_TRADER_DISAPPEARED, (entityvillagertrader) -> {
- return this.level().isNight() && !entityvillagertrader.isInvisible();
+ return this.canDrinkPotion && this.level().isNight() && !entityvillagertrader.isInvisible(); // Paper - Add more WanderingTrader API
}));
this.goalSelector.addGoal(0, new UseItemGoal<>(this, new ItemStack(Items.MILK_BUCKET), SoundEvents.WANDERING_TRADER_REAPPEARED, (entityvillagertrader) -> {
- return this.level().isDay() && entityvillagertrader.isInvisible();
+ return this.canDrinkMilk && this.level().isDay() && entityvillagertrader.isInvisible(); // Paper - Add more WanderingTrader API
}));
public WanderingTrader(EntityType<? extends WanderingTrader> entityType, Level level) {
super(entityType, level);
@@ -67,7 +_,7 @@
this,
PotionContents.createItemStack(Items.POTION, Potions.INVISIBILITY),
SoundEvents.WANDERING_TRADER_DISAPPEARED,
- wanderingTrader -> this.level().isNight() && !wanderingTrader.isInvisible()
+ wanderingTrader -> this.canDrinkPotion && this.level().isNight() && !wanderingTrader.isInvisible() // Paper - Add more WanderingTrader API
)
);
this.goalSelector
@@ -77,7 +_,7 @@
this,
new ItemStack(Items.MILK_BUCKET),
SoundEvents.WANDERING_TRADER_REAPPEARED,
- wanderingTrader -> this.level().isDay() && wanderingTrader.isInvisible()
+ wanderingTrader -> this.canDrinkMilk && this.level().isDay() && wanderingTrader.isInvisible() // Paper - Add more WanderingTrader API
)
);
this.goalSelector.addGoal(1, new TradeWithPlayerGoal(this));
this.goalSelector.addGoal(1, new AvoidEntityGoal<>(this, Zombie.class, 8.0F, 0.5D, 0.5D));
@@ -137,7 +150,16 @@
MerchantOffer merchantrecipe = villagertrades_imerchantrecipeoption.getOffer(this, this.random);
if (merchantrecipe != null) {
- merchantrecipelist.add(merchantrecipe);
@@ -145,7 +_,16 @@
VillagerTrades.ItemListing itemListing = itemListings1[randomInt];
MerchantOffer offer = itemListing.getOffer(this, this.random);
if (offer != null) {
- offers.add(offer);
+ // CraftBukkit start
+ VillagerAcquireTradeEvent event = new VillagerAcquireTradeEvent((AbstractVillager) this.getBukkitEntity(), merchantrecipe.asBukkit());
+ VillagerAcquireTradeEvent event = new VillagerAcquireTradeEvent((AbstractVillager) this.getBukkitEntity(), offer.asBukkit());
+ // Suppress during worldgen
+ if (this.valid) {
+ Bukkit.getPluginManager().callEvent(event);
+ }
+ if (!event.isCancelled()) {
+ merchantrecipelist.add(CraftMerchantRecipe.fromBukkit(event.getRecipe()).toMinecraft());
+ offers.add(CraftMerchantRecipe.fromBukkit(event.getRecipe()).toMinecraft());
+ }
+ // CraftBukkit end
}
}
@@ -190,7 +212,7 @@
}
@@ -189,7 +_,7 @@
protected void rewardTradeXp(MerchantOffer offer) {
if (offer.shouldRewardExp()) {
int i = 3 + this.random.nextInt(4);
- this.level().addFreshEntity(new ExperienceOrb(this.level(), this.getX(), this.getY() + 0.5D, this.getZ(), i));
+ this.level().addFreshEntity(new ExperienceOrb(this.level(), this.getX(), this.getY() + 0.5D, this.getZ(), i, org.bukkit.entity.ExperienceOrb.SpawnReason.VILLAGER_TRADE, this.getTradingPlayer(), this)); // Paper
- this.level().addFreshEntity(new ExperienceOrb(this.level(), this.getX(), this.getY() + 0.5, this.getZ(), i));
+ this.level().addFreshEntity(new ExperienceOrb(this.level(), this.getX(), this.getY() + 0.5, this.getZ(), i, org.bukkit.entity.ExperienceOrb.SpawnReason.VILLAGER_TRADE, this.getTradingPlayer(), this)); // Paper
}
}
@@ -244,7 +266,7 @@
@@ -204,7 +_,7 @@
}
@Override
- protected SoundEvent getDeathSound() {
+ public SoundEvent getDeathSound() {
return SoundEvents.WANDERING_TRADER_DEATH;
}
@@ -241,7 +_,7 @@
private void maybeDespawn() {
if (this.despawnDelay > 0 && !this.isTrading() && --this.despawnDelay == 0) {
- this.discard();
+ this.discard(EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause
}
}

View File

@ -0,0 +1,96 @@
--- a/net/minecraft/world/entity/npc/WanderingTraderSpawner.java
+++ b/net/minecraft/world/entity/npc/WanderingTraderSpawner.java
@@ -38,41 +_,50 @@
public WanderingTraderSpawner(ServerLevelData serverLevelData) {
this.serverLevelData = serverLevelData;
- this.tickDelay = 1200;
- this.spawnDelay = serverLevelData.getWanderingTraderSpawnDelay();
- this.spawnChance = serverLevelData.getWanderingTraderSpawnChance();
- if (this.spawnDelay == 0 && this.spawnChance == 0) {
- this.spawnDelay = 24000;
- serverLevelData.setWanderingTraderSpawnDelay(this.spawnDelay);
- this.spawnChance = 25;
- serverLevelData.setWanderingTraderSpawnChance(this.spawnChance);
- }
+ // Paper start - Add Wandering Trader spawn rate config options
+ this.tickDelay = Integer.MIN_VALUE;
+ // this.spawnDelay = serverLevelData.getWanderingTraderSpawnDelay();
+ // this.spawnChance = serverLevelData.getWanderingTraderSpawnChance();
+ // if (this.spawnDelay == 0 && this.spawnChance == 0) {
+ // this.spawnDelay = 24000;
+ // serverLevelData.setWanderingTraderSpawnDelay(this.spawnDelay);
+ // this.spawnChance = 25;
+ // serverLevelData.setWanderingTraderSpawnChance(this.spawnChance);
+ // }
+ // Paper end - Add Wandering Trader spawn rate config options
}
@Override
public int tick(ServerLevel level, boolean spawnHostiles, boolean spawnPassives) {
+ // Paper start - Add Wandering Trader spawn rate config options
+ if (this.tickDelay == Integer.MIN_VALUE) {
+ this.tickDelay = level.paperConfig().entities.spawning.wanderingTrader.spawnMinuteLength;
+ this.spawnDelay = level.paperConfig().entities.spawning.wanderingTrader.spawnDayLength;
+ this.spawnChance = level.paperConfig().entities.spawning.wanderingTrader.spawnChanceMin;
+ }
if (!level.getGameRules().getBoolean(GameRules.RULE_DO_TRADER_SPAWNING)) {
return 0;
- } else if (--this.tickDelay > 0) {
+ } else if (--this.tickDelay - 1 > 0) {
+ this.tickDelay = this.tickDelay - 1;
return 0;
} else {
- this.tickDelay = 1200;
- this.spawnDelay -= 1200;
- this.serverLevelData.setWanderingTraderSpawnDelay(this.spawnDelay);
+ this.tickDelay = level.paperConfig().entities.spawning.wanderingTrader.spawnMinuteLength;
+ this.spawnDelay = this.spawnDelay - level.paperConfig().entities.spawning.wanderingTrader.spawnMinuteLength;
+ //this.serverLevelData.setWanderingTraderSpawnDelay(this.spawnDelay); // Paper - We don't need to save this value to disk if it gets set back to a hardcoded value anyways
if (this.spawnDelay > 0) {
return 0;
} else {
- this.spawnDelay = 24000;
+ this.spawnDelay = level.paperConfig().entities.spawning.wanderingTrader.spawnDayLength;
if (!level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING)) {
return 0;
} else {
int i = this.spawnChance;
- this.spawnChance = Mth.clamp(this.spawnChance + 25, 25, 75);
- this.serverLevelData.setWanderingTraderSpawnChance(this.spawnChance);
+ this.spawnChance = Mth.clamp(this.spawnChance + level.paperConfig().entities.spawning.wanderingTrader.spawnChanceFailureIncrement, level.paperConfig().entities.spawning.wanderingTrader.spawnChanceMin, level.paperConfig().entities.spawning.wanderingTrader.spawnChanceMax);
+ //this.serverLevelData.setWanderingTraderSpawnChance(this.spawnChance); // Paper - We don't need to save this value to disk if it gets set back to a hardcoded value anyways
if (this.random.nextInt(100) > i) {
return 0;
} else if (this.spawn(level)) {
- this.spawnChance = 25;
+ this.spawnChance = level.paperConfig().entities.spawning.wanderingTrader.spawnChanceMin;
return 1;
} else {
return 0;
@@ -100,14 +_,14 @@
return false;
}
- WanderingTrader wanderingTrader = EntityType.WANDERING_TRADER.spawn(serverLevel, blockPos2, EntitySpawnReason.EVENT);
+ WanderingTrader wanderingTrader = EntityType.WANDERING_TRADER.spawn(serverLevel, trader -> trader.setDespawnDelay(48000), blockPos2, EntitySpawnReason.EVENT, false, false, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.NATURAL); // CraftBukkit // Paper - set despawnTimer before spawn events called
if (wanderingTrader != null) {
for (int i1 = 0; i1 < 2; i1++) {
this.tryToSpawnLlamaFor(serverLevel, wanderingTrader, 4);
}
this.serverLevelData.setWanderingTraderId(wanderingTrader.getUUID());
- wanderingTrader.setDespawnDelay(48000);
+ //wanderingTrader.setDespawnDelay(48000); // CraftBukkit - moved to EntityVillagerTrader constructor. This lets the value be modified by plugins on CreatureSpawnEvent
wanderingTrader.setWanderTarget(blockPos1);
wanderingTrader.restrictTo(blockPos1, 16);
return true;
@@ -121,7 +_,7 @@
private void tryToSpawnLlamaFor(ServerLevel serverLevel, WanderingTrader trader, int maxDistance) {
BlockPos blockPos = this.findSpawnPositionNear(serverLevel, trader.blockPosition(), maxDistance);
if (blockPos != null) {
- TraderLlama traderLlama = EntityType.TRADER_LLAMA.spawn(serverLevel, blockPos, EntitySpawnReason.EVENT);
+ TraderLlama traderLlama = EntityType.TRADER_LLAMA.spawn(serverLevel, blockPos, EntitySpawnReason.EVENT, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.NATURAL); // CraftBukkit
if (traderLlama != null) {
traderLlama.setLeashedTo(trader, true);
}

View File

@ -1,211 +0,0 @@
--- a/net/minecraft/world/entity/npc/Villager.java
+++ b/net/minecraft/world/entity/npc/Villager.java
@@ -93,6 +93,14 @@
import net.minecraft.world.phys.AABB;
import org.slf4j.Logger;
+// CraftBukkit start
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.event.CraftEventFactory;
+import org.bukkit.event.entity.EntityRemoveEvent;
+import org.bukkit.event.entity.EntityTransformEvent;
+import org.bukkit.event.entity.VillagerReplenishTradeEvent;
+// CraftBukkit end
+
public class Villager extends AbstractVillager implements ReputationEventHandler, VillagerDataHolder {
private static final Logger LOGGER = LogUtils.getLogger();
@@ -150,7 +158,7 @@
@Override
public Brain<Villager> getBrain() {
- return super.getBrain();
+ return (Brain<Villager>) super.getBrain(); // CraftBukkit - decompile error
}
@Override
@@ -216,7 +224,18 @@
return this.assignProfessionWhenSpawned;
}
+ // Spigot Start
@Override
+ public void inactiveTick() {
+ // SPIGOT-3874, SPIGOT-3894, SPIGOT-3846, SPIGOT-5286 :(
+ if (this.level().spigotConfig.tickInactiveVillagers && this.isEffectiveAi()) {
+ this.customServerAiStep((ServerLevel) this.level());
+ }
+ super.inactiveTick();
+ }
+ // Spigot End
+
+ @Override
protected void customServerAiStep(ServerLevel world) {
ProfilerFiller gameprofilerfiller = Profiler.get();
@@ -235,7 +254,7 @@
this.increaseProfessionLevelOnUpdate = false;
}
- this.addEffect(new MobEffectInstance(MobEffects.REGENERATION, 200, 0));
+ this.addEffect(new MobEffectInstance(MobEffects.REGENERATION, 200, 0), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.VILLAGER_TRADE); // CraftBukkit
}
}
@@ -360,7 +379,13 @@
while (iterator.hasNext()) {
MerchantOffer merchantrecipe = (MerchantOffer) iterator.next();
- merchantrecipe.resetUses();
+ // CraftBukkit start
+ VillagerReplenishTradeEvent event = new VillagerReplenishTradeEvent((org.bukkit.entity.Villager) this.getBukkitEntity(), merchantrecipe.asBukkit());
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ merchantrecipe.resetUses();
+ }
+ // CraftBukkit end
}
this.resendOffersToTradingPlayer();
@@ -429,7 +454,13 @@
while (iterator.hasNext()) {
MerchantOffer merchantrecipe = (MerchantOffer) iterator.next();
- merchantrecipe.resetUses();
+ // CraftBukkit start
+ VillagerReplenishTradeEvent event = new VillagerReplenishTradeEvent((org.bukkit.entity.Villager) this.getBukkitEntity(), merchantrecipe.asBukkit());
+ Bukkit.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ merchantrecipe.resetUses();
+ }
+ // CraftBukkit end
}
}
@@ -459,6 +490,7 @@
while (iterator.hasNext()) {
MerchantOffer merchantrecipe = (MerchantOffer) iterator.next();
+ if (merchantrecipe.ignoreDiscounts) continue; // Paper - Add ignore discounts API
merchantrecipe.addToSpecialPriceDiff(-Mth.floor((float) i * merchantrecipe.getPriceMultiplier()));
}
@@ -471,6 +503,7 @@
while (iterator1.hasNext()) {
MerchantOffer merchantrecipe1 = (MerchantOffer) iterator1.next();
+ if (merchantrecipe1.ignoreDiscounts) continue; // Paper - Add ignore discounts API
double d0 = 0.3D + 0.0625D * (double) j;
int k = (int) Math.floor(d0 * (double) merchantrecipe1.getBaseCostA().getCount());
@@ -489,7 +522,7 @@
@Override
public void addAdditionalSaveData(CompoundTag nbt) {
super.addAdditionalSaveData(nbt);
- DataResult dataresult = VillagerData.CODEC.encodeStart(NbtOps.INSTANCE, this.getVillagerData());
+ DataResult<Tag> dataresult = VillagerData.CODEC.encodeStart(NbtOps.INSTANCE, this.getVillagerData()); // CraftBukkit - decompile error
Logger logger = Villager.LOGGER;
Objects.requireNonNull(logger);
@@ -512,7 +545,7 @@
public void readAdditionalSaveData(CompoundTag nbt) {
super.readAdditionalSaveData(nbt);
if (nbt.contains("VillagerData", 10)) {
- DataResult dataresult = VillagerData.CODEC.parse(NbtOps.INSTANCE, nbt.get("VillagerData"));
+ DataResult<VillagerData> dataresult = VillagerData.CODEC.parse(new Dynamic(NbtOps.INSTANCE, nbt.get("VillagerData")));
Logger logger = Villager.LOGGER;
Objects.requireNonNull(logger);
@@ -599,7 +632,7 @@
}
if (offer.shouldRewardExp()) {
- this.level().addFreshEntity(new ExperienceOrb(this.level(), this.getX(), this.getY() + 0.5D, this.getZ(), i));
+ this.level().addFreshEntity(new ExperienceOrb(this.level(), this.getX(), this.getY() + 0.5D, this.getZ(), i, org.bukkit.entity.ExperienceOrb.SpawnReason.VILLAGER_TRADE, this.getTradingPlayer(), this)); // Paper
}
}
@@ -618,7 +651,7 @@
@Override
public void die(DamageSource damageSource) {
- Villager.LOGGER.info("Villager {} died, message: '{}'", this, damageSource.getLocalizedDeathMessage(this).getString());
+ if (org.spigotmc.SpigotConfig.logVillagerDeaths) Villager.LOGGER.info("Villager {} died, message: '{}'", this, damageSource.getLocalizedDeathMessage(this).getString()); // Spigot
Entity entity = damageSource.getEntity();
if (entity != null) {
@@ -803,12 +836,19 @@
@Override
public void thunderHit(ServerLevel world, LightningBolt lightning) {
if (world.getDifficulty() != Difficulty.PEACEFUL) {
- Villager.LOGGER.info("Villager {} was struck by lightning {}.", this, lightning);
+ // Paper - Add EntityZapEvent; move log down, event can cancel
Witch entitywitch = (Witch) this.convertTo(EntityType.WITCH, ConversionParams.single(this, false, false), (entitywitch1) -> {
+ // Paper start - Add EntityZapEvent
+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityZapEvent(this, lightning, entitywitch1).isCancelled()) {
+ return false;
+ }
+ if (org.spigotmc.SpigotConfig.logVillagerDeaths) Villager.LOGGER.info("Villager {} was struck by lightning {}.", this, lightning); // Move down
+ // Paper end - Add EntityZapEvent
entitywitch1.finalizeSpawn(world, world.getCurrentDifficultyAt(entitywitch1.blockPosition()), EntitySpawnReason.CONVERSION, (SpawnGroupData) null);
entitywitch1.setPersistenceRequired();
this.releaseAllPois();
- });
+ return true; // Paper start - Add EntityZapEvent
+ }, EntityTransformEvent.TransformReason.LIGHTNING, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.LIGHTNING); // CraftBukkit
if (entitywitch == null) {
super.thunderHit(world, lightning);
@@ -855,6 +895,12 @@
@Override
protected void updateTrades() {
+ // Paper start - More vanilla friendly methods to update trades
+ updateTrades(TRADES_PER_LEVEL);
+ }
+
+ public boolean updateTrades(int amount) {
+ // Paper end - More vanilla friendly methods to update trades
VillagerData villagerdata = this.getVillagerData();
Int2ObjectMap int2objectmap;
@@ -872,9 +918,11 @@
if (avillagertrades_imerchantrecipeoption != null) {
MerchantOffers merchantrecipelist = this.getOffers();
- this.addOffersFromItemListings(merchantrecipelist, avillagertrades_imerchantrecipeoption, 2);
+ this.addOffersFromItemListings(merchantrecipelist, avillagertrades_imerchantrecipeoption, amount); // Paper - More vanilla friendly methods to update trades
+ return true; // Paper - More vanilla friendly methods to update trades
}
}
+ return false; // Paper - More vanilla friendly methods to update trades
}
public void gossip(ServerLevel world, Villager villager, long time) {
@@ -906,7 +954,7 @@
}).limit(5L).toList();
if (list1.size() >= requiredCount) {
- if (!SpawnUtil.trySpawnMob(EntityType.IRON_GOLEM, EntitySpawnReason.MOB_SUMMONED, world, this.blockPosition(), 10, 8, 6, SpawnUtil.Strategy.LEGACY_IRON_GOLEM, false).isEmpty()) {
+ if (SpawnUtil.trySpawnMob(EntityType.IRON_GOLEM, EntitySpawnReason.MOB_SUMMONED, world, this.blockPosition(), 10, 8, 6, SpawnUtil.Strategy.LEGACY_IRON_GOLEM, false, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.VILLAGE_DEFENSE, () -> {GolemSensor.golemDetected(this);}).isPresent()) { // CraftBukkit // Paper - Set Golem Last Seen to stop it from spawning another one
list.forEach(GolemSensor::golemDetected);
}
}
@@ -963,7 +1011,7 @@
@Override
public void startSleeping(BlockPos pos) {
super.startSleeping(pos);
- this.brain.setMemory(MemoryModuleType.LAST_SLEPT, (Object) this.level().getGameTime());
+ this.brain.setMemory(MemoryModuleType.LAST_SLEPT, this.level().getGameTime()); // CraftBukkit - decompile error
this.brain.eraseMemory(MemoryModuleType.WALK_TARGET);
this.brain.eraseMemory(MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE);
}
@@ -971,7 +1019,7 @@
@Override
public void stopSleeping() {
super.stopSleeping();
- this.brain.setMemory(MemoryModuleType.LAST_WOKEN, (Object) this.level().getGameTime());
+ this.brain.setMemory(MemoryModuleType.LAST_WOKEN, this.level().getGameTime()); // CraftBukkit - decompile error
}
private boolean golemSpawnConditionsMet(long worldTime) {

View File

@ -1,100 +0,0 @@
--- a/net/minecraft/world/entity/npc/WanderingTraderSpawner.java
+++ b/net/minecraft/world/entity/npc/WanderingTraderSpawner.java
@@ -40,43 +40,53 @@
public WanderingTraderSpawner(ServerLevelData properties) {
this.serverLevelData = properties;
- this.tickDelay = 1200;
- this.spawnDelay = properties.getWanderingTraderSpawnDelay();
- this.spawnChance = properties.getWanderingTraderSpawnChance();
- if (this.spawnDelay == 0 && this.spawnChance == 0) {
- this.spawnDelay = 24000;
- properties.setWanderingTraderSpawnDelay(this.spawnDelay);
- this.spawnChance = 25;
- properties.setWanderingTraderSpawnChance(this.spawnChance);
- }
+ // Paper start - Add Wandering Trader spawn rate config options
+ this.tickDelay = Integer.MIN_VALUE;
+ //this.spawnDelay = properties.getWanderingTraderSpawnDelay(); // Paper - This value is read from the world file only for the first spawn, after which vanilla uses a hardcoded value
+ //this.spawnChance = properties.getWanderingTraderSpawnChance(); // Paper - This value is read from the world file only for the first spawn, after which vanilla uses a hardcoded value
+ //if (this.spawnDelay == 0 && this.spawnChance == 0) {
+ // this.spawnDelay = 24000;
+ // properties.setWanderingTraderSpawnDelay(this.spawnDelay);
+ // this.spawnChance = 25;
+ // properties.setWanderingTraderSpawnChance(this.spawnChance);
+ //}
+ // Paper end - Add Wandering Trader spawn rate config options
}
@Override
public int tick(ServerLevel world, boolean spawnMonsters, boolean spawnAnimals) {
+ // Paper start - Add Wandering Trader spawn rate config options
+ if (this.tickDelay == Integer.MIN_VALUE) {
+ this.tickDelay = world.paperConfig().entities.spawning.wanderingTrader.spawnMinuteLength;
+ this.spawnDelay = world.paperConfig().entities.spawning.wanderingTrader.spawnDayLength;
+ this.spawnChance = world.paperConfig().entities.spawning.wanderingTrader.spawnChanceMin;
+ }
if (!world.getGameRules().getBoolean(GameRules.RULE_DO_TRADER_SPAWNING)) {
return 0;
- } else if (--this.tickDelay > 0) {
+ } else if (this.tickDelay - 1 > 0) {
+ this.tickDelay = this.tickDelay - 1;
return 0;
} else {
- this.tickDelay = 1200;
- this.spawnDelay -= 1200;
- this.serverLevelData.setWanderingTraderSpawnDelay(this.spawnDelay);
+ this.tickDelay = world.paperConfig().entities.spawning.wanderingTrader.spawnMinuteLength;
+ this.spawnDelay = this.spawnDelay - world.paperConfig().entities.spawning.wanderingTrader.spawnMinuteLength;
+ //this.serverLevelData.setWanderingTraderSpawnDelay(this.spawnDelay); // Paper - We don't need to save this value to disk if it gets set back to a hardcoded value anyways
if (this.spawnDelay > 0) {
return 0;
} else {
- this.spawnDelay = 24000;
+ this.spawnDelay = world.paperConfig().entities.spawning.wanderingTrader.spawnDayLength;
if (!world.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING)) {
return 0;
} else {
int i = this.spawnChance;
- this.spawnChance = Mth.clamp(this.spawnChance + 25, 25, 75);
- this.serverLevelData.setWanderingTraderSpawnChance(this.spawnChance);
+ // this.serverLevelData.setWanderingTraderSpawnChance(this.spawnChance); // Paper - We don't need to save this value to disk if it gets set back to a hardcoded value anyways
+ this.spawnChance = Mth.clamp(i + world.paperConfig().entities.spawning.wanderingTrader.spawnChanceFailureIncrement, world.paperConfig().entities.spawning.wanderingTrader.spawnChanceMin, world.paperConfig().entities.spawning.wanderingTrader.spawnChanceMax);
if (this.random.nextInt(100) > i) {
return 0;
} else if (this.spawn(world)) {
- this.spawnChance = 25;
+ this.spawnChance = world.paperConfig().entities.spawning.wanderingTrader.spawnChanceMin;
+ // Paper end - Add Wandering Trader spawn rate config options
return 1;
} else {
return 0;
@@ -110,7 +120,7 @@
return false;
}
- WanderingTrader entityvillagertrader = (WanderingTrader) EntityType.WANDERING_TRADER.spawn(world, blockposition2, EntitySpawnReason.EVENT);
+ WanderingTrader entityvillagertrader = (WanderingTrader) EntityType.WANDERING_TRADER.spawn(world, trader -> trader.setDespawnDelay(48000), blockposition2, EntitySpawnReason.EVENT, false, false, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.NATURAL); // CraftBukkit // Paper - set despawnTimer before spawn events called
if (entityvillagertrader != null) {
for (int i = 0; i < 2; ++i) {
@@ -118,7 +128,7 @@
}
this.serverLevelData.setWanderingTraderId(entityvillagertrader.getUUID());
- entityvillagertrader.setDespawnDelay(48000);
+ // entityvillagertrader.setDespawnDelay(48000); // CraftBukkit - moved to EntityVillagerTrader constructor. This lets the value be modified by plugins on CreatureSpawnEvent
entityvillagertrader.setWanderTarget(blockposition1);
entityvillagertrader.restrictTo(blockposition1, 16);
return true;
@@ -133,7 +143,7 @@
BlockPos blockposition = this.findSpawnPositionNear(world, wanderingTrader.blockPosition(), range);
if (blockposition != null) {
- TraderLlama entityllamatrader = (TraderLlama) EntityType.TRADER_LLAMA.spawn(world, blockposition, EntitySpawnReason.EVENT);
+ TraderLlama entityllamatrader = (TraderLlama) EntityType.TRADER_LLAMA.spawn(world, blockposition, EntitySpawnReason.EVENT, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.NATURAL); // CraftBukkit
if (entityllamatrader != null) {
entityllamatrader.setLeashedTo(wanderingTrader, true);