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 0d22bce549c46d686ab3402a1cad87307d0e1c00..35d7737b195b3207e45a670636aed977d5d6c4a6 100644 --- a/src/main/java/net/minecraft/world/Container.java +++ b/src/main/java/net/minecraft/world/Container.java @@ -126,6 +126,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 3af715e2f3f3949af614a8fcebbc4a835d48ca49..94fb9bcf601832ee934331c0376de8707b5043c5 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,11 +14,16 @@ 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 static final int INTERACT_DIST_SQR = 5; private static final float SPEED_MODIFIER = 0.5F; - 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(ImmutableMap.of(MemoryModuleType.INTERACTION_TARGET, MemoryStatus.VALUE_PRESENT, MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES, MemoryStatus.VALUE_PRESENT)); @@ -49,15 +53,17 @@ public class TradeWithVillager extends Behavior { BehaviorUtils.lockGazeAndWalkToEachOther(entity, villager, 0.5F); 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.getMaxStackSize() / 2) { - throwHalfStack(entity, ImmutableSet.of(Items.WHEAT), villager); + throwHalfStack(entity, WHEAT_SINGLETON_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 } } @@ -68,15 +74,35 @@ 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) -> { - return !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 +115,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 75dc06a3041bfdfb08c914eb50cfa282ae9eb2fe..53b0519bbc5d52490040eaf0fe449648f021d0c2 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 10749bebb1c94ec1a74bacdce3b9b7903d8c4322..ae3628efe7628427c53bb7d0f7fc6e457a511b94 100644 --- a/src/main/java/net/minecraft/world/entity/npc/Villager.java +++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java @@ -89,6 +89,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 @@ -104,8 +105,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; @@ -914,7 +916,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 ac70c2c03241e73943bd517a8c69dd05e0873634..95197b601d93c30a7645d67c89c7608fc00a8de6 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,8 +15,10 @@ 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, @Nullable SoundEvent workSound) { +public record VillagerProfession(String name, Predicate> heldJobSite, Predicate> acquirableJobSite, @NotNull Item @Nullable [] requestedItems, @Nullable Block secondaryPoi, @Nullable SoundEvent workSound) { // Gale - optimize villager data storage public static final Predicate> ALL_ACQUIRABLE_JOBS = (poiType) -> { return poiType.is(PoiTypeTags.ACQUIRABLE_JOB_SITE); }; @@ -27,7 +27,7 @@ public record VillagerProfession(String name, Predicate> heldJob public static final VillagerProfession BUTCHER = register("butcher", PoiTypes.BUTCHER, SoundEvents.VILLAGER_WORK_BUTCHER); public static final VillagerProfession CARTOGRAPHER = register("cartographer", PoiTypes.CARTOGRAPHER, SoundEvents.VILLAGER_WORK_CARTOGRAPHER); public static final VillagerProfession CLERIC = register("cleric", PoiTypes.CLERIC, SoundEvents.VILLAGER_WORK_CLERIC); - 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), SoundEvents.VILLAGER_WORK_FARMER); + public static final VillagerProfession FARMER = register("farmer", PoiTypes.FARMER, new Item[] {Items.WHEAT, Items.WHEAT_SEEDS, Items.BEETROOT_SEEDS, Items.BONE_MEAL}, Blocks.FARMLAND, SoundEvents.VILLAGER_WORK_FARMER); // Gale - optimize villager data storage public static final VillagerProfession FISHERMAN = register("fisherman", PoiTypes.FISHERMAN, SoundEvents.VILLAGER_WORK_FISHERMAN); public static final VillagerProfession FLETCHER = register("fletcher", PoiTypes.FLETCHER, SoundEvents.VILLAGER_WORK_FLETCHER); public static final VillagerProfession LEATHERWORKER = register("leatherworker", PoiTypes.LEATHERWORKER, SoundEvents.VILLAGER_WORK_LEATHERWORKER); @@ -52,18 +52,20 @@ public record VillagerProfession(String name, Predicate> heldJob } 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); // Gale - optimize villager data storage } - private static VillagerProfession register(String id, ResourceKey heldWorkstation, ImmutableSet gatherableItems, ImmutableSet secondaryJobSites, @Nullable SoundEvent workSound) { + private static VillagerProfession register(String id, ResourceKey heldWorkstation, @NotNull Item @Nullable [] gatherableItems, @Nullable Block secondaryJobSite, @Nullable SoundEvent workSound) { // Gale - optimize villager data storage return register(id, (entry) -> { return entry.is(heldWorkstation); }, (entry) -> { return entry.is(heldWorkstation); - }, gatherableItems, secondaryJobSites, workSound); + }, gatherableItems, secondaryJobSite, workSound); // Gale - optimize villager data storage } - private static VillagerProfession register(String id, Predicate> heldWorkstation, Predicate> acquirableWorkstation, ImmutableSet gatherableItems, ImmutableSet secondaryJobSites, @Nullable SoundEvent workSound) { - return Registry.register(BuiltInRegistries.VILLAGER_PROFESSION, new ResourceLocation(id), new VillagerProfession(id, heldWorkstation, acquirableWorkstation, gatherableItems, secondaryJobSites, workSound)); + // Gale start - optimize villager data storage + private static VillagerProfession register(String id, Predicate> heldWorkstation, Predicate> acquirableWorkstation, @NotNull Item @Nullable [] gatherableItems, @Nullable Block secondaryJobSite, @Nullable SoundEvent workSound) { + return Registry.register(BuiltInRegistries.VILLAGER_PROFESSION, new ResourceLocation(id), new VillagerProfession(id, heldWorkstation, acquirableWorkstation, gatherableItems != null && gatherableItems.length == 0 ? null : gatherableItems, secondaryJobSite, workSound)); + // Gale end - optimize villager data storage } }