Compare commits

...

9 Commits

Author SHA1 Message Date
thedarkcolour
a5d25a09f7 Style fixes 2026-05-31 21:31:11 -07:00
Lorenz Wrobel
bec66622a0
split client/server recipe logic (#179)
* split client/server recipe caches

* cleanup
2026-05-31 17:34:48 -07:00
thedarkcolour
fecf69353e Fixed Crucible bug where pending solids could be converted to another fluid while tank was empty
Closes #180
2026-05-31 17:28:33 -07:00
thedarkcolour
3a532ff569 Prevent End Cake from crashing fake players
Closes #178
2026-05-31 14:19:48 -07:00
Jake Potrebic
fda9268490
Merge pull request #182 from Machine-Maker/fix/1.21.1-compressed-sieve
Backport compressed sieve fix
2026-05-30 10:04:47 -07:00
thedarkcolour
425ba948a4
Merge pull request #154 from Abbage230/patch-1
Update ja_jp.json (Fix)
2025-11-30 15:43:48 -08:00
thedarkcolour
b41f089549
Merge pull request #161 from SleepYamadaRyo/patch-1
Update zh_cn
2025-11-30 15:43:31 -08:00
眠そうな山田リョウ
cf66af2bf3
Update zh_cn
Update zh_cn
2025-11-08 12:13:42 +08:00
Abbage230
7cb1af3f4a
Update ja_jp.json (Fix) 2025-08-29 09:03:16 +09:00
29 changed files with 494 additions and 309 deletions

View File

@ -1,3 +1,8 @@
## Ex Deorum 3.11
- Fixed End Cakes crashing fake players (#178)
- Fixed Compressed Sieves not allowing simultaneous insertion of material even when Simultaneous Compressed Sieve Usage was enabled
- Fixed Crucible bug where pending solids could be converted to another fluid while tank was empty (#180)
## Ex Deorum 3.10
- Now requires KubeJS 7.2 to fix incompatibility (#158)

View File

@ -89,8 +89,9 @@ public class EndCakeBlock extends CakeBlock {
return InteractionResult.PASS;
}
// todo test
private static boolean tryTeleport(ServerLevel level, Player player) {
if (player.isFakePlayer()) return false;
if (level.dimension() != Level.END) {
var endLevel = level.getServer().getLevel(Level.END);

View File

@ -208,10 +208,11 @@ public abstract class AbstractCrucibleBlockEntity extends ETankBlockEntity {
}
var result = recipe.getResult();
var contained = this.tank.getFluid();
var hadPendingSolids = this.solids > 0;
shrinkAction.accept(item);
this.solids = (short) Math.min(this.solids + result.getAmount(), MAX_SOLIDS);
if (contained.isEmpty()) {
if (contained.isEmpty() && !hadPendingSolids) {
this.fluid = result.getFluid();
updateLight(this.level, this.worldPosition, this.fluid);
}
@ -242,7 +243,7 @@ public abstract class AbstractCrucibleBlockEntity extends ETankBlockEntity {
var result = recipe.getResult();
var contained = this.tank.getFluid();
if (FluidStack.isSameFluidSameComponents(result, contained) || contained.isEmpty()) {
if (FluidStack.isSameFluidSameComponents(result, contained) || (contained.isEmpty() && canAddToPendingFluid(result))) {
return result.getAmount() + this.solids <= MAX_SOLIDS ? InsertionResult.YES : InsertionResult.FULL;
}
}
@ -250,6 +251,10 @@ public abstract class AbstractCrucibleBlockEntity extends ETankBlockEntity {
return InsertionResult.NO;
}
private boolean canAddToPendingFluid(FluidStack result) {
return this.solids == 0 || this.fluid == null || result.getFluid() == this.fluid;
}
public abstract int getMeltingRate();
public int getSolids() {
@ -304,7 +309,7 @@ public abstract class AbstractCrucibleBlockEntity extends ETankBlockEntity {
}
}
private static class FluidHandler extends FluidHelper {
private class FluidHandler extends FluidHelper {
public FluidHandler() {
super(MAX_FLUID_CAPACITY);
}
@ -313,6 +318,16 @@ public abstract class AbstractCrucibleBlockEntity extends ETankBlockEntity {
public boolean isFluidValid(FluidStack stack) {
return false;
}
@Override
protected void onContentsChanged() {
if (this.fluid.isEmpty() && AbstractCrucibleBlockEntity.this.solids == 0) {
AbstractCrucibleBlockEntity.this.fluid = null;
}
updateLight(AbstractCrucibleBlockEntity.this.level, AbstractCrucibleBlockEntity.this.worldPosition, this.fluid.getFluid());
AbstractCrucibleBlockEntity.this.markUpdated();
}
}
// inner class

View File

@ -26,7 +26,6 @@ import net.minecraft.server.level.ServerLevel;
import net.minecraft.stats.Stats;
import net.minecraft.util.RandomSource;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.ItemInteractionResult;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.player.Player;
@ -160,7 +159,7 @@ public abstract class AbstractSieveBlockEntity extends EBlockEntity implements S
}
if ((x | z) != 0) {
if (level.getBlockEntity(cursor) instanceof SieveBlockEntity other) {
if (level.getBlockEntity(cursor) instanceof AbstractSieveBlockEntity other && other.getType() == getType()) {
var otherLogic = other.logic;
if (otherLogic.getContents().isEmpty()) {

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

@ -23,7 +23,6 @@ import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import thedarkcolour.exdeorum.recipe.RecipeUtil;
import thedarkcolour.exdeorum.recipe.crucible.CrucibleRecipe;
import thedarkcolour.exdeorum.registry.EBlockEntities;
@ -36,12 +35,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 this.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 !this.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

@ -1,7 +1,6 @@
package thedarkcolour.exdeorum.blockentity.logic;
import net.minecraft.world.item.ItemStack;
import thedarkcolour.exdeorum.recipe.RecipeUtil;
import thedarkcolour.exdeorum.recipe.sieve.SieveRecipe;
import java.util.List;
@ -13,6 +12,6 @@ public class CompressedSieveLogic extends SieveLogic {
@Override
protected List<? extends SieveRecipe> getDropsFor(ItemStack contents) {
return RecipeUtil.getCompressedSieveRecipes(this.mesh.getItem(), contents);
return this.owner.getRecipeCaches().getCompressedSieveRecipes(this.mesh.getItem(), contents);
}
}

View File

@ -32,6 +32,7 @@ import net.minecraft.world.item.enchantment.Enchantments;
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 +40,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 +130,7 @@ public class SieveLogic {
}
protected List<? extends SieveRecipe> getDropsFor(ItemStack contents) {
return RecipeUtil.getSieveRecipes(this.mesh.getItem(), contents);
return this.owner.getRecipeCaches().getSieveRecipes(this.mesh.getItem(), contents);
}
protected int getResultAmount(SieveRecipe recipe, LootContext context, RandomSource rand) {
@ -237,6 +238,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,8 @@ public class ClientHandler {
fmlBus.addListener(ClientHandler::onPlayerRespawn);
fmlBus.addListener(ClientHandler::onPlayerLogout);
fmlBus.addListener(ClientHandler::onScreenOpen);
fmlBus.addListener(ClientHandler::onRecipesUpdated);
// we need to be at HIGH or HIGHEST to be called before JEI
fmlBus.addListener(EventPriority.HIGHEST, ClientHandler::onRecipesUpdated);
if (ModList.get().isLoaded(ModIds.JEI) || ModList.get().isLoaded(ModIds.EMI)) {
modBus.addListener(ClientHandler::registerAdditionalModels);
@ -106,6 +108,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 +163,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,17 @@ 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() {
return RECIPE_CACHES;
}
@Nullable
public static RecipeManager getRecipeManager() {
ClientPacketListener connection = Minecraft.getInstance().getConnection();

View File

@ -27,6 +27,7 @@ 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.RecipeManager;
import net.minecraft.world.item.crafting.RecipeType;
import net.minecraft.world.item.enchantment.ItemEnchantments;
import net.minecraft.world.level.ItemLike;
@ -34,7 +35,6 @@ import net.minecraft.world.level.Level;
import net.neoforged.fml.ModList;
import thedarkcolour.exdeorum.material.DefaultMaterials;
import thedarkcolour.exdeorum.material.MaterialRegistry;
import thedarkcolour.exdeorum.recipe.RecipeUtil;
import thedarkcolour.exdeorum.registry.EItems;
import java.util.ArrayList;
@ -79,8 +79,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

@ -47,7 +47,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

@ -44,7 +44,7 @@ public class CycleTimer {
public void onDraw() {
if (!Screen.hasShiftDown()) {
if (pausedDuration > 0) {
if (this.pausedDuration > 0) {
this.startTime += this.pausedDuration;
this.pausedDuration = 0;
}

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

@ -75,7 +75,7 @@ class SieveCategory implements IRecipeCategory<XeiSieveRecipe> {
@Override
public int getHeight() {
return XeiUtil.SIEVE_ROW_START + XeiUtil.SIEVE_ROW_HEIGHT * rows.intValue();
return XeiUtil.SIEVE_ROW_START + XeiUtil.SIEVE_ROW_HEIGHT * this.rows.intValue();
}
@Override

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

@ -3,6 +3,7 @@ package thedarkcolour.exdeorum.loot;
import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.storage.loot.LootContext;
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition;
import net.neoforged.neoforge.common.loot.IGlobalLootModifier;
import net.neoforged.neoforge.common.loot.LootModifier;
@ -24,7 +25,7 @@ public class CompressedHammerLootModifier extends HammerLootModifier {
}
@Override
protected @Nullable HammerRecipe getRecipe(Item itemForm) {
return RecipeUtil.getCompressedHammerRecipe(itemForm);
protected @Nullable HammerRecipe getRecipe(Item itemForm, LootContext context) {
return RecipeUtil.getCaches(context.getLevel()).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

@ -69,7 +69,7 @@ public class HammerLootModifier extends LootModifier {
return generatedLoot;
}
var recipe = getRecipe(itemForm);
var recipe = getRecipe(itemForm, context);
if (recipe == null) {
return generatedLoot;
}
@ -91,8 +91,8 @@ public class HammerLootModifier extends LootModifier {
}
@Nullable
protected HammerRecipe getRecipe(Item itemForm) {
return RecipeUtil.getHammerRecipe(itemForm);
protected HammerRecipe getRecipe(Item itemForm, LootContext context) {
return RecipeUtil.getCaches(context.getLevel()).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 this.sieveRecipeCache.getRecipe(mesh, item);
}
public List<CompressedSieveRecipe> getCompressedSieveRecipes(Item mesh, ItemStack item) {
return this.compressedSieveRecipeCache.getRecipe(mesh, item);
}
@Nullable
public CrucibleRecipe getLavaCrucibleRecipe(ItemStack item) {
return this.lavaCrucibleRecipeCache.getRecipe(item);
}
@Nullable
public CrucibleRecipe getWaterCrucibleRecipe(ItemStack item) {
return this.waterCrucibleRecipeCache.getRecipe(item);
}
@Nullable
public BarrelCompostRecipe getBarrelCompostRecipe(ItemStack item) {
return this.barrelCompostRecipeCache.getRecipe(item);
}
@Nullable
public HammerRecipe getHammerRecipe(Item item) {
return this.hammerRecipeCache.getRecipe(item);
}
public Collection<RecipeHolder<HammerRecipe>> getCachedHammerRecipes() {
return this.hammerRecipeCache.getAllRecipes();
}
@Nullable
public CompressedHammerRecipe getCompressedHammerRecipe(Item item) {
return this.compressedHammerRecipeCache.getRecipe(item);
}
public Collection<RecipeHolder<CompressedHammerRecipe>> getCachedCompressedHammerRecipes() {
return this.compressedHammerRecipeCache.getAllRecipes();
}
public List<CrookRecipe> getCrookRecipes(BlockState state) {
return this.crookRecipeCache.getRecipes(state);
}
public boolean isCompostable(ItemStack stack) {
return this.barrelCompostRecipeCache != null && this.barrelCompostRecipeCache.getRecipe(stack) != null;
}
public int getHeatValue(BlockState state) {
return this.crucibleHeatRecipeCache.getValue(state);
}
public ObjectSet<Object2IntMap.Entry<BlockState>> getHeatSources() {
return this.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 = this.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 this.fluidTransformationRecipeCache.getRecipe(baseFluid, catalystState);
} else {
return null;
}
}
public void reload(RecipeManager recipes) {
this.barrelCompostRecipeCache = new SingleIngredientRecipeCache<>(recipes, ERecipeTypes.BARREL_COMPOST);
this.lavaCrucibleRecipeCache = new SingleIngredientRecipeCache<>(recipes, ERecipeTypes.LAVA_CRUCIBLE);
this.waterCrucibleRecipeCache = new SingleIngredientRecipeCache<>(recipes, ERecipeTypes.WATER_CRUCIBLE);
this.hammerRecipeCache = new SingleIngredientRecipeCache<>(recipes, ERecipeTypes.HAMMER).trackAllRecipes();
this.compressedHammerRecipeCache = new SingleIngredientRecipeCache<>(recipes, ERecipeTypes.COMPRESSED_HAMMER).trackAllRecipes();
this.sieveRecipeCache = new SieveRecipeCache<>(recipes, ERecipeTypes.SIEVE);
this.compressedSieveRecipeCache = new SieveRecipeCache<>(recipes, ERecipeTypes.COMPRESSED_SIEVE);
this.barrelFluidMixingRecipeCache = new BarrelFluidMixingRecipeCache(recipes);
this.fluidTransformationRecipeCache = new FluidTransformationRecipeCache(recipes);
this.crookRecipeCache = new CrookRecipeCache(recipes);
this.crucibleHeatRecipeCache = new CrucibleHeatRecipeCache(recipes);
}
public void unload() {
this.barrelCompostRecipeCache = null;
this.lavaCrucibleRecipeCache = null;
this.waterCrucibleRecipeCache = null;
this.hammerRecipeCache = null;
this.compressedHammerRecipeCache = null;
this.sieveRecipeCache = null;
this.compressedSieveRecipeCache = null;
this.barrelFluidMixingRecipeCache = null;
this.fluidTransformationRecipeCache = null;
this.crookRecipeCache = null;
this.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();
}
}

View File

@ -265,7 +265,7 @@
"item.exdeorum.iridium_ore_chunk": "イリジウム鉱石の塊",
"item.exdeorum.iron_hammer": "鉄のハンマー",
"item.exdeorum.iron_mesh": "鉄のメッシュ",
"item.exdeorum.iron_ore_chunk": "鉄鉱石の塊",
"item.exdeorum.iron_ore_chunk": "鉄鉱石の塊",
"item.exdeorum.iron_watering_can": "鉄のじょうろ",
"item.exdeorum.lead_ore_chunk": "鉛鉱石の塊",
"item.exdeorum.lithium_ore_chunk": "リチウム鉱石の塊",

View File

@ -1,36 +1,64 @@
{
"advancements.exdeorum.core.barrel.description": "用木桶把有机质堆肥成泥土。",
"advancements.exdeorum.core.barrel.title": "放入有机质",
"advancements.exdeorum.core.crook.description": "制作一个钩子,使树苗从树叶上掉落",
"advancements.exdeorum.core.barrel.description": "将有机物放入木桶使其堆肥为泥土",
"advancements.exdeorum.core.barrel.title": "那个里面该放湿垃圾",
"advancements.exdeorum.core.crook.description": "制作一个钩子,使树苗从树叶上掉落的概率加倍",
"advancements.exdeorum.core.crook.title": "给他钩",
"advancements.exdeorum.core.root.description": "出生在空岛",
"advancements.exdeorum.core.root.title": "不要向下看...",
"advancements.exdeorum.core.silk_worm.description": "获得一条蚕,用它感染一棵树来获得线",
"advancements.exdeorum.core.silk_worm.description": "获得一条蚕,用它感染一棵树来获得线",
"advancements.exdeorum.core.silk_worm.title": "这个看起来可以吃",
"advancements.exdeorum.core.string_mesh.description": "制作在筛子中使用的筛网",
"advancements.exdeorum.core.string_mesh.title": "所有这些小孔",
"advancements.exdeorum.core.string_mesh.title": "所有小孔",
"block.exdeorum.acacia_barrel": "金合欢木桶",
"block.exdeorum.acacia_compressed_sieve": "重型金合欢木筛子",
"block.exdeorum.acacia_crucible": "金合欢木坩埚",
"block.exdeorum.acacia_sieve": "金合欢木筛子",
"block.exdeorum.archwood_barrel": "至高木桶",
"block.exdeorum.archwood_sieve": "至高木筛子",
"block.exdeorum.bamboo_barrel": "竹桶",
"block.exdeorum.bamboo_compressed_sieve": "重型竹筛子",
"block.exdeorum.bamboo_crucible": "竹坩埚",
"block.exdeorum.bamboo_sieve": "竹筛子",
"block.exdeorum.birch_barrel": "白桦木桶",
"block.exdeorum.birch_compressed_sieve": "重型白桦木筛子",
"block.exdeorum.birch_crucible": "白桦木坩埚",
"block.exdeorum.birch_sieve": "白桦木筛子",
"block.exdeorum.blue_archwood_crucible": "至高木坩埚",
"block.exdeorum.bluebright_barrel": "蓝光木桶",
"block.exdeorum.bluebright_crucible": "蓝光木坩埚",
"block.exdeorum.bluebright_sieve": "蓝光木筛子",
"block.exdeorum.cherry_barrel": "樱桃木桶",
"block.exdeorum.cherry_crucible": "樱桃木坩埚",
"block.exdeorum.cherry_sieve": "樱桃木筛子",
"block.exdeorum.comet_barrel": "异星木桶",
"block.exdeorum.comet_crucible": "异星木坩埚",
"block.exdeorum.comet_sieve": "异星木筛子",
"block.exdeorum.blue_archwood_compressed_sieve": "奔流至高木重型筛子",
"block.exdeorum.blue_archwood_crucible": "奔流至高木坩埚",
"block.exdeorum.bluebright_barrel": "蓝辉木桶",
"block.exdeorum.bluebright_compressed_sieve": "蓝辉重型筛子",
"block.exdeorum.bluebright_crucible": "蓝辉坩埚",
"block.exdeorum.bluebright_sieve": "蓝辉筛子",
"block.exdeorum.cherry_barrel": "樱花木桶",
"block.exdeorum.cherry_compressed_sieve": "樱花重型筛子",
"block.exdeorum.cherry_crucible": "樱花坩埚",
"block.exdeorum.cherry_sieve": "樱花筛子",
"block.exdeorum.comet_barrel": "彗星木桶",
"block.exdeorum.comet_compressed_sieve": "彗星重型筛子",
"block.exdeorum.comet_crucible": "彗星坩埚",
"block.exdeorum.comet_sieve": "彗星筛子",
"block.exdeorum.compressed_andesite": "压缩安山岩",
"block.exdeorum.compressed_blackstone": "压缩黑石",
"block.exdeorum.compressed_cobbled_deepslate": "压缩深板岩圆石",
"block.exdeorum.compressed_cobblestone": "压缩圆石",
"block.exdeorum.compressed_crushed_blackstone": "压缩粉碎黑石",
"block.exdeorum.compressed_crushed_deepslate": "压缩粉碎深板岩",
"block.exdeorum.compressed_crushed_end_stone": "压缩粉碎末地石",
"block.exdeorum.compressed_crushed_netherrack": "压缩粉碎下界岩",
"block.exdeorum.compressed_deepslate": "压缩深板岩",
"block.exdeorum.compressed_diorite": "压缩闪长岩",
"block.exdeorum.compressed_dirt": "压缩泥土",
"block.exdeorum.compressed_dust": "压缩尘土",
"block.exdeorum.compressed_end_stone": "压缩末地石",
"block.exdeorum.compressed_granite": "压缩花岗岩",
"block.exdeorum.compressed_gravel": "压缩砂砾",
"block.exdeorum.compressed_moss_block": "压缩苔藓块",
"block.exdeorum.compressed_netherrack": "压缩下界岩",
"block.exdeorum.compressed_red_sand": "压缩红沙",
"block.exdeorum.compressed_sand": "压缩沙子",
"block.exdeorum.compressed_soul_sand": "压缩灵魂沙",
"block.exdeorum.crimson_barrel": "绯红木桶",
"block.exdeorum.crimson_compressed_sieve": "绯红木重型筛子",
"block.exdeorum.crimson_crucible": "绯红木坩埚",
"block.exdeorum.crimson_sieve": "绯红木筛子",
"block.exdeorum.crushed_blackstone": "粉碎黑石",
@ -38,134 +66,187 @@
"block.exdeorum.crushed_end_stone": "粉碎末地石",
"block.exdeorum.crushed_netherrack": "粉碎下界岩",
"block.exdeorum.crystallized_barrel": "结晶木桶",
"block.exdeorum.crystallized_compressed_sieve": "结晶木重型筛子",
"block.exdeorum.crystallized_crucible": "结晶木坩埚",
"block.exdeorum.crystallized_sieve": "结晶木筛子",
"block.exdeorum.dark_oak_barrel": "深色橡木桶",
"block.exdeorum.dark_oak_compressed_sieve": "深色橡木重型筛子",
"block.exdeorum.dark_oak_crucible": "深色橡木坩埚",
"block.exdeorum.dark_oak_sieve": "深色橡木筛子",
"block.exdeorum.dead_barrel": "枯木桶",
"block.exdeorum.dead_crucible": "枯木坩埚",
"block.exdeorum.dead_sieve": "枯木筛子",
"block.exdeorum.dead_barrel": "垂死木桶",
"block.exdeorum.dead_compressed_sieve": "垂死木重型筛子",
"block.exdeorum.dead_crucible": "垂死木坩埚",
"block.exdeorum.dead_sieve": "垂死木筛子",
"block.exdeorum.dusk_barrel": "暮光木桶",
"block.exdeorum.dusk_compressed_sieve": "暮光木重型筛子",
"block.exdeorum.dusk_crucible": "暮光木坩埚",
"block.exdeorum.dusk_sieve": "暮光木筛子",
"block.exdeorum.dust": "尘土",
"block.exdeorum.end_cake": "末地蛋糕",
"block.exdeorum.fir_barrel": "冷杉木桶",
"block.exdeorum.fir_compressed_sieve": "冷杉木重型筛子",
"block.exdeorum.fir_crucible": "冷杉木坩埚",
"block.exdeorum.fir_sieve": "冷杉木筛子",
"block.exdeorum.frostbright_barrel": "霜耀原木桶",
"block.exdeorum.frostbright_compressed_sieve": "霜耀原木重型筛子",
"block.exdeorum.frostbright_crucible": "霜耀原木坩埚",
"block.exdeorum.frostbright_sieve": "霜耀原木筛子",
"block.exdeorum.golden_oak_crucible": "金色橡木坩埚",
"block.exdeorum.green_archwood_crucible": "绿色至高木坩埚",
"block.exdeorum.golden_oak_compressed_sieve": "金琥珀木重型筛子",
"block.exdeorum.golden_oak_crucible": "金琥珀木坩埚",
"block.exdeorum.green_archwood_compressed_sieve": "繁茂至高木重型筛子",
"block.exdeorum.green_archwood_crucible": "繁茂至高木坩埚",
"block.exdeorum.hellbark_barrel": "地狱皮木桶",
"block.exdeorum.hellbark_compressed_sieve": "地狱皮木重型筛子",
"block.exdeorum.hellbark_crucible": "地狱皮木坩埚",
"block.exdeorum.hellbark_sieve": "地狱皮木筛子",
"block.exdeorum.infested_leaves": "被感染的树叶",
"block.exdeorum.infested_leaves.fully_infested": "完全感染",
"block.exdeorum.jacaranda_barrel": "蓝花楹木桶",
"block.exdeorum.jacaranda_compressed_sieve": "蓝花楹木重型筛子",
"block.exdeorum.jacaranda_crucible": "蓝花楹木坩埚",
"block.exdeorum.jacaranda_sieve": "蓝花楹木筛子",
"block.exdeorum.jungle_barrel": "丛林木桶",
"block.exdeorum.jungle_compressed_sieve": "丛林木重型筛子",
"block.exdeorum.jungle_crucible": "丛林木坩埚",
"block.exdeorum.jungle_sieve": "丛林木筛子",
"block.exdeorum.lunar_barrel": "月球木桶",
"block.exdeorum.lunar_barrel": "月球木木桶",
"block.exdeorum.lunar_compressed_sieve": "月球木重型筛子",
"block.exdeorum.lunar_crucible": "月球木坩埚",
"block.exdeorum.lunar_sieve": "月球木筛子",
"block.exdeorum.magic_barrel": "魔法木桶",
"block.exdeorum.magic_compressed_sieve": "魔法木重型筛子",
"block.exdeorum.magic_crucible": "魔法木坩埚",
"block.exdeorum.magic_sieve": "魔法木筛子",
"block.exdeorum.mahogany_barrel": "红木木桶",
"block.exdeorum.mahogany_crucible": "红木坩埚",
"block.exdeorum.mahogany_sieve": "红木筛子",
"block.exdeorum.mahogany_barrel": "桃花心木桶",
"block.exdeorum.mahogany_compressed_sieve": "桃花心木重型筛子",
"block.exdeorum.mahogany_crucible": "桃花心木坩埚",
"block.exdeorum.mahogany_sieve": "桃花心木筛子",
"block.exdeorum.mangrove_barrel": "红树木桶",
"block.exdeorum.mangrove_compressed_sieve": "红树木重型筛子",
"block.exdeorum.mangrove_crucible": "红树木坩埚",
"block.exdeorum.mangrove_sieve": "红树木筛子",
"block.exdeorum.maple_barrel": "枫木桶",
"block.exdeorum.maple_compressed_sieve": "枫木重型筛子",
"block.exdeorum.maple_crucible": "枫木坩埚",
"block.exdeorum.maple_sieve": "枫木筛子",
"block.exdeorum.mechanical_hammer": "机械锤",
"block.exdeorum.mechanical_sieve": "机械筛子",
"block.exdeorum.oak_barrel": "橡木桶",
"block.exdeorum.mechanical_sieve": "机械筛",
"block.exdeorum.oak_barrel": "橡木木桶",
"block.exdeorum.oak_compressed_sieve": "橡木重型筛子",
"block.exdeorum.oak_crucible": "橡木坩埚",
"block.exdeorum.oak_sieve": "橡木筛子",
"block.exdeorum.palm_barrel": "棕榈木桶",
"block.exdeorum.palm_compressed_sieve": "棕榈木重型筛子",
"block.exdeorum.palm_crucible": "棕榈木坩埚",
"block.exdeorum.palm_sieve": "棕榈木筛子",
"block.exdeorum.porcelain_crucible": "陶瓷坩埚",
"block.exdeorum.purple_archwood_crucible": "紫色至高木坩埚",
"block.exdeorum.red_archwood_crucible": "红色至高木坩埚",
"block.exdeorum.purple_archwood_compressed_sieve": "重型恼人至高木重型筛子",
"block.exdeorum.purple_archwood_crucible": "重型恼人至高木坩埚",
"block.exdeorum.red_archwood_compressed_sieve": "重型烈焰至高木重型筛子",
"block.exdeorum.red_archwood_crucible": "重型烈焰至高木坩埚",
"block.exdeorum.redwood_barrel": "红木桶",
"block.exdeorum.redwood_compressed_sieve": "红木重型筛子",
"block.exdeorum.redwood_crucible": "红木坩埚",
"block.exdeorum.redwood_sieve": "红木筛子",
"block.exdeorum.skyroot_barrel": "天根木桶",
"block.exdeorum.skyroot_compressed_sieve": "天根木重型筛子",
"block.exdeorum.skyroot_crucible": "天根木坩埚",
"block.exdeorum.skyroot_sieve": "天根木筛子",
"block.exdeorum.spruce_barrel": "云杉木桶",
"block.exdeorum.spruce_compressed_sieve": "云杉木重型筛子",
"block.exdeorum.spruce_crucible": "云杉木坩埚",
"block.exdeorum.spruce_sieve": "云杉木筛子",
"block.exdeorum.starlit_barrel": "流萤木桶",
"block.exdeorum.starlit_compressed_sieve": "流萤木重型筛子",
"block.exdeorum.starlit_crucible": "流萤木坩埚",
"block.exdeorum.starlit_sieve": "流萤木筛子",
"block.exdeorum.stone_barrel": "石桶",
"block.exdeorum.umbran_barrel": "暗影木桶",
"block.exdeorum.umbran_compressed_sieve": "暗影木重型筛子",
"block.exdeorum.umbran_crucible": "暗影木坩埚",
"block.exdeorum.umbran_sieve": "暗影木筛子",
"block.exdeorum.unfired_porcelain_crucible": "未烧制的陶瓷坩埚",
"block.exdeorum.warped_barrel": "诡异木桶",
"block.exdeorum.warped_compressed_sieve": "诡异木重型筛子",
"block.exdeorum.warped_crucible": "诡异木坩埚",
"block.exdeorum.warped_sieve": "诡异木筛子",
"block.exdeorum.willow_barrel": "柳木桶",
"block.exdeorum.willow_compressed_sieve": "柳木重型筛子",
"block.exdeorum.willow_crucible": "柳木坩埚",
"block.exdeorum.willow_sieve": "柳木筛子",
"block.exdeorum.witch_water": "巫水",
"config.jade.plugin_exdeorum.barrel": "桶",
"config.jade.plugin_exdeorum.crucible": "坩埚",
"config.jade.plugin_exdeorum.infested_leaves": "被感染的树叶",
"config.jade.plugin_exdeorum.sieve": "筛子",
"emi.category.exdeorum.barrel_compost": "堆肥",
"emi.category.exdeorum.barrel_fluid_mixing": "固液混合",
"emi.category.exdeorum.barrel_mixing": "混合",
"emi.category.exdeorum.compressed_hammer": "压缩锤",
"emi.category.exdeorum.compressed_sieve": "重型筛子",
"emi.category.exdeorum.crook": "钩子",
"emi.category.exdeorum.crucible_heat_sources": "坩埚的热源",
"emi.category.exdeorum.hammer": "锤子",
"emi.category.exdeorum.lava_crucible": "坩埚熔岩的生成",
"emi.category.exdeorum.sieve": "筛子",
"emi.category.exdeorum.water_crucible": "坩埚水的生成",
"exdeorum.container.mechanical_hammer": "机械锤",
"exdeorum.container.mechanical_sieve": "机械筛子",
"exdeorum.container.mechanical_sieve": "机械筛",
"fluid_type.exdeorum.witch_water": "巫水",
"generator.exdeorum.void_world": "无中生有空岛",
"gui.exdeorum.category.barrel_compost": "桶:堆肥",
"gui.exdeorum.category.barrel_compost.volume": "堆肥:%s",
"gui.exdeorum.category.barrel_fluid_mixing": "桶:流体混合",
"generator.exdeorum.void_world": "虚空世界",
"gui.exdeorum.category.barrel_compost": "堆肥",
"gui.exdeorum.category.barrel_compost.volume": "堆肥: %s",
"gui.exdeorum.category.barrel_fluid_mixing": "固液混合",
"gui.exdeorum.category.barrel_fluid_mixing.contents_are_consumed": "消耗",
"gui.exdeorum.category.barrel_mixing": "桶:混合",
"gui.exdeorum.category.crucible_heat_source": "坩埚热源",
"gui.exdeorum.category.crucible_heat_source.multiplier": "熔融速率:%sX",
"gui.exdeorum.category.hammer": "锤",
"gui.exdeorum.category.lava_crucible": "坩埚",
"gui.exdeorum.category.barrel_mixing": "混合",
"gui.exdeorum.category.compressed_hammer": "压缩锤",
"gui.exdeorum.category.compressed_sieve": "重型筛子",
"gui.exdeorum.category.crook": "钩子",
"gui.exdeorum.category.crook.requires_state": "必要属性:",
"gui.exdeorum.category.crucible_heat_source": "坩埚的热源",
"gui.exdeorum.category.crucible_heat_source.multiplier": "速度: %s倍",
"gui.exdeorum.category.hammer": "锤子",
"gui.exdeorum.category.lava_crucible": "坩埚熔岩的生成",
"gui.exdeorum.category.sieve": "筛子",
"gui.exdeorum.category.sieve.average_output": "平均输出:%s",
"gui.exdeorum.category.sieve.by_hand_only": "不能由机械筛子筛出",
"gui.exdeorum.category.sieve.chance": "几率:%s%%",
"gui.exdeorum.category.sieve.max_output": "最大:%s",
"gui.exdeorum.category.sieve.min_output": "最小:%s",
"gui.exdeorum.category.water_crucible": "坩埚水",
"gui.exdeorum.category.sieve.average_output": "平均输出: %s",
"gui.exdeorum.category.sieve.by_hand_only": "不会从机械筛上掉落物品",
"gui.exdeorum.category.sieve.chance": "几率: %s%%",
"gui.exdeorum.category.sieve.max_output": "最大: %s",
"gui.exdeorum.category.sieve.min_output": "最小: %s",
"gui.exdeorum.category.water_crucible": "坩埚的生成",
"gui.exdeorum.energy_label": "能量",
"gui.exdeorum.redstone_control.always": "总是",
"gui.exdeorum.redstone_control.label": "标签",
"gui.exdeorum.redstone_control.mode": "模式:",
"gui.exdeorum.redstone_control.powered": "充能的",
"gui.exdeorum.redstone_control.unpowered": "未充能的",
"info.exdeorum.crimson_nylium_spores": "在下界岩上右键使用,将其转化为绯红菌岩。",
"info.exdeorum.grass_seeds": "在泥土上右键使用,将其转化为一个草方块。",
"info.exdeorum.mechanical_hammer": "机械锤是一种自动锤需要提供FE能量。它可以在没有锤子的情况下运行但添加任何锤子都会使速度加倍锤子上的效率附魔会进一步提高速度。它支持三种不同模式的红石控制。无中生有神赐不提供产生FE能量的方法你需要另一个模组来供电。",
"info.exdeorum.mechanical_sieve": "机械筛子是一种自动筛子需要提供FE能量。它支持三种不同模式的红石控制。无中生有神赐不提供产生FE能量的方法你需要另一个模组来供电。",
"info.exdeorum.mycelium_spores": "在泥土上右键使用,将其转化为一个菌丝体。在牛牛上右键使用,它会发生奇妙的变化!",
"info.exdeorum.sculk_core": "在幽匿尖啸体上使用幽匿核心,使其能够产生监守者。正常情况下,玩家放置的幽匿尖啸体不能生成监守者,因此该物品对于在空岛世界中获取幽匿物品非常有用。",
"info.exdeorum.sieve": "筛子用于从砂砾和泥土等方块中筛子选物品。使用筛子需要筛网。筛网可以被附魔时运和效率。5×5区域内的筛子可同时使用。",
"info.exdeorum.sieve_mesh": "筛子使用筛网。不同的筛网产生不同的掉落物。筛网可以被附魔时运和效率,以增加掉落物几率和筛子速度。",
"info.exdeorum.silk_worm": "用钩子破坏树叶有1%%的几率掉落蚕。在树叶上放一个蚕会感染树叶,并逐渐蔓延到整棵树。破坏完全受感染的树叶会掉落线,不会掉落树苗。",
"info.exdeorum.warped_nylium_spores": "在下界岩上右键使用,将其转化为诡异菌岩。",
"info.exdeorum.watering_can": "洒水壶可以加速作物、树木生长和草的蔓延等。它可以从木桶和木坩埚中装水。金等级以上的洒水壶一旦装满就不需要再次装水。钻石洒水壶在3x3区域内浇水下界合金洒水壶可通过机械使用。",
"info.exdeorum.witch_water": "在菌丝体上方的桶中放入水,水将会转化为巫水。更多菌丝体能够加速这一过程。装有巫水的木桶会使附近的菌丝体上长出蘑菇。水和熔岩可以制造刷下界岩机,类似于刷石机。",
"gui.exdeorum.redstone_control.always": "忽略",
"gui.exdeorum.redstone_control.label": "红石模式",
"gui.exdeorum.redstone_control.mode": "模式: ",
"gui.exdeorum.redstone_control.powered": "",
"gui.exdeorum.redstone_control.unpowered": "",
"info.exdeorum.crimson_nylium_spores": "对下界岩使用可将其转变为绯红菌岩对下界岩使用可将其转变为绯红菌岩",
"info.exdeorum.grass_seeds": "对泥土使用可将其转变为草方块",
"info.exdeorum.mechanical_hammer": "机械锤是一台机器,当提供FE能量时,可以自动粉碎方块,无需玩家手动操作 它无需锤子即可工作,但放入任意锤子会使其速度翻倍,且锤子上的效率附魔会进一步提升速度 它支持三种不同的红石控制模式 由于 Ex Deorum 不提供产生 FE 的方式,您需要其他模组来提供能源",
"info.exdeorum.mechanical_sieve": "机械筛是一台机器,当提供筛网和FE能量时,可以自动筛方块,无需玩家手动操作 它支持三种不同的红石控制模式 由于 Ex Deorum 不提供产生 FE 的方式,您需要其他模组来提供能源",
"info.exdeorum.mycelium_spores": "对泥土使用可将其转变为菌丝体 对牛使用可将其转变为哞菇",
"info.exdeorum.sculk_core": "对玩家放置的幽匿尖哮体使用幽匿核心可使其能够变成可生成监守者的幽匿尖哮体 通常由玩家放置的潜唤尖体无法生成监守者,因此此物品在空岛世界中对于获取幽匿类物品非常有用",
"info.exdeorum.sieve": "筛子用于从沙砾、泥土等松散方块中筛取物品 使用筛子需要筛网 筛网可以附魔时运和效率 5x5区域内的筛子可以同时使用",
"info.exdeorum.sieve_mesh": "筛网用于筛子中 不同的筛网会产生不同的掉落物 筛网可以附魔时运(增加掉落几率)和效率(增加筛分速度)。",
"info.exdeorum.silk_worm": "蚕有1%的几率从用钩子破坏树叶中掉落 对树叶使用蚕会感染它们,并逐渐传播至整棵树 100%被感染的树叶可以收获获得线,但不会掉落树苗",
"info.exdeorum.warped_nylium_spores": "对下界岩使用可将其转变为诡异菌岩",
"info.exdeorum.watering_can": "浇洒水壶可以加速作物、树木生长和草方块蔓延等 它们可以从木桶和木坩埚中装水 金质及更高级的浇水壶一旦装满则无需再次补充 钻石浇水壶的作用范围为3x3区域,下界合金浇水壶可被机器使用",
"info.exdeorum.witch_water": "巫水可通过将水放入位于菌丝体上的木桶中获得 更多的菌丝体会加速此过程 装有巫水的木桶会在附近的菌丝体上生成蘑菇 巫水和熔岩可以制造类似刷石机的刷下界岩机",
"item.exdeorum.aluminum_ore_chunk": "铝矿物碎块",
"item.exdeorum.andesite_pebble": "安山岩石子",
"item.exdeorum.basalt_pebble": "玄武岩石子",
"item.exdeorum.blackstone_pebble": "黑石石子",
"item.exdeorum.bone_crook": "骨钩",
"item.exdeorum.boron_ore_chunk": "硼矿物块",
"item.exdeorum.boron_ore_chunk": "硼矿物块",
"item.exdeorum.calcite_pebble": "方解石石子",
"item.exdeorum.cobalt_ore_chunk": "钴矿物碎块",
"item.exdeorum.cooked_silk_worm": "煮熟的蚕",
"item.exdeorum.compressed_diamond_hammer": "压缩钻石锤",
"item.exdeorum.compressed_golden_hammer": "压缩金锤",
"item.exdeorum.compressed_iron_hammer": "压缩铁锤",
"item.exdeorum.compressed_netherite_hammer": "压缩下界合金锤",
"item.exdeorum.compressed_stone_hammer": "压缩石锤",
"item.exdeorum.compressed_wooden_hammer": "压缩木锤",
"item.exdeorum.cooked_silkworm": "煮熟的蚕",
"item.exdeorum.copper_ore_chunk": "铜矿物碎块",
"item.exdeorum.crimson_nylium_spores": "绯红菌孢子",
"item.exdeorum.crook": "木钩",
@ -189,8 +270,8 @@
"item.exdeorum.lead_ore_chunk": "铅矿物碎块",
"item.exdeorum.lithium_ore_chunk": "锂矿物碎块",
"item.exdeorum.magnesium_ore_chunk": "镁矿物碎块",
"item.exdeorum.mechanical_hammer.hammer_label": "锤:",
"item.exdeorum.mechanical_sieve.mesh_label": "筛网:",
"item.exdeorum.mechanical_hammer.hammer_label": "锤: ",
"item.exdeorum.mechanical_sieve.mesh_label": "筛网: ",
"item.exdeorum.mycelium_spores": "菌丝孢子",
"item.exdeorum.netherite_hammer": "下界合金锤",
"item.exdeorum.netherite_mesh": "下界合金筛网",
@ -205,9 +286,10 @@
"item.exdeorum.porcelain_water_bucket": "水瓷桶",
"item.exdeorum.porcelain_witch_water_bucket": "巫水瓷桶",
"item.exdeorum.random_armor_trim": "随机锻造模板",
"item.exdeorum.random_armor_trim.no_upgrade": "随机锻造模板(无下界合金升级模板)",
"item.exdeorum.random_pottery_sherd": "随机纹样陶片",
"item.exdeorum.sculk_core": "幽匿核心",
"item.exdeorum.silk_worm": "蚕",
"item.exdeorum.silkworm": "蚕",
"item.exdeorum.silver_ore_chunk": "银矿物碎块",
"item.exdeorum.stone_hammer": "石锤",
"item.exdeorum.stone_pebble": "石子",
@ -222,8 +304,49 @@
"item.exdeorum.watering_can_fluid_display": ":%s / %s",
"item.exdeorum.witch_water_bucket": "巫水桶",
"item.exdeorum.wood_chippings": "木屑",
"item.exdeorum.wooden_hammer": "木",
"item.exdeorum.wooden_hammer": "木",
"item.exdeorum.wooden_watering_can": "木洒水壶",
"item.exdeorum.zinc_ore_chunk": "锌矿物碎块",
"itemGroup.exdeorum.main": "无中生有:神赐"
}
"itemGroup.exdeorum.main": "无中生有:神赐",
"subtitles.exdeorum.barrel.add_compost": "添加堆肥",
"subtitles.exdeorum.barrel.compost_finish": "堆肥完成",
"subtitles.exdeorum.barrel.fluid_transform": "机械锤",
"subtitles.exdeorum.barrel.mixing_finish": "混合完成",
"subtitles.exdeorum.grass_seeds.place": "机械锤",
"subtitles.exdeorum.sculk_core.activate": "机械锤",
"subtitles.exdeorum.silk_worm.drop": "机械锤",
"subtitles.exdeorum.silk_worm.eat": "机械锤",
"subtitles.exdeorum.silk_worm.infest": "机械锤",
"subtitles.exdeorum.watering_can.stop": "机械锤",
"subtitles.exdeorum.watering_can.use": "机械锤",
"tag.item.exdeorum.barrels": "桶",
"tag.item.exdeorum.compressed.andesite": "压缩安山岩",
"tag.item.exdeorum.compressed.blackstone": "压缩黑石",
"tag.item.exdeorum.compressed.cobbled_deepslate": "压缩深板岩圆石",
"tag.item.exdeorum.compressed.cobblestone": "压缩圆石",
"tag.item.exdeorum.compressed.crushed_blackstone": "压缩粉碎黑石",
"tag.item.exdeorum.compressed.crushed_deepslate": "压缩粉碎深板岩",
"tag.item.exdeorum.compressed.crushed_end_stone": "压缩粉碎末地石",
"tag.item.exdeorum.compressed.crushed_netherrack": "压缩粉碎下界岩",
"tag.item.exdeorum.compressed.deepslate": "压缩深板岩",
"tag.item.exdeorum.compressed.diorite": "压缩闪长岩",
"tag.item.exdeorum.compressed.dirt": "压缩泥土",
"tag.item.exdeorum.compressed.dust": "压缩尘土",
"tag.item.exdeorum.compressed.end_stone": "压缩末地石",
"tag.item.exdeorum.compressed.granite": "压缩花岗岩",
"tag.item.exdeorum.compressed.gravel": "压缩砂砾",
"tag.item.exdeorum.compressed.moss_block": "压缩苔藓块",
"tag.item.exdeorum.compressed.netherrack": "压缩下界岩",
"tag.item.exdeorum.compressed.red_sand": "压缩红沙",
"tag.item.exdeorum.compressed.sand": "压缩沙子",
"tag.item.exdeorum.compressed.sands": "压缩沙子",
"tag.item.exdeorum.compressed.soul_sand": "压缩灵魂沙",
"tag.item.exdeorum.compressed_hammers": "压缩锤",
"tag.item.exdeorum.crooks": "钩子",
"tag.item.exdeorum.end_cake_materials": "末地蛋糕材料",
"tag.item.exdeorum.hammers": "锤子",
"tag.item.exdeorum.pebbles": "石子",
"tag.item.exdeorum.sieve_meshes": "筛网",
"tag.item.exdeorum.stone_barrels": "石桶",
"tag.item.exdeorum.wooden_barrels": "木桶"
}