Skip to content

Commit

Permalink
Bunch of fixes (#10445)
Browse files Browse the repository at this point in the history
All food checks are now in Food utils
Fix citizens getting stuck in eating state with invalid foods
Fix citizens without homebuilding, or level 1 trying to take zero stacks of food
CAN_EAT now is called EDIBLE as can_eat implies citizen can eat this item, which is not necessarily the case. Use FoodUtils.canEat() to check that
RS serilization errors no longer prevent a player from joining the world, instead it logs & resets
Fix fisher pathfinding sometimes getting caught in an infinite loop
Fix navigator NPE when no path was found
  • Loading branch information
someaddons authored Nov 14, 2024
1 parent 87ec949 commit a0aff36
Show file tree
Hide file tree
Showing 16 changed files with 146 additions and 79 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@
import java.util.*;
import java.util.function.Predicate;

import static com.minecolonies.api.util.constant.Suppression.GENERIC_WILDCARD;
import static com.minecolonies.api.util.constant.EquipmentLevelConstants.BASIC_TOOL_LEVEL;
import static com.minecolonies.api.util.constant.EquipmentLevelConstants.TOOL_LEVEL_MAXIMUM;
import static com.minecolonies.api.util.constant.Suppression.GENERIC_WILDCARD;

public interface IBuilding extends IBuildingContainer, IModuleContainer<IBuildingModule>, IRequestResolverProvider, IRequester, ISchematicProvider
{
Expand Down Expand Up @@ -502,7 +502,10 @@ default boolean canAssignCitizens()
* @param stack the stack to test.
* @return true if so.
*/
boolean canEat(final ItemStack stack);
default boolean canEat(final ItemStack stack)
{
return true;
}

/**
* Get the standing position for a building.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -785,7 +785,7 @@ private void discoverFood(final ItemStack stack)
if (ISFOOD.test(stack) || ISCOOKABLE.test(stack))
{
food.add(new ItemStorage(stack));
if (CAN_EAT.test(stack))
if (FoodUtils.EDIBLE.test(stack))
{
edibles.add(new ItemStorage(stack));
}
Expand Down
29 changes: 25 additions & 4 deletions src/main/java/com/minecolonies/api/util/FoodUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
import net.minecraft.world.level.block.entity.BlockEntity;

import javax.annotation.Nullable;

import java.util.Set;
import java.util.function.Predicate;

import static com.minecolonies.api.research.util.ResearchConstants.SATURATION;
import static com.minecolonies.api.util.constant.Constants.MAX_BUILDING_LEVEL;
Expand All @@ -27,13 +27,34 @@
*/
public class FoodUtils
{
/**
* Predicate describing food which can be eaten (is not raw).
*/
public static Predicate<ItemStack> EDIBLE;

/**
* @param stack
* @param workBuilding
* @return
*/
public static boolean canEat(final ItemStack stack, final IBuilding homeBuilding, final IBuilding workBuilding)
{
if (!EDIBLE.test(stack))
{
return false;
}

final int homeBuildingLevel = homeBuilding == null ? 0 : homeBuilding.getBuildingLevel();
return canEatLevel(stack, homeBuildingLevel) && (workBuilding == null || workBuilding.canEat(stack));
}

/**
* Check if that food can be eaten at a given building level.
* @param stack the stack to check.
* @param buildingLevel the respective building level.
* @return true if so.
*/
public static boolean canEat(final ItemStack stack, final int buildingLevel)
public static boolean canEatLevel(final ItemStack stack, final int buildingLevel)
{
if (buildingLevel < 3)
{
Expand Down Expand Up @@ -107,7 +128,7 @@ public static int getBestFoodForCitizen(final InventoryCitizen inventoryCitizen,
for (int i = 0; i < inventoryCitizen.getSlots(); i++)
{
final ItemStorage invStack = new ItemStorage(inventoryCitizen.getStackInSlot(i));
if ((menu == null || menu.contains(invStack)) && (citizenData.getHomeBuilding() == null || FoodUtils.canEat(invStack.getItemStack(), citizenData.getHomeBuilding().getBuildingLevel())))
if ((menu == null || menu.contains(invStack)) && FoodUtils.canEat(invStack.getItemStack(), citizenData.getHomeBuilding(), citizenData.getWorkBuilding()))
{
final boolean isMinecolfood = invStack.getItem() instanceof IMinecoloniesFoodItem;
final int localScore = foodHandler.checkLastEaten(invStack.getItem()) * (isMinecolfood ? 2 : 1);
Expand Down Expand Up @@ -177,7 +198,7 @@ public static ItemStorage checkForFoodInBuilding(final ICitizenData citizenData,
{
for (final ItemStorage storage : rackEntity.getAllContent().keySet())
{
if ((menu == null || menu.contains(storage)) && FoodUtils.canEat(storage.getItemStack(), homeBuildingLevel))
if ((menu == null || menu.contains(storage)) && FoodUtils.canEat(storage.getItemStack(), citizenData.getHomeBuilding(), citizenData.getWorkBuilding()))
{
final boolean isMinecolfood = storage.getItem() instanceof IMinecoloniesFoodItem;
final int localScore = foodHandler.checkLastEaten(storage.getItem());
Expand Down
7 changes: 1 addition & 6 deletions src/main/java/com/minecolonies/api/util/ItemStackUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@

import static com.minecolonies.api.items.ModTags.fungi;
import static com.minecolonies.api.util.constant.Constants.*;
import static com.minecolonies.api.util.constant.HappinessConstants.*;
import static com.minecolonies.api.util.constant.HappinessConstants.HADGREATFOOD;

/**
* Utility methods for the inventories.
Expand Down Expand Up @@ -120,11 +120,6 @@ public final class ItemStackUtils
*/
public static Predicate<ItemStack> IS_SMELTABLE;

/**
* Predicate describing food which can be eaten (is not raw).
*/
public static Predicate<ItemStack> CAN_EAT;

/**
* Predicate describing cookables.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.minecolonies.api.entity.citizen.citizenhandlers.ICitizenFoodHandler;
import com.minecolonies.api.entity.citizen.happiness.ITimeBasedHappinessModifier;
import com.minecolonies.api.items.ModTags;
import com.minecolonies.api.util.FoodUtils;
import com.minecolonies.api.util.InventoryUtils;
import com.minecolonies.api.util.ItemStackUtils;
import com.minecolonies.core.colony.buildings.AbstractBuilding;
Expand Down Expand Up @@ -55,7 +56,9 @@ public static void init()
citizen -> citizen.getWorkBuilding() != null && citizen.getWorkBuilding().hasModule(BuildingModules.FURNACE) && citizen.getWorkBuilding().getModule(BuildingModules.FURNACE).getFurnaces().isEmpty());
InteractionValidatorRegistry.registerStandardPredicate(Component.translatable(RAW_FOOD),
citizen -> InventoryUtils.findFirstSlotInItemHandlerNotEmptyWith(citizen.getInventory(), ISCOOKABLE) != -1
&& InventoryUtils.findFirstSlotInItemHandlerNotEmptyWith(citizen.getInventory(), stack -> CAN_EAT.test(stack) && (citizen.getWorkBuilding() == null || citizen.getWorkBuilding().canEat(stack))) == -1);
&&
InventoryUtils.findFirstSlotInItemHandlerNotEmptyWith(citizen.getInventory(), stack -> FoodUtils.canEat(stack, citizen.getHomeBuilding(), citizen.getWorkBuilding()))
== -1);
InteractionValidatorRegistry.registerStandardPredicate(Component.translatable(BETTER_FOOD),
citizen -> citizen.getSaturation() == 0 && !citizen.isChild() && citizen.needsBetterFood());
InteractionValidatorRegistry.registerStandardPredicate(Component.translatable(BETTER_FOOD_CHILDREN),
Expand Down
5 changes: 2 additions & 3 deletions src/main/java/com/minecolonies/core/colony/CitizenData.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@

import static com.minecolonies.api.entity.citizen.AbstractEntityCitizen.*;
import static com.minecolonies.api.research.util.ResearchConstants.*;
import static com.minecolonies.api.util.ItemStackUtils.CAN_EAT;
import static com.minecolonies.api.util.constant.BuildingConstants.TAG_ACTIVE;
import static com.minecolonies.api.util.constant.CitizenConstants.*;
import static com.minecolonies.api.util.constant.ColonyConstants.UPDATE_SUBSCRIBERS_INTERVAL;
Expand Down Expand Up @@ -1794,9 +1793,9 @@ public boolean needsBetterFood()
else
{
int slotBadFood = InventoryUtils.findFirstSlotInItemHandlerNotEmptyWith(inventory,
stack -> CAN_EAT.test(stack) && !this.getHomeBuilding().canEat(stack));
stack -> FoodUtils.canEat(stack, getHomeBuilding(), getWorkBuilding()));
int slotGoodFood = InventoryUtils.findFirstSlotInItemHandlerNotEmptyWith(inventory,
stack -> CAN_EAT.test(stack) && this.getHomeBuilding().canEat(stack));
stack -> FoodUtils.canEat(stack, getHomeBuilding(), getWorkBuilding()));
return slotBadFood != -1 && slotGoodFood == -1;
}
}
Expand Down
22 changes: 16 additions & 6 deletions src/main/java/com/minecolonies/core/colony/ColonyView.java
Original file line number Diff line number Diff line change
Expand Up @@ -324,13 +324,23 @@ public static void serializeNetworkData(@NotNull Colony colony, @NotNull Friendl

if (colony.getRequestManager() != null && (colony.getRequestManager().isDirty() || hasNewSubscribers))
{
final int preSize = buf.writerIndex();
buf.writeBoolean(true);
colony.getRequestManager().serialize(StandardFactoryController.getInstance(), buf);
final int postSize = buf.writerIndex();
if ((postSize - preSize) >= ColonyView.REQUEST_MANAGER_MAX_SIZE)
final int preIndex = buf.writerIndex();
try
{
Log.getLogger().warn("Colony " + colony.getID() + " has a very big memory imprint, this could be a memory leak, please contact the mod author!");
buf.writeBoolean(true);
colony.getRequestManager().serialize(StandardFactoryController.getInstance(), buf);
final int postSize = buf.writerIndex();
if ((postSize - preIndex) >= ColonyView.REQUEST_MANAGER_MAX_SIZE)
{
Log.getLogger().warn("Colony " + colony.getID() + " has a very big memory imprint, this could be a memory leak, please contact the mod author!");
}
}
catch (Exception e)
{
buf.writerIndex(preIndex);
Log.getLogger().warn("Error during request manager serialization for:" + colony.getID(), e);
colony.getRequestManager().reset();
buf.writeBoolean(false);
}
}
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1187,7 +1187,7 @@ public Map<Predicate<ItemStack>, Tuple<Integer, Boolean>> getRequiredItemsAndAmo

if (keepFood())
{
toKeep.put(stack -> ItemStackUtils.CAN_EAT.test(stack) && canEat(stack), new Tuple<>(getBuildingLevel() * 2, true));
toKeep.put(stack -> FoodUtils.canEat(stack, null, this), new Tuple<>(getBuildingLevel() * 2, true));
}
for (final IHasRequiredItemsModule module : getModulesByType(IHasRequiredItemsModule.class))
{
Expand All @@ -1198,12 +1198,6 @@ public Map<Predicate<ItemStack>, Tuple<Integer, Boolean>> getRequiredItemsAndAmo
return toKeep;
}

@Override
public boolean canEat(final ItemStack stack)
{
return FoodUtils.canEat(stack, this.getBuildingLevel());
}

@Override
public int getMaxBuildingLevel()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import com.minecolonies.api.colony.jobs.registry.JobEntry;
import com.minecolonies.api.crafting.IGenericRecipe;
import com.minecolonies.api.util.CraftingUtils;
import com.minecolonies.api.util.ItemStackUtils;
import com.minecolonies.api.util.FoodUtils;
import com.minecolonies.api.util.OptionalPredicate;
import com.minecolonies.core.colony.buildings.AbstractBuilding;
import com.minecolonies.core.colony.buildings.modules.AbstractCraftingBuildingModule;
Expand All @@ -13,7 +13,7 @@
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.NotNull;

import java.util.*;
import java.util.Optional;

import static com.minecolonies.api.util.constant.Suppression.OVERRIDE_EQUALS;
import static com.minecolonies.api.util.constant.TagConstants.CRAFTING_COOK;
Expand Down Expand Up @@ -88,8 +88,8 @@ public boolean isRecipeCompatible(@NotNull final IGenericRecipe recipe)
if (isRecipeAllowed.isPresent()) return isRecipeAllowed.get();

final ItemStack output = recipe.getPrimaryOutput();
return ItemStackUtils.CAN_EAT.test(output)
|| ItemStackUtils.CAN_EAT.test(FurnaceRecipes.getInstance()
return FoodUtils.EDIBLE.test(output)
|| FoodUtils.EDIBLE.test(FurnaceRecipes.getInstance()
.getSmeltingResult(output));
}
}
Expand Down Expand Up @@ -118,7 +118,7 @@ public OptionalPredicate<ItemStack> getIngredientValidator()
public boolean isRecipeCompatible(@NotNull final IGenericRecipe recipe)
{
if (!super.isRecipeCompatible(recipe)) return false;
return CraftingUtils.isRecipeCompatibleBasedOnTags(recipe, CRAFTING_COOK).orElse(ItemStackUtils.CAN_EAT.test(recipe.getPrimaryOutput()));
return CraftingUtils.isRecipeCompatibleBasedOnTags(recipe, CRAFTING_COOK).orElse(FoodUtils.EDIBLE.test(recipe.getPrimaryOutput()));
}
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
package com.minecolonies.core.colony.jobs;

import net.minecraft.resources.ResourceLocation;
import com.minecolonies.api.client.render.modeltype.ModModelTypes;
import com.minecolonies.api.colony.ICitizenData;
import com.minecolonies.api.entity.citizen.AbstractEntityCitizen;
import com.minecolonies.api.util.BlockPosUtil;
import com.minecolonies.api.util.Tuple;
import com.minecolonies.core.entity.ai.workers.production.agriculture.EntityAIWorkFisherman;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.core.BlockPos;

import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,19 @@
import com.minecolonies.core.colony.interactionhandling.StandardInteraction;
import com.minecolonies.core.colony.jobs.AbstractJobGuard;
import com.minecolonies.core.colony.jobs.JobCook;
import com.minecolonies.core.entity.other.SittingEntity;
import com.minecolonies.core.entity.citizen.EntityCitizen;
import com.minecolonies.core.entity.other.SittingEntity;
import com.minecolonies.core.network.messages.client.ItemParticleEffectMessage;
import net.minecraft.core.BlockPos;
import net.minecraft.network.chat.Component;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.item.*;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;

import java.util.LinkedHashSet;
import java.util.Set;

import static com.minecolonies.api.util.ItemStackUtils.CAN_EAT;
import static com.minecolonies.api.util.ItemStackUtils.ISCOOKABLE;
import static com.minecolonies.api.util.constant.CitizenConstants.FULL_SATURATION;
import static com.minecolonies.api.util.constant.Constants.SECONDS_A_MINUTE;
Expand Down Expand Up @@ -171,18 +171,6 @@ private EatingState getFood()
return GO_TO_HUT;
}

/**
* Check if a citizen can eat something.
*
* @param citizenData the citizen to check.
* @param stack the stack to check.
* @return true if so.
*/
private boolean canEat(final ICitizenData citizenData, final ItemStack stack)
{
return citizenData.getHomeBuilding() == null || citizenData.getHomeBuilding().canEat(stack);
}

/**
* Actual action of eating.
*
Expand All @@ -197,7 +185,7 @@ private IState eat()

final ICitizenData citizenData = citizen.getCitizenData();
final ItemStack foodStack = citizenData.getInventory().getStackInSlot(foodSlot);
if (!CAN_EAT.test(foodStack) || !canEat(citizenData, foodStack))
if (!FoodUtils.canEat(foodStack, citizenData.getHomeBuilding(), citizenData.getWorkBuilding()))
{
return CHECK_FOR_FOOD;
}
Expand Down Expand Up @@ -273,8 +261,9 @@ private EatingState getFoodYourself()
final ItemStorage storageToGet = FoodUtils.checkForFoodInBuilding(citizen.getCitizenData(), cookBuilding.getModule(RESTAURANT_MENU).getMenu(), cookBuilding);
if (storageToGet != null)
{
int homeBuildingLevel = citizen.getCitizenData().getHomeBuilding() == null ? 0 : citizen.getCitizenData().getHomeBuilding().getBuildingLevel();
int qty = (int) (Math.max(1.0, (FULL_SATURATION - citizen.getCitizenData().getSaturation()) / FoodUtils.getFoodValue(storageToGet.getItemStack(), citizen)) * homeBuildingLevel/2.0);
int homeBuildingLevel = citizen.getCitizenData().getHomeBuilding() == null ? 1 : citizen.getCitizenData().getHomeBuilding().getBuildingLevel();
int qty = (int) (Math.max(1.0,
(FULL_SATURATION - citizen.getCitizenData().getSaturation()) / FoodUtils.getFoodValue(storageToGet.getItemStack(), citizen) * homeBuildingLevel / 2.0));
InventoryUtils.transferItemStackIntoNextBestSlotInItemHandler(cookBuilding, storageToGet, qty, citizen.getInventoryCitizen());
return EAT;
}
Expand Down Expand Up @@ -501,7 +490,7 @@ private boolean hasFood()
{
citizenData.triggerInteraction(new StandardInteraction(Component.translatable(RAW_FOOD), ChatPriority.PENDING));
}
else if (InventoryUtils.hasItemInItemHandler(citizen.getInventoryCitizen(), stack -> CAN_EAT.test(stack) && !canEat(citizenData, stack)))
else if (InventoryUtils.hasItemInItemHandler(citizen.getInventoryCitizen(), stack -> FoodUtils.canEat(stack, citizenData.getHomeBuilding(), citizenData.getWorkBuilding())))
{
if (citizenData.isChild())
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@
import net.minecraftforge.items.wrapper.InvWrapper;
import org.jetbrains.annotations.NotNull;

import java.util.*;
import java.util.ArrayDeque;
import java.util.List;
import java.util.Queue;
import java.util.function.Predicate;

import static com.minecolonies.api.entity.ai.statemachine.states.AIWorkerState.*;
Expand Down Expand Up @@ -136,7 +138,7 @@ protected boolean reachedMaxToKeep()
}
final int buildingLimit = Math.max(1, building.getBuildingLevel() * building.getBuildingLevel()) * SLOT_PER_LINE;
return InventoryUtils.getCountFromBuildingWithLimit(building,
ItemStackUtils.CAN_EAT.and(stack -> FoodUtils.canEat(stack, building.getBuildingLevel() - 1)),
FoodUtils.EDIBLE.and(stack -> FoodUtils.canEatLevel(stack, building.getBuildingLevel() - 1)),
stack -> stack.getMaxStackSize() * 6) > buildingLimit;
}

Expand Down
Loading

0 comments on commit a0aff36

Please sign in to comment.