split client/server recipe caches

This commit is contained in:
Lorenz Wrobel 2026-05-18 22:45:23 +02:00
parent 425ba948a4
commit 32856f8da7
22 changed files with 274 additions and 229 deletions

View File

@ -37,8 +37,10 @@ import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.neoforged.neoforge.common.util.FakePlayer;
import org.jetbrains.annotations.Nullable;
import thedarkcolour.exdeorum.blockentity.logic.SieveLogic;
import thedarkcolour.exdeorum.config.EConfig;
import thedarkcolour.exdeorum.recipe.RecipeCaches;
import java.util.function.Function;

View File

@ -266,7 +266,7 @@ public class BarrelBlockEntity extends ETankBlockEntity {
var itemFluidCap = playerItem.getCapability(Capabilities.FluidHandler.ITEM);
if (itemFluidCap != null) {
var itemFluid = itemFluidCap.drain(1000, IFluidHandler.FluidAction.SIMULATE);
BarrelFluidMixingRecipe recipe = RecipeUtil.getFluidMixingRecipe(this.tank.getFluid(), itemFluid.getFluid());
BarrelFluidMixingRecipe recipe = getRecipeCaches().getFluidMixingRecipe(this.tank.getFluid(), itemFluid.getFluid());
// If draining item fluid was possible and tank has enough fluid to mix...
if (recipe != null && this.tank.getFluidAmount() >= recipe.baseFluid().amount() && itemFluid.getAmount() == 1000) {
@ -370,7 +370,7 @@ public class BarrelBlockEntity extends ETankBlockEntity {
return false;
}
var recipe = RecipeUtil.getBarrelMixingRecipe(this.level.getRecipeManager(), playerItem, this.tank.getFluid());
var recipe = getRecipeCaches().getBarrelMixingRecipe(this.level.getRecipeManager(), playerItem, this.tank.getFluid());
if (recipe != null) {
if (!simulate) {
@ -389,9 +389,9 @@ public class BarrelBlockEntity extends ETankBlockEntity {
private boolean tryComposting(ItemStack stack, boolean simulate) {
if (simulate) {
return RecipeUtil.isCompostable(stack);
return getRecipeCaches().isCompostable(stack);
} else {
var recipe = RecipeUtil.getBarrelCompostRecipe(stack);
var recipe = getRecipeCaches().getBarrelCompostRecipe(stack);
if (recipe != null) {
addCompost(stack, recipe.getVolume());
return true;
@ -437,7 +437,7 @@ public class BarrelBlockEntity extends ETankBlockEntity {
var aboveFluid = aboveFluidState.getType();
if (aboveFluid != Fluids.EMPTY) {
BarrelFluidMixingRecipe recipe = RecipeUtil.getFluidMixingRecipe(this.tank.getFluid(), aboveFluid instanceof FlowingFluid flowing ? flowing.getSource() : aboveFluid);
BarrelFluidMixingRecipe recipe = getRecipeCaches().getFluidMixingRecipe(this.tank.getFluid(), aboveFluid instanceof FlowingFluid flowing ? flowing.getSource() : aboveFluid);
if (recipe != null) {
// If additive is not consumed, just craft
@ -464,7 +464,7 @@ public class BarrelBlockEntity extends ETankBlockEntity {
this.currentTransformRecipe = null;
} else {
var belowState = this.level.getBlockState(this.worldPosition.below());
this.currentTransformRecipe = RecipeUtil.getFluidTransformationRecipe(this.tank.getFluid().getFluid(), belowState);
this.currentTransformRecipe = getRecipeCaches().getFluidTransformationRecipe(this.tank.getFluid().getFluid(), belowState);
if (this.currentTransformRecipe != null) {
var color = this.currentTransformRecipe.resultColor();

View File

@ -34,6 +34,10 @@ import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import thedarkcolour.exdeorum.network.VisualUpdateTracker;
import thedarkcolour.exdeorum.recipe.RecipeCaches;
import thedarkcolour.exdeorum.recipe.RecipeUtil;
import java.util.Objects;
public abstract class EBlockEntity extends BlockEntity {
public EBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
@ -78,4 +82,8 @@ public abstract class EBlockEntity extends BlockEntity {
public InteractionResult useWithoutItem(Level level, Player player) {
return InteractionResult.PASS;
}
public RecipeCaches getRecipeCaches() {
return RecipeUtil.getCaches(Objects.requireNonNull(this.level));
}
}

View File

@ -28,6 +28,7 @@ import thedarkcolour.exdeorum.recipe.crucible.CrucibleRecipe;
import thedarkcolour.exdeorum.registry.EBlockEntities;
import javax.annotation.Nullable;
import java.util.Objects;
public class LavaCrucibleBlockEntity extends AbstractCrucibleBlockEntity {
public LavaCrucibleBlockEntity(BlockPos pos, BlockState state) {
@ -36,12 +37,12 @@ public class LavaCrucibleBlockEntity extends AbstractCrucibleBlockEntity {
@Override
public int getMeltingRate() {
return RecipeUtil.getHeatValue(this.level.getBlockState(getBlockPos().below()));
return getRecipeCaches().getHeatValue(this.level.getBlockState(getBlockPos().below()));
}
@Override
protected @Nullable CrucibleRecipe getRecipe(ItemStack item) {
return RecipeUtil.getLavaCrucibleRecipe(item);
return getRecipeCaches().getLavaCrucibleRecipe(item);
}
@Override

View File

@ -20,20 +20,18 @@ package thedarkcolour.exdeorum.blockentity;
import net.minecraft.core.BlockPos;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.Registry;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.RandomSource;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.enchantment.Enchantments;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.storage.loot.LootContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import thedarkcolour.exdeorum.block.MechanicalHammerBlock;
import thedarkcolour.exdeorum.blockentity.helper.ItemHelper;
@ -64,8 +62,8 @@ public class MechanicalHammerBlockEntity extends AbstractMachineBlockEntity<Mech
super(EBlockEntities.MECHANICAL_HAMMER.get(), pos, state, ItemHandler::new, EConfig.SERVER.mechanicalHammerEnergyStorage.get());
}
public static boolean isValidInput(ItemStack stack) {
return RecipeUtil.getHammerRecipe(stack.getItem()) != null;
public static boolean isValidInput(Level level, ItemStack stack) {
return RecipeUtil.getCaches(level).getHammerRecipe(stack.getItem()) != null;
}
@Override
@ -128,7 +126,7 @@ public class MechanicalHammerBlockEntity extends AbstractMachineBlockEntity<Mech
var output = this.inventory.getStackInSlot(OUTPUT_SLOT);
if (output.isEmpty() || output.getCount() < output.getMaxStackSize()) {
var recipe = RecipeUtil.getHammerRecipe(input.getItem());
var recipe = getRecipeCaches().getHammerRecipe(input.getItem());
if (recipe != null && (output.isEmpty() || ItemStack.isSameItemSameComponents(recipe.result, output))) {
return recipe;
@ -229,9 +227,9 @@ public class MechanicalHammerBlockEntity extends AbstractMachineBlockEntity<Mech
}
@Override
public boolean isItemValid(int slot, @NotNull ItemStack stack) {
public boolean isItemValid(int slot, ItemStack stack) {
if (slot == INPUT_SLOT) {
return RecipeUtil.getHammerRecipe(stack.getItem()) != null;
return hammer.getRecipeCaches().getHammerRecipe(stack.getItem()) != null;
} else if (slot == HAMMER_SLOT) {
return stack.is(EItemTags.HAMMERS);
} else {

View File

@ -36,10 +36,13 @@ import thedarkcolour.exdeorum.blockentity.logic.SieveLogic;
import thedarkcolour.exdeorum.config.EConfig;
import thedarkcolour.exdeorum.data.TranslationKeys;
import thedarkcolour.exdeorum.menu.MechanicalSieveMenu;
import thedarkcolour.exdeorum.recipe.RecipeCaches;
import thedarkcolour.exdeorum.recipe.RecipeUtil;
import thedarkcolour.exdeorum.registry.EBlockEntities;
import thedarkcolour.exdeorum.tag.EItemTags;
import java.util.Objects;
public class MechanicalSieveBlockEntity extends AbstractMachineBlockEntity<MechanicalSieveBlockEntity> implements SieveLogic.Owner {
private static final Component TITLE = Component.translatable(TranslationKeys.MECHANICAL_SIEVE_SCREEN_TITLE);
private static final int INPUT_SLOT = 0;
@ -181,7 +184,7 @@ public class MechanicalSieveBlockEntity extends AbstractMachineBlockEntity<Mecha
@Override
public boolean isItemValid(int slot, ItemStack stack) {
if (slot == INPUT_SLOT) {
return !RecipeUtil.getSieveRecipes(getStackInSlot(1).getItem(), stack).isEmpty();
return !sieve.getRecipeCaches().getSieveRecipes(getStackInSlot(1).getItem(), stack).isEmpty();
} else if (slot == MESH_SLOT) {
return stack.is(EItemTags.SIEVE_MESHES);
} else {
@ -202,7 +205,7 @@ public class MechanicalSieveBlockEntity extends AbstractMachineBlockEntity<Mecha
@Override
protected void onContentsChanged(int slot) {
if (slot == MESH_SLOT) {
this.sieve.logic.setMesh(this.sieve.level.registryAccess(), this.sieve.inventory.getStackInSlot(MESH_SLOT));
this.sieve.logic.setMesh(Objects.requireNonNull(this.sieve.level).registryAccess(), this.sieve.inventory.getStackInSlot(MESH_SLOT));
}
}

View File

@ -36,7 +36,7 @@ public class WaterCrucibleBlockEntity extends AbstractCrucibleBlockEntity {
@Override
protected @Nullable CrucibleRecipe getRecipe(ItemStack item) {
return RecipeUtil.getWaterCrucibleRecipe(item);
return getRecipeCaches().getWaterCrucibleRecipe(item);
}
@Override

View File

@ -13,6 +13,6 @@ public class CompressedSieveLogic extends SieveLogic {
@Override
protected List<? extends SieveRecipe> getDropsFor(ItemStack contents) {
return RecipeUtil.getCompressedSieveRecipes(this.mesh.getItem(), contents);
return owner.getRecipeCaches().getCompressedSieveRecipes(this.mesh.getItem(), contents);
}
}

View File

@ -29,9 +29,11 @@ import net.minecraft.util.RandomSource;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.enchantment.Enchantments;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.storage.loot.LootContext;
import thedarkcolour.exdeorum.config.EConfig;
import thedarkcolour.exdeorum.recipe.RecipeCaches;
import thedarkcolour.exdeorum.recipe.RecipeUtil;
import thedarkcolour.exdeorum.recipe.sieve.SieveRecipe;
import thedarkcolour.exdeorum.tag.EItemTags;
@ -39,7 +41,7 @@ import thedarkcolour.exdeorum.tag.EItemTags;
import java.util.List;
public class SieveLogic {
private final Owner owner;
protected final Owner owner;
private final boolean mechanical;
// block currently being sifted
@ -129,7 +131,7 @@ public class SieveLogic {
}
protected List<? extends SieveRecipe> getDropsFor(ItemStack contents) {
return RecipeUtil.getSieveRecipes(this.mesh.getItem(), contents);
return owner.getRecipeCaches().getSieveRecipes(this.mesh.getItem(), contents);
}
protected int getResultAmount(SieveRecipe recipe, LootContext context, RandomSource rand) {
@ -237,6 +239,8 @@ public class SieveLogic {
// implement on the owner of this sieve logic
public interface Owner {
RecipeCaches getRecipeCaches();
ServerLevel getServerLevel();
// Return whether the result item was consumed

View File

@ -31,6 +31,7 @@ import net.minecraft.core.Holder;
import net.minecraft.core.registries.Registries;
import net.minecraft.util.Unit;
import net.minecraft.world.level.levelgen.presets.WorldPreset;
import net.neoforged.bus.api.EventPriority;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.fml.ModList;
import net.neoforged.fml.event.config.ModConfigEvent;
@ -70,7 +71,7 @@ public class ClientHandler {
fmlBus.addListener(ClientHandler::onPlayerRespawn);
fmlBus.addListener(ClientHandler::onPlayerLogout);
fmlBus.addListener(ClientHandler::onScreenOpen);
fmlBus.addListener(ClientHandler::onRecipesUpdated);
fmlBus.addListener(EventPriority.HIGHEST, ClientHandler::onRecipesUpdated); // We need to be at HIGH or HIGHEST to be called before JEI
if (ModList.get().isLoaded(ModIds.JEI) || ModList.get().isLoaded(ModIds.EMI)) {
modBus.addListener(ClientHandler::registerAdditionalModels);
@ -106,6 +107,7 @@ public class ClientHandler {
private static void onPlayerLogout(ClientPlayerNetworkEvent.LoggingOut event) {
isInVoidWorld = false;
ClientsideCode.getRecipeCaches().unload();
}
private static void onConfigChanged(ModConfigEvent.Reloading event) {
@ -160,9 +162,7 @@ public class ClientHandler {
}
private static void onRecipesUpdated(RecipesUpdatedEvent event) {
if (!Minecraft.getInstance().isSingleplayer()) {
RecipeUtil.reload(event.getRecipeManager());
}
ClientsideCode.getRecipeCaches().reload(event.getRecipeManager());
}
public static void disableVoidFogRendering() {

View File

@ -21,9 +21,18 @@ package thedarkcolour.exdeorum.client;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientPacketListener;
import net.minecraft.world.item.crafting.RecipeManager;
import net.neoforged.fml.util.thread.EffectiveSide;
import org.jetbrains.annotations.Nullable;
import thedarkcolour.exdeorum.recipe.RecipeCaches;
public class ClientsideCode {
private static final RecipeCaches RECIPE_CACHES = new RecipeCaches();
public static RecipeCaches getRecipeCaches() {
assert EffectiveSide.get().isClient() : Thread.currentThread().getName();
return RECIPE_CACHES;
}
@Nullable
public static RecipeManager getRecipeManager() {
ClientPacketListener connection = Minecraft.getInstance().getConnection();

View File

@ -24,10 +24,7 @@ import net.minecraft.network.chat.Component;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.item.crafting.Recipe;
import net.minecraft.world.item.crafting.RecipeHolder;
import net.minecraft.world.item.crafting.RecipeInput;
import net.minecraft.world.item.crafting.RecipeType;
import net.minecraft.world.item.crafting.*;
import net.minecraft.world.item.enchantment.ItemEnchantments;
import net.minecraft.world.level.ItemLike;
import net.minecraft.world.level.Level;
@ -79,8 +76,8 @@ public class CompatUtil {
return materials;
}
public static <C extends RecipeInput, R extends Recipe<C>, T> List<T> collectAllRecipes(RecipeType<R> recipeType, Function<R, T> mapper) {
var byType = RecipeUtil.getRecipeManager().byType(recipeType);
public static <C extends RecipeInput, R extends Recipe<C>, T> List<T> collectAllRecipes(RecipeManager recipeManager, RecipeType<R> recipeType, Function<R, T> mapper) {
var byType = recipeManager.byType(recipeType);
List<T> recipes = new ObjectArrayList<>(byType.size());
for (RecipeHolder<R> value : byType) {
recipes.add(mapper.apply(value.value()));

View File

@ -30,6 +30,7 @@ import net.minecraft.world.item.crafting.RecipeType;
import net.minecraft.world.level.storage.loot.providers.number.ConstantValue;
import net.minecraft.world.level.storage.loot.providers.number.NumberProvider;
import org.apache.commons.lang3.mutable.MutableInt;
import thedarkcolour.exdeorum.client.ClientsideCode;
import thedarkcolour.exdeorum.recipe.RecipeUtil;
import thedarkcolour.exdeorum.recipe.sieve.SieveRecipe;
import thedarkcolour.exdeorum.registry.EItems;
@ -47,7 +48,7 @@ public record XeiSieveRecipe(Ingredient ingredient, ItemStack mesh, List<Result>
public static ImmutableList<XeiSieveRecipe> getAllRecipesGrouped(RecipeType<? extends SieveRecipe> recipeType, MutableInt maxRows) {
int maxSieveRows = 1;
var recipes = CompatUtil.collectAllRecipes(recipeType, Function.identity());
var recipes = CompatUtil.collectAllRecipes(RecipeUtil.getClientRecipeManager(), recipeType, Function.identity());
Multimap<Ingredient, SieveRecipe> ingredientGrouper = ArrayListMultimap.create();
for (int i = 0; i < recipes.size(); i++) {

View File

@ -234,33 +234,33 @@ public class XeiUtil {
tooltipLines.accept(Component.translatable(TranslationKeys.SIEVE_RECIPE_MAX_OUTPUT, maxFormatted).withStyle(ChatFormatting.GRAY));
}
public interface HeatRecipeAcceptor {
void accept(int heat, BlockState state);
}
public static void addCrucibleHeatRecipes(HeatRecipeAcceptor acceptor) {
var values = new Object2IntOpenHashMap<Block>();
for (var entry : RecipeUtil.getHeatSources()) {
var state = entry.getKey();
var block = state.getBlock();
if (block instanceof WallTorchBlock) continue;
if (block != Blocks.AIR) {
final int newValue = entry.getIntValue();
values.computeInt(block, (key, value) -> {
if (value != null) {
return Math.max(value, newValue);
} else {
return newValue == 0 ? null : newValue;
}
});
}
}
for (var entry : values.object2IntEntrySet()) {
acceptor.accept(entry.getIntValue(), entry.getKey().defaultBlockState());
}
}
// public interface HeatRecipeAcceptor {
// void accept(int heat, BlockState state);
// }
//
// public static void addCrucibleHeatRecipes(HeatRecipeAcceptor acceptor) {
// var values = new Object2IntOpenHashMap<Block>();
// for (var entry : RecipeUtil.getHeatSources()) {
// var state = entry.getKey();
// var block = state.getBlock();
//
// if (block instanceof WallTorchBlock) continue;
//
// if (block != Blocks.AIR) {
// final int newValue = entry.getIntValue();
//
// values.computeInt(block, (key, value) -> {
// if (value != null) {
// return Math.max(value, newValue);
// } else {
// return newValue == 0 ? null : newValue;
// }
// });
// }
// }
//
// for (var entry : values.object2IntEntrySet()) {
// acceptor.accept(entry.getIntValue(), entry.getKey().defaultBlockState());
// }
// }
}

View File

@ -46,6 +46,7 @@ import net.neoforged.fml.ModList;
import net.neoforged.neoforge.fluids.FluidStack;
import net.neoforged.neoforge.registries.DeferredHolder;
import thedarkcolour.exdeorum.ExDeorum;
import thedarkcolour.exdeorum.client.ClientsideCode;
import thedarkcolour.exdeorum.client.screen.MechanicalHammerScreen;
import thedarkcolour.exdeorum.client.screen.MechanicalSieveScreen;
import thedarkcolour.exdeorum.compat.CompatUtil;
@ -215,7 +216,7 @@ public class ExDeorumJeiPlugin implements IModPlugin {
addRecipes(registration, HAMMER, ERecipeTypes.HAMMER);
//noinspection rawtypes,unchecked
addRecipes(registration, COMPRESSED_HAMMER, ((DeferredHolder) ERecipeTypes.COMPRESSED_HAMMER));
registration.addRecipes(CROOK, CompatUtil.collectAllRecipes(ERecipeTypes.CROOK.get(), CrookJeiRecipe::create));
registration.addRecipes(CROOK, CompatUtil.collectAllRecipes(RecipeUtil.getClientRecipeManager(), ERecipeTypes.CROOK.get(), CrookJeiRecipe::create));
registration.addRecipes(SIEVE, XeiSieveRecipe.getAllRecipesGrouped(ERecipeTypes.SIEVE.get(), XeiSieveRecipe.SIEVE_ROWS));
registration.addRecipes(COMPRESSED_SIEVE, XeiSieveRecipe.getAllRecipesGrouped(ERecipeTypes.COMPRESSED_SIEVE.get(), XeiSieveRecipe.COMPRESSED_SIEVE_ROWS));
@ -224,7 +225,7 @@ public class ExDeorumJeiPlugin implements IModPlugin {
private static void addCrucibleHeatSources(IRecipeRegistration registration) {
var values = new Object2IntOpenHashMap<Block>();
for (var entry : RecipeUtil.getHeatSources()) {
for (var entry : ClientsideCode.getRecipeCaches().getHeatSources()) {
var state = entry.getKey();
var block = state.getBlock();
@ -300,6 +301,6 @@ public class ExDeorumJeiPlugin implements IModPlugin {
}
private static <C extends RecipeInput, T extends Recipe<C>> void addRecipes(IRecipeRegistration registration, RecipeType<T> category, Supplier<net.minecraft.world.item.crafting.RecipeType<T>> type) {
registration.addRecipes(category, CompatUtil.collectAllRecipes(type.get(), Function.identity()));
registration.addRecipes(category, CompatUtil.collectAllRecipes(RecipeUtil.getClientRecipeManager(), type.get(), Function.identity()));
}
}

View File

@ -58,6 +58,7 @@ import net.neoforged.neoforge.fluids.FluidInteractionRegistry;
import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent;
import thedarkcolour.exdeorum.ExDeorum;
import thedarkcolour.exdeorum.blockentity.helper.ItemHelper;
import thedarkcolour.exdeorum.client.ClientsideCode;
import thedarkcolour.exdeorum.client.CompostColors;
import thedarkcolour.exdeorum.compat.ModIds;
import thedarkcolour.exdeorum.config.EConfig;
@ -66,6 +67,7 @@ import thedarkcolour.exdeorum.item.WateringCanItem;
import thedarkcolour.exdeorum.material.BarrelMaterial;
import thedarkcolour.exdeorum.network.NetworkHandler;
import thedarkcolour.exdeorum.network.VisualUpdateTracker;
import thedarkcolour.exdeorum.recipe.RecipeCaches;
import thedarkcolour.exdeorum.recipe.RecipeUtil;
import thedarkcolour.exdeorum.registry.EBlockEntities;
import thedarkcolour.exdeorum.registry.EFluids;
@ -95,7 +97,7 @@ public final class EventHandler {
}
private static void serverShutdown(ServerStoppingEvent event) {
RecipeUtil.unload();
RecipeUtil.getServerRecipeCaches().unload();
}
private static void handleDebugCommands(ClientChatEvent event) {
@ -209,10 +211,6 @@ public final class EventHandler {
ExDeorum.LOGGER.error("Unable to grant player the Void World advancement. Ex Deorum advancements will not show");
}
}
} else {
if (Minecraft.getInstance().getConnection() != null) {
RecipeUtil.reload(Minecraft.getInstance().getConnection().getRecipeManager());
}
}
}
@ -231,7 +229,8 @@ public final class EventHandler {
var recipes = event.getServerResources().getRecipeManager();
event.addListener((prepBarrier, resourceManager, prepProfiler, reloadProfiler, backgroundExecutor, gameExecutor) -> {
return prepBarrier.wait(Unit.INSTANCE).thenRunAsync(() -> {
RecipeUtil.reload(recipes);
// This is called on render thread when joining a singleplayer world, so we skip assertions
RecipeUtil.getServerRecipeCaches(true).reload(recipes);
}, gameExecutor);
});
}

View File

@ -25,6 +25,6 @@ public class CompressedHammerLootModifier extends HammerLootModifier {
@Override
protected @Nullable HammerRecipe getRecipe(Item itemForm) {
return RecipeUtil.getCompressedHammerRecipe(itemForm);
return RecipeUtil.getServerRecipeCaches().getCompressedHammerRecipe(itemForm);
}
}

View File

@ -34,7 +34,6 @@ import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition;
import net.neoforged.neoforge.common.loot.IGlobalLootModifier;
import net.neoforged.neoforge.common.loot.LootModifier;
import org.jetbrains.annotations.NotNull;
import thedarkcolour.exdeorum.recipe.RecipeUtil;
import thedarkcolour.exdeorum.recipe.crook.CrookRecipe;
@ -48,7 +47,7 @@ public class CrookLootModifier extends LootModifier {
}
@Override
protected @NotNull ObjectArrayList<ItemStack> doApply(ObjectArrayList<ItemStack> generatedLoot, LootContext context) {
protected ObjectArrayList<ItemStack> doApply(ObjectArrayList<ItemStack> generatedLoot, LootContext context) {
var state = context.getParamOrNull(LootContextParams.BLOCK_STATE);
var stack = context.getParamOrNull(LootContextParams.TOOL);
@ -59,7 +58,7 @@ public class CrookLootModifier extends LootModifier {
var fortune = stack.getEnchantmentLevel(context.getLevel().holderLookup(Registries.ENCHANTMENT).getOrThrow(Enchantments.FORTUNE));
var rolls = Math.max(1, Mth.ceil(fortune / 3f));
for (CrookRecipe recipe : RecipeUtil.getCrookRecipes(state)) {
for (CrookRecipe recipe : RecipeUtil.getCaches(context.getLevel()).getCrookRecipes(state)) {
for (int i = 0; i < rolls; i++) {
if (rand.nextFloat() < recipe.chance()) {
generatedLoot.add(recipe.result().copy());

View File

@ -92,7 +92,7 @@ public class HammerLootModifier extends LootModifier {
@Nullable
protected HammerRecipe getRecipe(Item itemForm) {
return RecipeUtil.getHammerRecipe(itemForm);
return RecipeUtil.getServerRecipeCaches().getHammerRecipe(itemForm);
}
@Override

View File

@ -70,7 +70,7 @@ public class MechanicalHammerMenu extends AbstractMachineMenu<MechanicalHammerBl
if (!moveItemStackTo(clickedStack, NUM_SLOTS, NUM_SLOTS + PLAYER_SLOTS, false)) {
return ItemStack.EMPTY;
}
} else if (MechanicalHammerBlockEntity.isValidInput(clickedStack)) { // attempting to move into input slot
} else if (MechanicalHammerBlockEntity.isValidInput(player.level(), clickedStack)) { // attempting to move into input slot
if (!moveItemStackTo(clickedStack, 0, 1, false)) {
return ItemStack.EMPTY;
}

View File

@ -0,0 +1,158 @@
package thedarkcolour.exdeorum.recipe;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.ObjectSet;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.RecipeHolder;
import net.minecraft.world.item.crafting.RecipeManager;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.Fluid;
import net.minecraft.world.level.material.Fluids;
import net.neoforged.neoforge.fluids.FluidStack;
import org.jetbrains.annotations.Nullable;
import thedarkcolour.exdeorum.recipe.barrel.BarrelCompostRecipe;
import thedarkcolour.exdeorum.recipe.barrel.BarrelFluidMixingRecipe;
import thedarkcolour.exdeorum.recipe.barrel.BarrelMixingRecipe;
import thedarkcolour.exdeorum.recipe.barrel.FluidTransformationRecipe;
import thedarkcolour.exdeorum.recipe.cache.*;
import thedarkcolour.exdeorum.recipe.crook.CrookRecipe;
import thedarkcolour.exdeorum.recipe.crucible.CrucibleRecipe;
import thedarkcolour.exdeorum.recipe.hammer.CompressedHammerRecipe;
import thedarkcolour.exdeorum.recipe.hammer.HammerRecipe;
import thedarkcolour.exdeorum.recipe.sieve.CompressedSieveRecipe;
import thedarkcolour.exdeorum.recipe.sieve.SieveRecipe;
import thedarkcolour.exdeorum.registry.ERecipeTypes;
import java.util.Collection;
import java.util.List;
public class RecipeCaches {
private SingleIngredientRecipeCache<BarrelCompostRecipe> barrelCompostRecipeCache;
private SingleIngredientRecipeCache<CrucibleRecipe> lavaCrucibleRecipeCache;
private SingleIngredientRecipeCache<CrucibleRecipe> waterCrucibleRecipeCache;
private SingleIngredientRecipeCache<HammerRecipe> hammerRecipeCache;
private SingleIngredientRecipeCache<CompressedHammerRecipe> compressedHammerRecipeCache;
private SieveRecipeCache<SieveRecipe> sieveRecipeCache;
private SieveRecipeCache<CompressedSieveRecipe> compressedSieveRecipeCache;
private BarrelFluidMixingRecipeCache barrelFluidMixingRecipeCache;
private FluidTransformationRecipeCache fluidTransformationRecipeCache;
private CrookRecipeCache crookRecipeCache;
private CrucibleHeatRecipeCache crucibleHeatRecipeCache;
public List<SieveRecipe> getSieveRecipes(Item mesh, ItemStack item) {
return sieveRecipeCache.getRecipe(mesh, item);
}
public List<CompressedSieveRecipe> getCompressedSieveRecipes(Item mesh, ItemStack item) {
return compressedSieveRecipeCache.getRecipe(mesh, item);
}
@Nullable
public CrucibleRecipe getLavaCrucibleRecipe(ItemStack item) {
return lavaCrucibleRecipeCache.getRecipe(item);
}
@Nullable
public CrucibleRecipe getWaterCrucibleRecipe(ItemStack item) {
return waterCrucibleRecipeCache.getRecipe(item);
}
@Nullable
public BarrelCompostRecipe getBarrelCompostRecipe(ItemStack item) {
return barrelCompostRecipeCache.getRecipe(item);
}
@Nullable
public HammerRecipe getHammerRecipe(Item item) {
return hammerRecipeCache.getRecipe(item);
}
public Collection<RecipeHolder<HammerRecipe>> getCachedHammerRecipes() {
return hammerRecipeCache.getAllRecipes();
}
@Nullable
public CompressedHammerRecipe getCompressedHammerRecipe(Item item) {
return compressedHammerRecipeCache.getRecipe(item);
}
public Collection<RecipeHolder<CompressedHammerRecipe>> getCachedCompressedHammerRecipes() {
return compressedHammerRecipeCache.getAllRecipes();
}
public List<CrookRecipe> getCrookRecipes(BlockState state) {
return crookRecipeCache.getRecipes(state);
}
public boolean isCompostable(ItemStack stack) {
return barrelCompostRecipeCache != null && barrelCompostRecipeCache.getRecipe(stack) != null;
}
public int getHeatValue(BlockState state) {
return crucibleHeatRecipeCache.getValue(state);
}
public ObjectSet<Object2IntMap.Entry<BlockState>> getHeatSources() {
return crucibleHeatRecipeCache.getEntries();
}
// todo stop using the RecipeManager
@Nullable
public BarrelMixingRecipe getBarrelMixingRecipe(RecipeManager recipes, ItemStack stack, FluidStack fluid) {
for (var recipe : recipes.byType(ERecipeTypes.BARREL_MIXING.get())) {
if (recipe.value().matches(stack, fluid)) {
return recipe.value();
}
}
return null;
}
@Nullable
public BarrelFluidMixingRecipe getFluidMixingRecipe(FluidStack base, Fluid additive) {
var recipe = barrelFluidMixingRecipeCache.getRecipe(base.getFluid(), additive);
if (recipe != null && base.getAmount() >= recipe.baseFluid().amount()) {
return recipe;
} else {
return null;
}
}
@Nullable
public FluidTransformationRecipe getFluidTransformationRecipe(Fluid baseFluid, BlockState catalystState) {
if (baseFluid != Fluids.EMPTY) {
return fluidTransformationRecipeCache.getRecipe(baseFluid, catalystState);
} else {
return null;
}
}
public void reload(RecipeManager recipes) {
barrelCompostRecipeCache = new SingleIngredientRecipeCache<>(recipes, ERecipeTypes.BARREL_COMPOST);
lavaCrucibleRecipeCache = new SingleIngredientRecipeCache<>(recipes, ERecipeTypes.LAVA_CRUCIBLE);
waterCrucibleRecipeCache = new SingleIngredientRecipeCache<>(recipes, ERecipeTypes.WATER_CRUCIBLE);
hammerRecipeCache = new SingleIngredientRecipeCache<>(recipes, ERecipeTypes.HAMMER).trackAllRecipes();
compressedHammerRecipeCache = new SingleIngredientRecipeCache<>(recipes, ERecipeTypes.COMPRESSED_HAMMER).trackAllRecipes();
sieveRecipeCache = new SieveRecipeCache<>(recipes, ERecipeTypes.SIEVE);
compressedSieveRecipeCache = new SieveRecipeCache<>(recipes, ERecipeTypes.COMPRESSED_SIEVE);
barrelFluidMixingRecipeCache = new BarrelFluidMixingRecipeCache(recipes);
fluidTransformationRecipeCache = new FluidTransformationRecipeCache(recipes);
crookRecipeCache = new CrookRecipeCache(recipes);
crucibleHeatRecipeCache = new CrucibleHeatRecipeCache(recipes);
}
public void unload() {
barrelCompostRecipeCache = null;
lavaCrucibleRecipeCache = null;
waterCrucibleRecipeCache = null;
hammerRecipeCache = null;
compressedHammerRecipeCache = null;
sieveRecipeCache = null;
compressedSieveRecipeCache = null;
barrelFluidMixingRecipeCache = null;
fluidTransformationRecipeCache = null;
crookRecipeCache = null;
crucibleHeatRecipeCache = null;
}
}

View File

@ -19,52 +19,32 @@
package thedarkcolour.exdeorum.recipe;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import it.unimi.dsi.fastutil.objects.ObjectSet;
import net.minecraft.commands.arguments.blocks.BlockStateParser;
import net.minecraft.core.Registry;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.tags.TagKey;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.item.crafting.RecipeHolder;
import net.minecraft.world.item.crafting.RecipeManager;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.Property;
import net.minecraft.world.level.material.Fluid;
import net.minecraft.world.level.material.Fluids;
import net.minecraft.world.level.storage.loot.LootContext;
import net.minecraft.world.level.storage.loot.LootParams;
import net.minecraft.world.level.storage.loot.providers.number.*;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.fml.loading.FMLEnvironment;
import net.neoforged.neoforge.fluids.FluidStack;
import net.neoforged.fml.util.thread.EffectiveSide;
import net.neoforged.neoforge.server.ServerLifecycleHooks;
import org.jetbrains.annotations.Nullable;
import thedarkcolour.exdeorum.compat.PreferredOres;
import thedarkcolour.exdeorum.client.ClientsideCode;
import thedarkcolour.exdeorum.compat.PreferredOres;
import thedarkcolour.exdeorum.loot.SummationGenerator;
import thedarkcolour.exdeorum.recipe.barrel.BarrelCompostRecipe;
import thedarkcolour.exdeorum.recipe.barrel.BarrelFluidMixingRecipe;
import thedarkcolour.exdeorum.recipe.barrel.BarrelMixingRecipe;
import thedarkcolour.exdeorum.recipe.barrel.FluidTransformationRecipe;
import thedarkcolour.exdeorum.recipe.cache.*;
import thedarkcolour.exdeorum.recipe.crook.CrookRecipe;
import thedarkcolour.exdeorum.recipe.crucible.CrucibleRecipe;
import thedarkcolour.exdeorum.recipe.hammer.CompressedHammerRecipe;
import thedarkcolour.exdeorum.recipe.hammer.HammerRecipe;
import thedarkcolour.exdeorum.recipe.sieve.CompressedSieveRecipe;
import thedarkcolour.exdeorum.recipe.sieve.SieveRecipe;
import thedarkcolour.exdeorum.registry.ENumberProviders;
import thedarkcolour.exdeorum.registry.ERecipeTypes;
import java.util.*;
@ -75,85 +55,19 @@ public final class RecipeUtil {
private static final int SUMMATION_TYPE = 4;
private static final int UNKNOWN_TYPE = 99;
private static SingleIngredientRecipeCache<BarrelCompostRecipe> barrelCompostRecipeCache;
private static SingleIngredientRecipeCache<CrucibleRecipe> lavaCrucibleRecipeCache;
private static SingleIngredientRecipeCache<CrucibleRecipe> waterCrucibleRecipeCache;
private static SingleIngredientRecipeCache<HammerRecipe> hammerRecipeCache;
private static SingleIngredientRecipeCache<CompressedHammerRecipe> compressedHammerRecipeCache;
private static SieveRecipeCache<SieveRecipe> sieveRecipeCache;
private static SieveRecipeCache<CompressedSieveRecipe> compressedSieveRecipeCache;
private static BarrelFluidMixingRecipeCache barrelFluidMixingRecipeCache;
private static FluidTransformationRecipeCache fluidTransformationRecipeCache;
private static CrookRecipeCache crookRecipeCache;
private static CrucibleHeatRecipeCache crucibleHeatRecipeCache;
private static final RecipeCaches SERVER_RECIPE_CACHES = new RecipeCaches();
public static void reload(RecipeManager recipes) {
barrelCompostRecipeCache = new SingleIngredientRecipeCache<>(recipes, ERecipeTypes.BARREL_COMPOST);
lavaCrucibleRecipeCache = new SingleIngredientRecipeCache<>(recipes, ERecipeTypes.LAVA_CRUCIBLE);
waterCrucibleRecipeCache = new SingleIngredientRecipeCache<>(recipes, ERecipeTypes.WATER_CRUCIBLE);
hammerRecipeCache = new SingleIngredientRecipeCache<>(recipes, ERecipeTypes.HAMMER).trackAllRecipes();
compressedHammerRecipeCache = new SingleIngredientRecipeCache<>(recipes, ERecipeTypes.COMPRESSED_HAMMER).trackAllRecipes();
sieveRecipeCache = new SieveRecipeCache<>(recipes, ERecipeTypes.SIEVE);
compressedSieveRecipeCache = new SieveRecipeCache<>(recipes, ERecipeTypes.COMPRESSED_SIEVE);
barrelFluidMixingRecipeCache = new BarrelFluidMixingRecipeCache(recipes);
fluidTransformationRecipeCache = new FluidTransformationRecipeCache(recipes);
crookRecipeCache = new CrookRecipeCache(recipes);
crucibleHeatRecipeCache = new CrucibleHeatRecipeCache(recipes);
public static RecipeCaches getServerRecipeCaches() {
return getServerRecipeCaches(false);
}
public static void unload() {
barrelCompostRecipeCache = null;
lavaCrucibleRecipeCache = null;
waterCrucibleRecipeCache = null;
hammerRecipeCache = null;
compressedHammerRecipeCache = null;
sieveRecipeCache = null;
compressedSieveRecipeCache = null;
barrelFluidMixingRecipeCache = null;
fluidTransformationRecipeCache = null;
crookRecipeCache = null;
crucibleHeatRecipeCache = null;
public static RecipeCaches getServerRecipeCaches(boolean skipAssert) {
assert skipAssert || EffectiveSide.get().isServer() : Thread.currentThread().getName();
return SERVER_RECIPE_CACHES;
}
public static List<SieveRecipe> getSieveRecipes(Item mesh, ItemStack item) {
return sieveRecipeCache.getRecipe(mesh, item);
}
public static List<CompressedSieveRecipe> getCompressedSieveRecipes(Item mesh, ItemStack item) {
return compressedSieveRecipeCache.getRecipe(mesh, item);
}
@Nullable
public static CrucibleRecipe getLavaCrucibleRecipe(ItemStack item) {
return lavaCrucibleRecipeCache.getRecipe(item);
}
@Nullable
public static CrucibleRecipe getWaterCrucibleRecipe(ItemStack item) {
return waterCrucibleRecipeCache.getRecipe(item);
}
@Nullable
public static BarrelCompostRecipe getBarrelCompostRecipe(ItemStack item) {
return barrelCompostRecipeCache.getRecipe(item);
}
@Nullable
public static HammerRecipe getHammerRecipe(Item item) {
return hammerRecipeCache.getRecipe(item);
}
public static Collection<RecipeHolder<HammerRecipe>> getCachedHammerRecipes() {
return hammerRecipeCache.getAllRecipes();
}
@Nullable
public static CompressedHammerRecipe getCompressedHammerRecipe(Item item) {
return compressedHammerRecipeCache.getRecipe(item);
}
public static Collection<RecipeHolder<CompressedHammerRecipe>> getCachedCompressedHammerRecipes() {
return compressedHammerRecipeCache.getAllRecipes();
public static RecipeCaches getCaches(Level level) {
return level.isClientSide() ? ClientsideCode.getRecipeCaches() : getServerRecipeCaches();
}
public static void toNetworkNumberProvider(FriendlyByteBuf buffer, NumberProvider provider) {
@ -281,41 +195,6 @@ public final class RecipeUtil {
}
}
public static boolean isCompostable(ItemStack stack) {
return barrelCompostRecipeCache != null && barrelCompostRecipeCache.getRecipe(stack) != null;
}
// todo stop using the RecipeManager
@Nullable
public static BarrelMixingRecipe getBarrelMixingRecipe(RecipeManager recipes, ItemStack stack, FluidStack fluid) {
for (var recipe : recipes.byType(ERecipeTypes.BARREL_MIXING.get())) {
if (recipe.value().matches(stack, fluid)) {
return recipe.value();
}
}
return null;
}
@Nullable
public static BarrelFluidMixingRecipe getFluidMixingRecipe(FluidStack base, Fluid additive) {
var recipe = barrelFluidMixingRecipeCache.getRecipe(base.getFluid(), additive);
if (recipe != null && base.getAmount() >= recipe.baseFluid().amount()) {
return recipe;
} else {
return null;
}
}
@Nullable
public static FluidTransformationRecipe getFluidTransformationRecipe(Fluid baseFluid, BlockState catalystState) {
if (baseFluid != Fluids.EMPTY) {
return fluidTransformationRecipeCache.getRecipe(baseFluid, catalystState);
} else {
return null;
}
}
@SuppressWarnings("IfCanBeSwitch")
public static double getExpectedValue(NumberProvider provider) {
if (provider instanceof ConstantValue constant) {
@ -352,18 +231,6 @@ public final class RecipeUtil {
return new LootContext.Builder(new LootParams(level, Map.of(), Map.of(), 0)).create(Optional.empty());
}
public static List<CrookRecipe> getCrookRecipes(BlockState state) {
return crookRecipeCache.getRecipes(state);
}
public static int getHeatValue(BlockState state) {
return crucibleHeatRecipeCache.getValue(state);
}
public static ObjectSet<Object2IntMap.Entry<BlockState>> getHeatSources() {
return crucibleHeatRecipeCache.getEntries();
}
@SuppressWarnings({"unchecked", "rawtypes"})
public static String writeBlockState(BlockState state) {
var registryKey = BuiltInRegistries.BLOCK.getKey(state.getBlock());
@ -410,13 +277,11 @@ public final class RecipeUtil {
return ResourceLocation.tryParse(string) != null;
}
/**
* From Forestry: Community Edition
* @return The global registry manager. {@code null} on server when there is no server, or when there is no world (on client).
*/
@Nullable
public static RecipeManager getRecipeManager() {
MinecraftServer server = ServerLifecycleHooks.getCurrentServer();
return server == null ? (FMLEnvironment.dist == Dist.CLIENT ? ClientsideCode.getRecipeManager() : null) : server.getRecipeManager();
public static RecipeManager getClientRecipeManager() {
return Objects.requireNonNull(ClientsideCode.getRecipeManager());
}
public static RecipeManager getServerRecipeManager() {
return Objects.requireNonNull(ServerLifecycleHooks.getCurrentServer()).getRecipeManager();
}
}