From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Martijn Muijsers Date: Sun, 25 Dec 2022 11:20:37 +0100 Subject: [PATCH] Optimize villager data storage License: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) Gale - https://galemc.org diff --git a/src/main/java/net/minecraft/world/Container.java b/src/main/java/net/minecraft/world/Container.java index 5db5ba026462ca642dcee718af732f80fadabef5..cd380ceb40d38acc7eef289ded11259388becabd 100644 --- a/src/main/java/net/minecraft/world/Container.java +++ b/src/main/java/net/minecraft/world/Container.java @@ -71,6 +71,20 @@ public interface Container extends Clearable { }); } + // Gale start - optimize villager data storage + default boolean hasAnyOf(Item[] items) { + for (int i = 0; i < this.getContainerSize(); ++i) { + ItemStack itemstack = this.getItem(i); + for (Item item : items) { + if (itemstack.is(item)) { + return true; + } + } + } + return false; + } + // Gale end - optimize villager data storage + default boolean hasAnyMatching(Predicate predicate) { for (int i = 0; i < this.getContainerSize(); ++i) { ItemStack itemstack = this.getItem(i); diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/TradeWithVillager.java b/src/main/java/net/minecraft/world/entity/ai/behavior/TradeWithVillager.java index 8508ac7de8cda3127b73e11ff4aee62502e65ead..3232f40ef11f59091cec469f0dd40c60ee2a16e9 100644 --- a/src/main/java/net/minecraft/world/entity/ai/behavior/TradeWithVillager.java +++ b/src/main/java/net/minecraft/world/entity/ai/behavior/TradeWithVillager.java @@ -1,9 +1,8 @@ package net.minecraft.world.entity.ai.behavior; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import java.util.Set; -import java.util.stream.Collectors; + +import java.util.Arrays; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.SimpleContainer; import net.minecraft.world.entity.EntityType; @@ -15,9 +14,14 @@ import net.minecraft.world.entity.npc.VillagerProfession; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public class TradeWithVillager extends Behavior { - private Set trades = ImmutableSet.of(); + // Gale start - optimize villager data storage + private static final Item[] WHEAT_SINGLETON_ARRAY = {Items.WHEAT}; + private @NotNull Item @Nullable [] trades = null; + // Gale end - optimize villager data storage public TradeWithVillager() { super( @@ -51,16 +55,18 @@ public class TradeWithVillager extends Behavior { BehaviorUtils.lockGazeAndWalkToEachOther(entity, villager, 0.5F, 2); entity.gossip(world, villager, time); if (entity.hasExcessFood() && (entity.getVillagerData().getProfession() == VillagerProfession.FARMER || villager.wantsMoreFood())) { - throwHalfStack(entity, Villager.FOOD_POINTS.keySet(), villager); + throwHalfStack(entity, Villager.FOOD_POINTS_KEY_ARRAY, villager); // Gale - optimize villager data storage } if (villager.getVillagerData().getProfession() == VillagerProfession.FARMER && entity.getInventory().countItem(Items.WHEAT) > Items.WHEAT.getDefaultMaxStackSize() / 2) { - throwHalfStack(entity, ImmutableSet.of(Items.WHEAT), villager); + throwHalfStack(entity, Villager.FOOD_POINTS_KEY_ARRAY, villager); // Gale - optimize villager data storage } - if (!this.trades.isEmpty() && entity.getInventory().hasAnyOf(this.trades)) { + // Gale start - optimize villager data storage + if (this.trades != null && entity.getInventory().hasAnyOf(this.trades)) { throwHalfStack(entity, this.trades, villager); + // Gale end - optimize villager data storage } } } @@ -70,13 +76,36 @@ public class TradeWithVillager extends Behavior { villager.getBrain().eraseMemory(MemoryModuleType.INTERACTION_TARGET); } - private static Set figureOutWhatIAmWillingToTrade(Villager entity, Villager target) { - ImmutableSet immutableSet = target.getVillagerData().getProfession().requestedItems(); - ImmutableSet immutableSet2 = entity.getVillagerData().getProfession().requestedItems(); - return immutableSet.stream().filter(item -> !immutableSet2.contains(item)).collect(Collectors.toSet()); + // Gale start - optimize villager data storage + private static @NotNull Item @Nullable [] figureOutWhatIAmWillingToTrade(Villager entity, Villager target) { + @NotNull Item @Nullable [] immutableSet = target.getVillagerData().getProfession().requestedItems(); + if (immutableSet == null) { + return null; + } + @NotNull Item @Nullable [] immutableSet2 = entity.getVillagerData().getProfession().requestedItems(); + if (immutableSet2 == null) { + return immutableSet; + } + if (immutableSet == immutableSet2) { + return null; + } + Item[] willingToTrade = new Item[immutableSet.length]; + int willingToTradeSize = 0; + forImmutableSet: + for (Item item : immutableSet) { + for (Item item2 : immutableSet2) { + if (item == item2) { + continue forImmutableSet; + } + } + willingToTrade[willingToTradeSize] = item; + willingToTradeSize++; + } + return Arrays.copyOf(willingToTrade, willingToTradeSize); + // Gale end - optimize villager data storage } - private static void throwHalfStack(Villager villager, Set validItems, LivingEntity target) { + private static void throwHalfStack(Villager villager, @NotNull Item @NotNull [] validItems, LivingEntity target) { // Gale - optimize villager data storage SimpleContainer simpleContainer = villager.getInventory(); ItemStack itemStack = ItemStack.EMPTY; int i = 0; @@ -89,7 +118,16 @@ public class TradeWithVillager extends Behavior { itemStack2 = simpleContainer.getItem(i); if (!itemStack2.isEmpty()) { item = itemStack2.getItem(); - if (validItems.contains(item)) { + // Gale start - optimize villager data storage + boolean inValidItems = false; + for (Item validItem : validItems) { + if (validItem == item) { + inValidItems = true; + break; + } + } + if (inValidItems) { + // Gale end - optimize villager data storage if (itemStack2.getCount() > itemStack2.getMaxStackSize() / 2) { j = itemStack2.getCount() / 2; break label28; diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/SecondaryPoiSensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/SecondaryPoiSensor.java index 1595568f3140a62b0f2236644ac2da11db12af05..ce8851c2cacfd3145b1e2c11443140a0759a1b07 100644 --- a/src/main/java/net/minecraft/world/entity/ai/sensing/SecondaryPoiSensor.java +++ b/src/main/java/net/minecraft/world/entity/ai/sensing/SecondaryPoiSensor.java @@ -2,7 +2,8 @@ package net.minecraft.world.entity.ai.sensing; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; -import java.util.List; + +import java.util.ArrayList; import java.util.Set; import net.minecraft.core.BlockPos; import net.minecraft.core.GlobalPos; @@ -12,6 +13,7 @@ import net.minecraft.world.entity.ai.Brain; import net.minecraft.world.entity.ai.memory.MemoryModuleType; import net.minecraft.world.entity.npc.Villager; import net.minecraft.world.level.Level; +import org.jetbrains.annotations.Nullable; public class SecondaryPoiSensor extends Sensor { private static final int SCAN_RATE = 40; @@ -24,21 +26,26 @@ public class SecondaryPoiSensor extends Sensor { protected void doTick(ServerLevel world, Villager entity) { // Gale start - Lithium - skip secondary POI sensor if absent var secondaryPoi = entity.getVillagerData().getProfession().secondaryPoi(); - if (secondaryPoi.isEmpty()) { + if (secondaryPoi == null) { // Gale - optimize villager data storage entity.getBrain().eraseMemory(MemoryModuleType.SECONDARY_JOB_SITE); return; } // Gale end - Lithium - skip secondary POI sensor if absent ResourceKey resourceKey = world.dimension(); BlockPos blockPos = entity.blockPosition(); - List list = Lists.newArrayList(); + @Nullable ArrayList list = null; // Gale - optimize villager data storage int i = 4; for (int j = -4; j <= 4; j++) { for (int k = -2; k <= 2; k++) { for (int l = -4; l <= 4; l++) { BlockPos blockPos2 = blockPos.offset(j, k, l); - if (entity.getVillagerData().getProfession().secondaryPoi().contains(world.getBlockState(blockPos2).getBlock())) { + // Gale start - optimize villager data storage + if (secondaryPoi == world.getBlockState(blockPos2).getBlock()) { + if (list == null) { + list = Lists.newArrayList(); + } + // Gale end - optimize villager data storage list.add(GlobalPos.of(resourceKey, blockPos2)); } } @@ -46,7 +53,10 @@ public class SecondaryPoiSensor extends Sensor { } Brain brain = entity.getBrain(); - if (!list.isEmpty()) { + // Gale start - optimize villager data storage + if (list != null) { + list.trimToSize(); + // Gale end - optimize villager data storage brain.setMemory(MemoryModuleType.SECONDARY_JOB_SITE, list); } else { brain.eraseMemory(MemoryModuleType.SECONDARY_JOB_SITE); 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 ba49442ed0b7c05c50dbc2a640f5759e391902f2..71a3eadd2f1e00fa066dbfe9918d749d43435a18 100644 --- a/src/main/java/net/minecraft/world/entity/npc/Villager.java +++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java @@ -90,6 +90,7 @@ import net.minecraft.world.item.trading.MerchantOffers; import net.minecraft.world.level.Level; import net.minecraft.world.level.ServerLevelAccessor; import net.minecraft.world.phys.AABB; +import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; // CraftBukkit start @@ -106,8 +107,9 @@ public class Villager extends AbstractVillager implements ReputationEventHandler private static final EntityDataAccessor DATA_VILLAGER_DATA = SynchedEntityData.defineId(Villager.class, EntityDataSerializers.VILLAGER_DATA); public static final int BREEDING_FOOD_THRESHOLD = 12; public static final Map FOOD_POINTS = ImmutableMap.of(Items.BREAD, 4, Items.POTATO, 1, Items.CARROT, 1, Items.BEETROOT, 1); + public static final Item[] FOOD_POINTS_KEY_ARRAY = FOOD_POINTS.keySet().toArray(Item[]::new); // Gale - optimize villager data storage private static final int TRADES_PER_LEVEL = 2; - private static final Set WANTED_ITEMS = ImmutableSet.of(Items.BREAD, Items.POTATO, Items.CARROT, Items.WHEAT, Items.WHEAT_SEEDS, Items.BEETROOT, new Item[]{Items.BEETROOT_SEEDS, Items.TORCHFLOWER_SEEDS, Items.PITCHER_POD}); + private static final Item[] WANTED_ITEMS = {Items.BREAD, Items.POTATO, Items.CARROT, Items.WHEAT, Items.WHEAT_SEEDS, Items.BEETROOT, Items.BEETROOT_SEEDS, Items.TORCHFLOWER_SEEDS, Items.PITCHER_POD}; // Gale - optimize villager data storage private static final int MAX_GOSSIP_TOPICS = 10; private static final int GOSSIP_COOLDOWN = 1200; private static final int GOSSIP_DECAY_INTERVAL = 24000; @@ -910,7 +912,28 @@ public class Villager extends AbstractVillager implements ReputationEventHandler public boolean wantsToPickUp(ItemStack stack) { Item item = stack.getItem(); - return (Villager.WANTED_ITEMS.contains(item) || this.getVillagerData().getProfession().requestedItems().contains(item)) && this.getInventory().canAddItem(stack); + // Gale start - optimize villager data storage + boolean isDesired = false; + for (Item wantedItem : WANTED_ITEMS) { + if (wantedItem == item) { + isDesired = true; + break; + } + } + if (!isDesired) { + var requestedItems = this.getVillagerData().getProfession().requestedItems(); + if (requestedItems == null) { + return false; + } + for (Item requestedItem : requestedItems) { + if (requestedItem == item) { + isDesired = true; + break; + } + } + } + return isDesired && this.getInventory().canAddItem(stack); + // Gale end - optimize villager data storage } public boolean hasExcessFood() { diff --git a/src/main/java/net/minecraft/world/entity/npc/VillagerProfession.java b/src/main/java/net/minecraft/world/entity/npc/VillagerProfession.java index 8734ab1bd8299bbf43906d81a349c2a13e0981a7..018b577aecf612e4a4e98cbd7d9292955d04693c 100644 --- a/src/main/java/net/minecraft/world/entity/npc/VillagerProfession.java +++ b/src/main/java/net/minecraft/world/entity/npc/VillagerProfession.java @@ -1,8 +1,6 @@ package net.minecraft.world.entity.npc; -import com.google.common.collect.ImmutableSet; import java.util.function.Predicate; -import javax.annotation.Nullable; import net.minecraft.core.Holder; import net.minecraft.core.Registry; import net.minecraft.core.registries.BuiltInRegistries; @@ -17,13 +15,15 @@ import net.minecraft.world.item.Item; import net.minecraft.world.item.Items; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public record VillagerProfession( String name, Predicate> heldJobSite, Predicate> acquirableJobSite, - ImmutableSet requestedItems, - ImmutableSet secondaryPoi, + @NotNull Item @Nullable [] requestedItems, // Gale - optimize villager data storage + @Nullable Block secondaryPoi, // Gale - optimize villager data storage @Nullable SoundEvent workSound ) { public static final Predicate> ALL_ACQUIRABLE_JOBS = poiType -> poiType.is(PoiTypeTags.ACQUIRABLE_JOB_SITE); @@ -35,8 +35,8 @@ public record VillagerProfession( public static final VillagerProfession FARMER = register( "farmer", PoiTypes.FARMER, - ImmutableSet.of(Items.WHEAT, Items.WHEAT_SEEDS, Items.BEETROOT_SEEDS, Items.BONE_MEAL), - ImmutableSet.of(Blocks.FARMLAND), + new Item[] {Items.WHEAT, Items.WHEAT_SEEDS, Items.BEETROOT_SEEDS, Items.BONE_MEAL}, // Gale - optimize villager data storage + Blocks.FARMLAND, // Gale - optimize villager data storage SoundEvents.VILLAGER_WORK_FARMER ); public static final VillagerProfession FISHERMAN = register("fisherman", PoiTypes.FISHERMAN, SoundEvents.VILLAGER_WORK_FISHERMAN); @@ -58,34 +58,36 @@ public record VillagerProfession( return register(id, entry -> entry.is(heldWorkstation), entry -> entry.is(heldWorkstation), workSound); } + // Gale start - optimize villager data storage private static VillagerProfession register( String id, Predicate> heldWorkstation, Predicate> acquirableWorkstation, @Nullable SoundEvent workSound ) { - return register(id, heldWorkstation, acquirableWorkstation, ImmutableSet.of(), ImmutableSet.of(), workSound); + return register(id, heldWorkstation, acquirableWorkstation, null, null, workSound); } private static VillagerProfession register( String id, ResourceKey heldWorkstation, - ImmutableSet gatherableItems, - ImmutableSet secondaryJobSites, + @NotNull Item @Nullable [] gatherableItems, + @Nullable Block secondaryJobSite, @Nullable SoundEvent workSound ) { - return register(id, entry -> entry.is(heldWorkstation), entry -> entry.is(heldWorkstation), gatherableItems, secondaryJobSites, workSound); + return register(id, entry -> entry.is(heldWorkstation), entry -> entry.is(heldWorkstation), gatherableItems, secondaryJobSite, workSound); } private static VillagerProfession register( String id, Predicate> heldWorkstation, Predicate> acquirableWorkstation, - ImmutableSet gatherableItems, - ImmutableSet secondaryJobSites, + @NotNull Item @Nullable [] gatherableItems, + @Nullable Block secondaryJobSite, @Nullable SoundEvent workSound ) { return Registry.register( BuiltInRegistries.VILLAGER_PROFESSION, ResourceLocation.withDefaultNamespace(id), - new VillagerProfession(id, heldWorkstation, acquirableWorkstation, gatherableItems, secondaryJobSites, workSound) + new VillagerProfession(id, heldWorkstation, acquirableWorkstation, gatherableItems != null && gatherableItems.length == 0 ? null : gatherableItems, secondaryJobSite, workSound) ); + // Gale end - optimize villager data storage } }