diff options
Diffstat (limited to 'src/main/java/lv/enes/mc')
| -rw-r--r-- | src/main/java/lv/enes/mc/eris_alchemy/Emc.java | 171 | ||||
| -rw-r--r-- | src/main/java/lv/enes/mc/eris_alchemy/EmcLoader.java | 15 | ||||
| -rw-r--r-- | src/main/java/lv/enes/mc/eris_alchemy/ErisAlchemy.java | 6 | ||||
| -rw-r--r-- | src/main/java/lv/enes/mc/eris_alchemy/recipe/BannedRecipe.java | 40 | ||||
| -rw-r--r-- | src/main/java/lv/enes/mc/eris_alchemy/recipe/SimplifiedRecipe.java (renamed from src/main/java/lv/enes/mc/eris_alchemy/SimplifiedRecipe.java) | 72 | ||||
| -rw-r--r-- | src/main/java/lv/enes/mc/eris_alchemy/utils/ConsList.java | 103 | ||||
| -rw-r--r-- | src/main/java/lv/enes/mc/eris_alchemy/utils/IngredientProvider.java | 44 | ||||
| -rw-r--r-- | src/main/java/lv/enes/mc/eris_alchemy/utils/ItemUtils.java | 5 | ||||
| -rw-r--r-- | src/main/java/lv/enes/mc/eris_alchemy/utils/OptionalDoubleSummer.java | 47 | ||||
| -rw-r--r-- | src/main/java/lv/enes/mc/eris_alchemy/utils/Ref.java | 14 |
10 files changed, 425 insertions, 92 deletions
diff --git a/src/main/java/lv/enes/mc/eris_alchemy/Emc.java b/src/main/java/lv/enes/mc/eris_alchemy/Emc.java index 2e7e2d7..d058b94 100644 --- a/src/main/java/lv/enes/mc/eris_alchemy/Emc.java +++ b/src/main/java/lv/enes/mc/eris_alchemy/Emc.java | |||
| @@ -2,9 +2,9 @@ package lv.enes.mc.eris_alchemy; | |||
| 2 | 2 | ||
| 3 | import jakarta.annotation.Nullable; | 3 | import jakarta.annotation.Nullable; |
| 4 | import lv.enes.mc.eris_alchemy.ErisAlchemyRegistry.NetworkingConstants; | 4 | import lv.enes.mc.eris_alchemy.ErisAlchemyRegistry.NetworkingConstants; |
| 5 | import lv.enes.mc.eris_alchemy.utils.BufUtils; | 5 | import lv.enes.mc.eris_alchemy.recipe.BannedRecipe; |
| 6 | import lv.enes.mc.eris_alchemy.utils.ItemUtils; | 6 | import lv.enes.mc.eris_alchemy.recipe.SimplifiedRecipe; |
| 7 | import lv.enes.mc.eris_alchemy.utils.PlayerUtils; | 7 | import lv.enes.mc.eris_alchemy.utils.*; |
| 8 | import net.minecraft.client.Minecraft; | 8 | import net.minecraft.client.Minecraft; |
| 9 | import net.minecraft.core.registries.BuiltInRegistries; | 9 | import net.minecraft.core.registries.BuiltInRegistries; |
| 10 | import net.minecraft.core.registries.Registries; | 10 | import net.minecraft.core.registries.Registries; |
| @@ -27,6 +27,7 @@ import org.quiltmc.qsl.networking.api.client.ClientPlayNetworking; | |||
| 27 | 27 | ||
| 28 | import java.text.DecimalFormat; | 28 | import java.text.DecimalFormat; |
| 29 | import java.util.*; | 29 | import java.util.*; |
| 30 | import java.util.function.Supplier; | ||
| 30 | import java.util.stream.Stream; | 31 | import java.util.stream.Stream; |
| 31 | 32 | ||
| 32 | public final class Emc { | 33 | public final class Emc { |
| @@ -36,6 +37,7 @@ public final class Emc { | |||
| 36 | private static final Map<TagKey<Item>, OptionalDouble> ITEM_TAG_VALUES = new HashMap<>(); | 37 | private static final Map<TagKey<Item>, OptionalDouble> ITEM_TAG_VALUES = new HashMap<>(); |
| 37 | private static final Map<TagKey<Block>, OptionalDouble> BLOCK_TAG_VALUES = new HashMap<>(); | 38 | private static final Map<TagKey<Block>, OptionalDouble> BLOCK_TAG_VALUES = new HashMap<>(); |
| 38 | private static final List<SimplifiedRecipe> FAKE_RECIPES = new ArrayList<>(); | 39 | private static final List<SimplifiedRecipe> FAKE_RECIPES = new ArrayList<>(); |
| 40 | private static final List<BannedRecipe> BANNED_RECIPES = new ArrayList<>(); | ||
| 39 | 41 | ||
| 40 | private static final Map<ResourceLocation, OptionalDouble> VALUES = Collections.synchronizedMap(new HashMap<>()); | 42 | private static final Map<ResourceLocation, OptionalDouble> VALUES = Collections.synchronizedMap(new HashMap<>()); |
| 41 | 43 | ||
| @@ -102,7 +104,8 @@ public final class Emc { | |||
| 102 | Map<ResourceLocation, OptionalDouble> itemValues, | 104 | Map<ResourceLocation, OptionalDouble> itemValues, |
| 103 | Map<ResourceLocation, OptionalDouble> itemTagValues, | 105 | Map<ResourceLocation, OptionalDouble> itemTagValues, |
| 104 | Map<ResourceLocation, OptionalDouble> blockTagValues, | 106 | Map<ResourceLocation, OptionalDouble> blockTagValues, |
| 105 | List<SimplifiedRecipe> fakeRecipes | 107 | List<SimplifiedRecipe> fakeRecipes, |
| 108 | List<BannedRecipe> bannedRecipes | ||
| 106 | ) { | 109 | ) { |
| 107 | ITEM_VALUES.clear(); | 110 | ITEM_VALUES.clear(); |
| 108 | ITEM_VALUES.putAll(itemValues); | 111 | ITEM_VALUES.putAll(itemValues); |
| @@ -116,86 +119,69 @@ public final class Emc { | |||
| 116 | FAKE_RECIPES.clear(); | 119 | FAKE_RECIPES.clear(); |
| 117 | FAKE_RECIPES.addAll(fakeRecipes); | 120 | FAKE_RECIPES.addAll(fakeRecipes); |
| 118 | 121 | ||
| 122 | BANNED_RECIPES.clear(); | ||
| 123 | BANNED_RECIPES.addAll(bannedRecipes); | ||
| 124 | |||
| 119 | reinit(); | 125 | reinit(); |
| 120 | warnOfMissingValues(); | 126 | warnOfMissingValues(); |
| 121 | } | 127 | } |
| 122 | 128 | ||
| 123 | private static OptionalDouble calculateEmcForIngredient( | 129 | private static OptionalDouble calcEmc( |
| 124 | List<SimplifiedRecipe> recipes, | 130 | ResourceLocation item, |
| 125 | Set<ResourceLocation> configured, | 131 | Map<ResourceLocation, List<SimplifiedRecipe>> allRecipes |
| 126 | Ingredient ingredient | ||
| 127 | ) { | 132 | ) { |
| 133 | return allRecipes.getOrDefault(item, List.of()) | ||
| 134 | .stream() | ||
| 135 | .map(Emc::calcEmcForRecipe) | ||
| 136 | .flatMapToDouble(OptionalDouble::stream) | ||
| 137 | .average(); | ||
| 138 | } | ||
| 139 | |||
| 140 | private static OptionalDouble calcEmcForIngredient(Ingredient ingredient) { | ||
| 128 | return Arrays.stream(ingredient.getItems()) | 141 | return Arrays.stream(ingredient.getItems()) |
| 129 | .map(stack -> configEmc(recipes, configured, ItemUtils.getId(stack.getItem())).stream() | 142 | .map(Emc::get) |
| 130 | .map(x -> x * stack.getCount()) | 143 | .flatMapToDouble(OptionalDouble::stream) |
| 131 | .findFirst()) | ||
| 132 | .filter(OptionalDouble::isPresent) | ||
| 133 | .mapToDouble(OptionalDouble::getAsDouble) | ||
| 134 | .filter(x -> x > 0) | ||
| 135 | .average(); | 144 | .average(); |
| 136 | } | 145 | } |
| 137 | 146 | ||
| 138 | private static OptionalDouble calculateEmcForRecipe( | 147 | private static OptionalDouble calcEmcForRecipe(SimplifiedRecipe recipe) { |
| 139 | List<SimplifiedRecipe> recipes, | 148 | if (recipe.input().isEmpty()) { |
| 140 | Set<ResourceLocation> configured, | ||
| 141 | SimplifiedRecipe recipe | ||
| 142 | ) { | ||
| 143 | try { | ||
| 144 | if (recipe.input().isEmpty()) { | ||
| 145 | return OptionalDouble.empty(); | ||
| 146 | } | ||
| 147 | |||
| 148 | var inputEmc = recipe.input() | ||
| 149 | .stream() | ||
| 150 | .map(ingredient -> calculateEmcForIngredient(recipes, configured, ingredient)) | ||
| 151 | .mapToDouble(OptionalDouble::orElseThrow) | ||
| 152 | .sum(); | ||
| 153 | |||
| 154 | var remainderEmc = recipe.remainder() | ||
| 155 | .stream() | ||
| 156 | .map(remainder -> configEmc(recipes, configured, ItemUtils.getId(remainder))) | ||
| 157 | .mapToDouble(OptionalDouble::orElseThrow) | ||
| 158 | .sum(); | ||
| 159 | |||
| 160 | if (remainderEmc > inputEmc) { | ||
| 161 | ErisAlchemy.LOGGER.warn("Recipe generating {} creates too much EMC out of thin air!", recipe.output()); | ||
| 162 | return OptionalDouble.empty(); | ||
| 163 | } | ||
| 164 | |||
| 165 | var outputDivisor = (double) recipe.output().getCount(); | ||
| 166 | |||
| 167 | return OptionalDouble.of((inputEmc - remainderEmc) / outputDivisor); | ||
| 168 | } catch (NoSuchElementException e) { | ||
| 169 | return OptionalDouble.empty(); | 149 | return OptionalDouble.empty(); |
| 170 | } | 150 | } |
| 171 | } | ||
| 172 | 151 | ||
| 173 | private static OptionalDouble configEmc( | 152 | var inputEmcOpt = recipe.input() |
| 174 | List<SimplifiedRecipe> recipes, | 153 | .stream() |
| 175 | Set<ResourceLocation> configured, | 154 | .map(Supplier::get) |
| 176 | ResourceLocation itemId | 155 | .map(Emc::calcEmcForIngredient) |
| 177 | ) { | 156 | .collect(new OptionalDoubleSummer()); |
| 178 | var res = get(itemId); | 157 | |
| 179 | if (res.isPresent() || configured.contains(itemId)) { | 158 | if (inputEmcOpt.isEmpty()) { |
| 180 | return res; | 159 | return OptionalDouble.empty(); |
| 181 | } | 160 | } |
| 182 | 161 | ||
| 183 | configured.add(itemId); | 162 | var remainderEmcOpt = recipe.remainder() |
| 184 | var item = BuiltInRegistries.ITEM.get(itemId); | 163 | .stream() |
| 185 | res = recipes.stream() | 164 | .map(Emc::get) |
| 186 | .filter(recipe -> recipe.output().is(item)) | 165 | .collect(new OptionalDoubleSummer()); |
| 187 | .map(recipe -> calculateEmcForRecipe(recipes, configured, recipe)) | 166 | |
| 188 | .flatMapToDouble(OptionalDouble::stream) | 167 | if (remainderEmcOpt.isEmpty()) { |
| 189 | .average(); | 168 | return OptionalDouble.empty(); |
| 190 | res.ifPresentOrElse( | 169 | } |
| 191 | emc -> VALUES.put(itemId, OptionalDouble.of(emc)), | 170 | |
| 192 | () -> configured.remove(itemId) | 171 | var inputEmc = inputEmcOpt.getAsDouble(); |
| 193 | ); | 172 | var remainderEmc = remainderEmcOpt.getAsDouble(); |
| 194 | return res; | 173 | if (remainderEmc > inputEmc) { |
| 174 | ErisAlchemy.LOGGER.warn("Recipe generating {} creates too much EMC out of thin air!", recipe.output()); | ||
| 175 | return OptionalDouble.empty(); | ||
| 176 | } | ||
| 177 | |||
| 178 | var outputDivisor = (double) recipe.output().getCount(); | ||
| 179 | |||
| 180 | return OptionalDouble.of((inputEmc - remainderEmc) / outputDivisor); | ||
| 195 | } | 181 | } |
| 196 | 182 | ||
| 197 | private static List<SimplifiedRecipe> getRecipes(@Nullable Level world) { | 183 | private static Stream<SimplifiedRecipe> getRecipes(@Nullable Level world) { |
| 198 | Stream<SimplifiedRecipe> recipes = FAKE_RECIPES.stream(); | 184 | var recipes = FAKE_RECIPES.stream(); |
| 199 | 185 | ||
| 200 | if (world != null) { | 186 | if (world != null) { |
| 201 | recipes = Stream.concat( | 187 | recipes = Stream.concat( |
| @@ -207,7 +193,7 @@ public final class Emc { | |||
| 207 | ); | 193 | ); |
| 208 | } | 194 | } |
| 209 | 195 | ||
| 210 | return recipes.toList(); | 196 | return recipes; |
| 211 | } | 197 | } |
| 212 | 198 | ||
| 213 | private static void reinit() { | 199 | private static void reinit() { |
| @@ -225,14 +211,55 @@ public final class Emc { | |||
| 225 | ); | 211 | ); |
| 226 | 212 | ||
| 227 | ErisAlchemy.LOGGER.info("Calculating EMC values from recipes..."); | 213 | ErisAlchemy.LOGGER.info("Calculating EMC values from recipes..."); |
| 228 | var recipes = getRecipes(overworld); | 214 | var recipes = new HashMap<ResourceLocation, List<SimplifiedRecipe>>(); |
| 229 | var configured = new HashSet<>(VALUES.keySet()); | 215 | getRecipes(overworld) |
| 230 | BuiltInRegistries.ITEM.keySet().forEach(item -> configEmc(recipes, configured, item)); | 216 | .filter(recipe -> !recipe.hasDuplication()) |
| 217 | .filter(recipe -> recipe.isAllowed(BANNED_RECIPES)) | ||
| 218 | .forEach(recipe -> | ||
| 219 | recipes | ||
| 220 | .computeIfAbsent(ItemUtils.getId(recipe.output()), k -> new ArrayList<>()) | ||
| 221 | .add(recipe) | ||
| 222 | ); | ||
| 223 | var sortedItems = sorted(recipes); | ||
| 224 | sortedItems.stream() | ||
| 225 | .filter(id -> !VALUES.containsKey(id)) | ||
| 226 | .forEach(id -> VALUES.put(id, calcEmc(id, recipes))); | ||
| 231 | ErisAlchemy.LOGGER.info("Done calculating EMC values..."); | 227 | ErisAlchemy.LOGGER.info("Done calculating EMC values..."); |
| 232 | 228 | ||
| 233 | sync(); | 229 | sync(); |
| 234 | } | 230 | } |
| 235 | 231 | ||
| 232 | private static void sortDps( | ||
| 233 | Set<ResourceLocation> permSorted, | ||
| 234 | ConsList<ResourceLocation> tmpSorted, | ||
| 235 | ResourceLocation item, | ||
| 236 | Map<ResourceLocation, List<SimplifiedRecipe>> data | ||
| 237 | ) { | ||
| 238 | if (permSorted.contains(item)) { | ||
| 239 | return; | ||
| 240 | } | ||
| 241 | |||
| 242 | var newTmpSorted = ConsList.cons(item, tmpSorted); | ||
| 243 | |||
| 244 | if (tmpSorted.contains(item)) { | ||
| 245 | ErisAlchemy.LOGGER.warn("Cycle in recipes detected: {}, breaking here", newTmpSorted); | ||
| 246 | return; | ||
| 247 | } | ||
| 248 | |||
| 249 | data.getOrDefault(item, List.of()) | ||
| 250 | .stream() | ||
| 251 | .flatMap(SimplifiedRecipe::dependencies) | ||
| 252 | .distinct() | ||
| 253 | .forEach(dep -> sortDps(permSorted, newTmpSorted, dep, data)); | ||
| 254 | permSorted.add(item); | ||
| 255 | } | ||
| 256 | |||
| 257 | private static Set<ResourceLocation> sorted(Map<ResourceLocation, List<SimplifiedRecipe>> unsorted) { | ||
| 258 | var res = new LinkedHashSet<ResourceLocation>(); | ||
| 259 | unsorted.forEach((item, recipes) -> sortDps(res, ConsList.nil(), item, unsorted)); | ||
| 260 | return res; | ||
| 261 | } | ||
| 262 | |||
| 236 | private static void sync() { | 263 | private static void sync() { |
| 237 | syncTo(PlayerUtils.all()); | 264 | syncTo(PlayerUtils.all()); |
| 238 | } | 265 | } |
diff --git a/src/main/java/lv/enes/mc/eris_alchemy/EmcLoader.java b/src/main/java/lv/enes/mc/eris_alchemy/EmcLoader.java index dd4613f..373e561 100644 --- a/src/main/java/lv/enes/mc/eris_alchemy/EmcLoader.java +++ b/src/main/java/lv/enes/mc/eris_alchemy/EmcLoader.java | |||
| @@ -2,6 +2,8 @@ package lv.enes.mc.eris_alchemy; | |||
| 2 | 2 | ||
| 3 | import com.google.gson.reflect.TypeToken; | 3 | import com.google.gson.reflect.TypeToken; |
| 4 | import jakarta.annotation.Nonnull; | 4 | import jakarta.annotation.Nonnull; |
| 5 | import lv.enes.mc.eris_alchemy.recipe.BannedRecipe; | ||
| 6 | import lv.enes.mc.eris_alchemy.recipe.SimplifiedRecipe; | ||
| 5 | import net.minecraft.resources.ResourceLocation; | 7 | import net.minecraft.resources.ResourceLocation; |
| 6 | import net.minecraft.server.packs.resources.ResourceManager; | 8 | import net.minecraft.server.packs.resources.ResourceManager; |
| 7 | import org.quiltmc.qsl.resource.loader.api.reloader.SimpleSynchronousResourceReloader; | 9 | import org.quiltmc.qsl.resource.loader.api.reloader.SimpleSynchronousResourceReloader; |
| @@ -30,7 +32,9 @@ public class EmcLoader implements SimpleSynchronousResourceReloader { | |||
| 30 | 32 | ||
| 31 | var fakeRecipes = loadAllFiles(manager, "fake_recipes", new ArrayList<>(), EmcLoader::loadFakeRecipes); | 33 | var fakeRecipes = loadAllFiles(manager, "fake_recipes", new ArrayList<>(), EmcLoader::loadFakeRecipes); |
| 32 | 34 | ||
| 33 | Emc.reloadData(itemValues, itemTagValues, blockTagValues, fakeRecipes); | 35 | var bannedRecipes = loadAllFiles(manager, "cycle_cut", new ArrayList<>(), EmcLoader::loadBannedRecipes); |
| 36 | |||
| 37 | Emc.reloadData(itemValues, itemTagValues, blockTagValues, fakeRecipes, bannedRecipes); | ||
| 34 | } | 38 | } |
| 35 | 39 | ||
| 36 | private static <T> T loadAllFiles( | 40 | private static <T> T loadAllFiles( |
| @@ -58,6 +62,15 @@ public class EmcLoader implements SimpleSynchronousResourceReloader { | |||
| 58 | return arg; | 62 | return arg; |
| 59 | } | 63 | } |
| 60 | 64 | ||
| 65 | private static void loadBannedRecipes(List<BannedRecipe> recipes, ResourceLocation id, InputStream is) | ||
| 66 | throws IOException | ||
| 67 | { | ||
| 68 | try (var reader = new InputStreamReader(is)) { | ||
| 69 | var json = GSON.fromJson(reader, new TypeToken<List<BannedRecipe>>(){}); | ||
| 70 | recipes.addAll(json); | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 61 | private static void loadEmcValues(Map<ResourceLocation, OptionalDouble> map, ResourceLocation id, InputStream is) | 74 | private static void loadEmcValues(Map<ResourceLocation, OptionalDouble> map, ResourceLocation id, InputStream is) |
| 62 | throws IOException | 75 | throws IOException |
| 63 | { | 76 | { |
diff --git a/src/main/java/lv/enes/mc/eris_alchemy/ErisAlchemy.java b/src/main/java/lv/enes/mc/eris_alchemy/ErisAlchemy.java index 1d363c2..644a2a4 100644 --- a/src/main/java/lv/enes/mc/eris_alchemy/ErisAlchemy.java +++ b/src/main/java/lv/enes/mc/eris_alchemy/ErisAlchemy.java | |||
| @@ -2,6 +2,9 @@ package lv.enes.mc.eris_alchemy; | |||
| 2 | 2 | ||
| 3 | import com.google.gson.Gson; | 3 | import com.google.gson.Gson; |
| 4 | import com.google.gson.GsonBuilder; | 4 | import com.google.gson.GsonBuilder; |
| 5 | import lv.enes.mc.eris_alchemy.recipe.BannedRecipe; | ||
| 6 | import lv.enes.mc.eris_alchemy.recipe.SimplifiedRecipe; | ||
| 7 | import lv.enes.mc.eris_alchemy.utils.IngredientProvider; | ||
| 5 | import lv.enes.mc.eris_alchemy.utils.PlayerUtils; | 8 | import lv.enes.mc.eris_alchemy.utils.PlayerUtils; |
| 6 | import net.fabricmc.fabric.api.itemgroup.v1.FabricItemGroup; | 9 | import net.fabricmc.fabric.api.itemgroup.v1.FabricItemGroup; |
| 7 | import net.minecraft.core.Registry; | 10 | import net.minecraft.core.Registry; |
| @@ -11,6 +14,7 @@ import net.minecraft.resources.ResourceLocation; | |||
| 11 | import net.minecraft.server.packs.PackType; | 14 | import net.minecraft.server.packs.PackType; |
| 12 | import net.minecraft.world.item.CreativeModeTab; | 15 | import net.minecraft.world.item.CreativeModeTab; |
| 13 | import net.minecraft.world.item.ItemStack; | 16 | import net.minecraft.world.item.ItemStack; |
| 17 | import net.minecraft.world.item.crafting.Ingredient; | ||
| 14 | import org.quiltmc.loader.api.ModContainer; | 18 | import org.quiltmc.loader.api.ModContainer; |
| 15 | import org.quiltmc.qsl.base.api.entrypoint.ModInitializer; | 19 | import org.quiltmc.qsl.base.api.entrypoint.ModInitializer; |
| 16 | import org.quiltmc.qsl.lifecycle.api.event.ServerLifecycleEvents; | 20 | import org.quiltmc.qsl.lifecycle.api.event.ServerLifecycleEvents; |
| @@ -20,6 +24,8 @@ import org.slf4j.LoggerFactory; | |||
| 20 | 24 | ||
| 21 | public class ErisAlchemy implements ModInitializer { | 25 | public class ErisAlchemy implements ModInitializer { |
| 22 | public static final Gson GSON = new GsonBuilder() | 26 | public static final Gson GSON = new GsonBuilder() |
| 27 | .registerTypeAdapter(BannedRecipe.class, new BannedRecipe.Deserializer()) | ||
| 28 | .registerTypeAdapter(Ingredient.class, new IngredientProvider()) | ||
| 23 | .registerTypeAdapter(ResourceLocation.class, new ResourceLocation.Serializer()) | 29 | .registerTypeAdapter(ResourceLocation.class, new ResourceLocation.Serializer()) |
| 24 | .registerTypeAdapter(SimplifiedRecipe.class, new SimplifiedRecipe.Deserializer()) | 30 | .registerTypeAdapter(SimplifiedRecipe.class, new SimplifiedRecipe.Deserializer()) |
| 25 | .create(); | 31 | .create(); |
diff --git a/src/main/java/lv/enes/mc/eris_alchemy/recipe/BannedRecipe.java b/src/main/java/lv/enes/mc/eris_alchemy/recipe/BannedRecipe.java new file mode 100644 index 0000000..d94222c --- /dev/null +++ b/src/main/java/lv/enes/mc/eris_alchemy/recipe/BannedRecipe.java | |||
| @@ -0,0 +1,40 @@ | |||
| 1 | package lv.enes.mc.eris_alchemy.recipe; | ||
| 2 | |||
| 3 | import com.google.gson.JsonDeserializationContext; | ||
| 4 | import com.google.gson.JsonDeserializer; | ||
| 5 | import com.google.gson.JsonElement; | ||
| 6 | import com.google.gson.JsonParseException; | ||
| 7 | import lv.enes.mc.eris_alchemy.utils.IngredientProvider; | ||
| 8 | import net.minecraft.world.item.crafting.Ingredient; | ||
| 9 | |||
| 10 | import java.lang.reflect.Type; | ||
| 11 | import java.util.function.Supplier; | ||
| 12 | |||
| 13 | public record BannedRecipe(Supplier<Ingredient> input, Supplier<Ingredient> output) { | ||
| 14 | public static BannedRecipe deserialize(JsonElement el) { | ||
| 15 | if (el.isJsonPrimitive() && el.getAsJsonPrimitive().isString()) { | ||
| 16 | return deserialize(el.getAsString()); | ||
| 17 | } else { | ||
| 18 | // TODO: Verbose object representation | ||
| 19 | throw new JsonParseException("Banned recipes should all be strings"); | ||
| 20 | } | ||
| 21 | } | ||
| 22 | |||
| 23 | public static BannedRecipe deserialize(String str) { | ||
| 24 | var split = str.indexOf("->"); | ||
| 25 | var input = IngredientProvider.deserialize(str.substring(0, split).trim()); | ||
| 26 | var output = IngredientProvider.deserialize(str.substring(split + 2).trim()); | ||
| 27 | return new BannedRecipe(input, output); | ||
| 28 | } | ||
| 29 | |||
| 30 | public static class Deserializer implements JsonDeserializer<BannedRecipe> { | ||
| 31 | @Override | ||
| 32 | public BannedRecipe deserialize( | ||
| 33 | JsonElement el, | ||
| 34 | Type t, | ||
| 35 | JsonDeserializationContext ctx | ||
| 36 | ) throws JsonParseException { | ||
| 37 | return BannedRecipe.deserialize(el); | ||
| 38 | } | ||
| 39 | } | ||
| 40 | } | ||
diff --git a/src/main/java/lv/enes/mc/eris_alchemy/SimplifiedRecipe.java b/src/main/java/lv/enes/mc/eris_alchemy/recipe/SimplifiedRecipe.java index ef69b84..469ed52 100644 --- a/src/main/java/lv/enes/mc/eris_alchemy/SimplifiedRecipe.java +++ b/src/main/java/lv/enes/mc/eris_alchemy/recipe/SimplifiedRecipe.java | |||
| @@ -1,7 +1,12 @@ | |||
| 1 | package lv.enes.mc.eris_alchemy; | 1 | package lv.enes.mc.eris_alchemy.recipe; |
| 2 | 2 | ||
| 3 | import com.google.gson.*; | 3 | import com.google.gson.JsonDeserializationContext; |
| 4 | import com.google.gson.JsonDeserializer; | ||
| 5 | import com.google.gson.JsonElement; | ||
| 6 | import com.google.gson.JsonParseException; | ||
| 4 | import jakarta.annotation.Nonnull; | 7 | import jakarta.annotation.Nonnull; |
| 8 | import lv.enes.mc.eris_alchemy.utils.IngredientProvider; | ||
| 9 | import lv.enes.mc.eris_alchemy.utils.ItemUtils; | ||
| 5 | import lv.enes.mc.eris_alchemy.utils.RecipeUtils; | 10 | import lv.enes.mc.eris_alchemy.utils.RecipeUtils; |
| 6 | import net.minecraft.core.RegistryAccess; | 11 | import net.minecraft.core.RegistryAccess; |
| 7 | import net.minecraft.core.registries.BuiltInRegistries; | 12 | import net.minecraft.core.registries.BuiltInRegistries; |
| @@ -12,18 +17,58 @@ import net.minecraft.world.item.crafting.Recipe; | |||
| 12 | import net.minecraft.world.item.crafting.ShapedRecipe; | 17 | import net.minecraft.world.item.crafting.ShapedRecipe; |
| 13 | 18 | ||
| 14 | import java.lang.reflect.Type; | 19 | import java.lang.reflect.Type; |
| 20 | import java.util.Arrays; | ||
| 21 | import java.util.Collection; | ||
| 15 | import java.util.List; | 22 | import java.util.List; |
| 23 | import java.util.Objects; | ||
| 24 | import java.util.function.Supplier; | ||
| 25 | import java.util.stream.Stream; | ||
| 16 | 26 | ||
| 17 | public record SimplifiedRecipe(ItemStack output, List<ItemStack> remainder, List<Ingredient> input) { | 27 | public record SimplifiedRecipe(ItemStack output, List<ItemStack> remainder, List<Supplier<Ingredient>> input) { |
| 18 | public SimplifiedRecipe(Recipe<?> recipe, RegistryAccess registryAccess) { | 28 | public SimplifiedRecipe(Recipe<?> recipe, RegistryAccess registryAccess) { |
| 19 | this( | 29 | this( |
| 20 | RecipeUtils.getOutput(recipe, registryAccess), | 30 | RecipeUtils.getOutput(recipe, registryAccess), |
| 21 | List.of(), // TODO: | 31 | List.of(), // TODO: |
| 22 | RecipeUtils.getIngredients(recipe).stream().filter(ingredient -> !ingredient.isEmpty()).toList() | 32 | RecipeUtils.getIngredients(recipe) |
| 33 | .stream() | ||
| 34 | .filter(ingredient -> !ingredient.isEmpty()) | ||
| 35 | .map(x -> (Supplier<Ingredient>) () -> x) | ||
| 36 | .toList() | ||
| 23 | ); | 37 | ); |
| 24 | } | 38 | } |
| 25 | 39 | ||
| 26 | static class Deserializer implements JsonDeserializer<SimplifiedRecipe> { | 40 | public Stream<ResourceLocation> dependencies() { |
| 41 | return Stream.concat( | ||
| 42 | input.stream().map(Supplier::get).map(Ingredient::getItems).flatMap(Arrays::stream), | ||
| 43 | remainder.stream() | ||
| 44 | ).map(ItemUtils::getId); | ||
| 45 | } | ||
| 46 | |||
| 47 | public boolean hasDuplication() { | ||
| 48 | var outputId = ItemUtils.getId(output); | ||
| 49 | var outputsInRemainder = remainder.stream() | ||
| 50 | .map(ItemUtils::getId) | ||
| 51 | .anyMatch(id -> Objects.equals(id, outputId)); | ||
| 52 | if (outputsInRemainder) { | ||
| 53 | return true; | ||
| 54 | } | ||
| 55 | |||
| 56 | return input.stream().anyMatch(ingredient -> ingredient.get().test(output)); | ||
| 57 | } | ||
| 58 | |||
| 59 | public boolean isAllowed(BannedRecipe ban) { | ||
| 60 | if (!ban.output().get().test(output())) { | ||
| 61 | return true; | ||
| 62 | } | ||
| 63 | |||
| 64 | return dependencies().noneMatch(dep -> ban.input().get().test(ItemUtils.get(dep).getDefaultInstance())); | ||
| 65 | } | ||
| 66 | |||
| 67 | public boolean isAllowed(Collection<BannedRecipe> bans) { | ||
| 68 | return bans.stream().allMatch(this::isAllowed); | ||
| 69 | } | ||
| 70 | |||
| 71 | public static class Deserializer implements JsonDeserializer<SimplifiedRecipe> { | ||
| 27 | @Nonnull | 72 | @Nonnull |
| 28 | @Override | 73 | @Override |
| 29 | public SimplifiedRecipe deserialize( | 74 | public SimplifiedRecipe deserialize( |
| @@ -46,23 +91,12 @@ public record SimplifiedRecipe(ItemStack output, List<ItemStack> remainder, List | |||
| 46 | return new SimplifiedRecipe(output, remainder, input); | 91 | return new SimplifiedRecipe(output, remainder, input); |
| 47 | } | 92 | } |
| 48 | 93 | ||
| 49 | private List<Ingredient> parseInputs(JsonElement el) { | 94 | private List<Supplier<Ingredient>> parseInputs(JsonElement el) { |
| 50 | if (el.isJsonArray()) { | 95 | if (el.isJsonArray()) { |
| 51 | return el.getAsJsonArray().asList().stream().map(this::parseInput).map(Ingredient::of).toList(); | 96 | return el.getAsJsonArray().asList().stream().map(IngredientProvider::deserialize).toList(); |
| 52 | } | 97 | } |
| 53 | 98 | ||
| 54 | return List.of(Ingredient.of(parseInput(el))); | 99 | return List.of(IngredientProvider.deserialize(el)); |
| 55 | } | ||
| 56 | |||
| 57 | private ItemStack parseInput(JsonElement el) { | ||
| 58 | if (el.isJsonObject()) { | ||
| 59 | return ShapedRecipe.itemStackFromJson(el.getAsJsonObject()); | ||
| 60 | } else if (el.isJsonPrimitive() && el.getAsJsonPrimitive().isString()) { | ||
| 61 | var id = new ResourceLocation(el.getAsString()); | ||
| 62 | return BuiltInRegistries.ITEM.get(id).getDefaultInstance(); | ||
| 63 | } else { | ||
| 64 | throw new JsonParseException("Every recipe input should be an object or a string"); | ||
| 65 | } | ||
| 66 | } | 100 | } |
| 67 | 101 | ||
| 68 | private ItemStack parseOutputOrRemainder(JsonElement el) { | 102 | private ItemStack parseOutputOrRemainder(JsonElement el) { |
diff --git a/src/main/java/lv/enes/mc/eris_alchemy/utils/ConsList.java b/src/main/java/lv/enes/mc/eris_alchemy/utils/ConsList.java new file mode 100644 index 0000000..bbab8a6 --- /dev/null +++ b/src/main/java/lv/enes/mc/eris_alchemy/utils/ConsList.java | |||
| @@ -0,0 +1,103 @@ | |||
| 1 | package lv.enes.mc.eris_alchemy.utils; | ||
| 2 | |||
| 3 | import jakarta.annotation.Nonnull; | ||
| 4 | |||
| 5 | import java.util.Objects; | ||
| 6 | import java.util.Optional; | ||
| 7 | import java.util.function.Supplier; | ||
| 8 | import java.util.stream.Stream; | ||
| 9 | |||
| 10 | public sealed abstract class ConsList<E> { | ||
| 11 | public static <E> ConsList<E> cons(E car, ConsList<E> cdr) { | ||
| 12 | return new Cons<>(car, cdr); | ||
| 13 | } | ||
| 14 | |||
| 15 | public static <E> ConsList<E> nil() { | ||
| 16 | return Nil.of(); | ||
| 17 | } | ||
| 18 | |||
| 19 | @Nonnull | ||
| 20 | public abstract Optional<E> car(); | ||
| 21 | |||
| 22 | @Nonnull | ||
| 23 | public abstract ConsList<E> cdr(); | ||
| 24 | |||
| 25 | public boolean contains(E elem) { | ||
| 26 | return stream().anyMatch(x -> Objects.equals(x, elem)); | ||
| 27 | } | ||
| 28 | |||
| 29 | public Stream<E> stream() { | ||
| 30 | var car = car(); | ||
| 31 | if (car.isEmpty()) { | ||
| 32 | return Stream.empty(); | ||
| 33 | } | ||
| 34 | |||
| 35 | return Stream.<Supplier<Stream<E>>>of( | ||
| 36 | () -> Stream.of(car.get()), | ||
| 37 | () -> cdr().stream() | ||
| 38 | ) | ||
| 39 | .flatMap(Supplier::get); | ||
| 40 | } | ||
| 41 | |||
| 42 | @Override | ||
| 43 | public String toString() { | ||
| 44 | if (car().isEmpty()) { | ||
| 45 | return "[]"; | ||
| 46 | } | ||
| 47 | |||
| 48 | var sb = new StringBuilder("["); | ||
| 49 | sb.append(car().get()); | ||
| 50 | stream().skip(1) | ||
| 51 | .forEach(x -> { | ||
| 52 | sb.append(" -> "); | ||
| 53 | sb.append(x); | ||
| 54 | }); | ||
| 55 | sb.append("]"); | ||
| 56 | return sb.toString(); | ||
| 57 | } | ||
| 58 | |||
| 59 | private static final class Cons<E> extends ConsList<E> { | ||
| 60 | private final E car; | ||
| 61 | private final ConsList<E> cdr; | ||
| 62 | |||
| 63 | public Cons(E car, ConsList<E> cdr) { | ||
| 64 | this.car = car; | ||
| 65 | this.cdr = cdr; | ||
| 66 | } | ||
| 67 | |||
| 68 | @Nonnull | ||
| 69 | @Override | ||
| 70 | public Optional<E> car() { | ||
| 71 | return Optional.of(car); | ||
| 72 | } | ||
| 73 | |||
| 74 | @Nonnull | ||
| 75 | @Override | ||
| 76 | public ConsList<E> cdr() { | ||
| 77 | return cdr; | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | private static final class Nil<E> extends ConsList<E> { | ||
| 82 | private static final Nil<?> INSTANCE = new Nil<>(); | ||
| 83 | |||
| 84 | @SuppressWarnings("unchecked") | ||
| 85 | public static <E> Nil<E> of() { | ||
| 86 | return (Nil<E>)INSTANCE; | ||
| 87 | } | ||
| 88 | |||
| 89 | private Nil(){} | ||
| 90 | |||
| 91 | @Nonnull | ||
| 92 | @Override | ||
| 93 | public Optional<E> car() { | ||
| 94 | return Optional.empty(); | ||
| 95 | } | ||
| 96 | |||
| 97 | @Nonnull | ||
| 98 | @Override | ||
| 99 | public ConsList<E> cdr() { | ||
| 100 | return Nil.of(); | ||
| 101 | } | ||
| 102 | } | ||
| 103 | } | ||
diff --git a/src/main/java/lv/enes/mc/eris_alchemy/utils/IngredientProvider.java b/src/main/java/lv/enes/mc/eris_alchemy/utils/IngredientProvider.java new file mode 100644 index 0000000..e859114 --- /dev/null +++ b/src/main/java/lv/enes/mc/eris_alchemy/utils/IngredientProvider.java | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | package lv.enes.mc.eris_alchemy.utils; | ||
| 2 | |||
| 3 | import com.google.gson.JsonDeserializationContext; | ||
| 4 | import com.google.gson.JsonDeserializer; | ||
| 5 | import com.google.gson.JsonElement; | ||
| 6 | import com.google.gson.JsonParseException; | ||
| 7 | import net.minecraft.core.registries.Registries; | ||
| 8 | import net.minecraft.resources.ResourceLocation; | ||
| 9 | import net.minecraft.tags.TagKey; | ||
| 10 | import net.minecraft.world.item.crafting.Ingredient; | ||
| 11 | import net.minecraft.world.item.crafting.ShapedRecipe; | ||
| 12 | |||
| 13 | import java.lang.reflect.Type; | ||
| 14 | import java.util.function.Supplier; | ||
| 15 | |||
| 16 | public class IngredientProvider implements JsonDeserializer<Supplier<Ingredient>> { | ||
| 17 | public static Supplier<Ingredient> deserialize(JsonElement el) { | ||
| 18 | if (el.isJsonObject()) { | ||
| 19 | return () -> Ingredient.of(ShapedRecipe.itemStackFromJson(el.getAsJsonObject())); | ||
| 20 | } else if (el.isJsonPrimitive() && el.getAsJsonPrimitive().isString()) { | ||
| 21 | return deserialize(el.getAsString().strip()); | ||
| 22 | } else { | ||
| 23 | throw new JsonParseException("Every ingredient should be an object or a string"); | ||
| 24 | } | ||
| 25 | } | ||
| 26 | |||
| 27 | public static Supplier<Ingredient> deserialize(String str) { | ||
| 28 | if (str.startsWith("#")) { | ||
| 29 | var tag = TagKey.create(Registries.ITEM, new ResourceLocation(str.substring(1).strip())); | ||
| 30 | return () -> Ingredient.of(tag); | ||
| 31 | } else { | ||
| 32 | return () -> Ingredient.of(ItemUtils.get(new ResourceLocation(str))); | ||
| 33 | } | ||
| 34 | } | ||
| 35 | |||
| 36 | @Override | ||
| 37 | public Supplier<Ingredient> deserialize( | ||
| 38 | JsonElement el, | ||
| 39 | Type t, | ||
| 40 | JsonDeserializationContext c | ||
| 41 | ) { | ||
| 42 | return IngredientProvider.deserialize(el); | ||
| 43 | } | ||
| 44 | } | ||
diff --git a/src/main/java/lv/enes/mc/eris_alchemy/utils/ItemUtils.java b/src/main/java/lv/enes/mc/eris_alchemy/utils/ItemUtils.java index 4414d06..7ac7358 100644 --- a/src/main/java/lv/enes/mc/eris_alchemy/utils/ItemUtils.java +++ b/src/main/java/lv/enes/mc/eris_alchemy/utils/ItemUtils.java | |||
| @@ -3,10 +3,15 @@ package lv.enes.mc.eris_alchemy.utils; | |||
| 3 | import net.minecraft.core.Holder; | 3 | import net.minecraft.core.Holder; |
| 4 | import net.minecraft.core.registries.BuiltInRegistries; | 4 | import net.minecraft.core.registries.BuiltInRegistries; |
| 5 | import net.minecraft.resources.ResourceLocation; | 5 | import net.minecraft.resources.ResourceLocation; |
| 6 | import net.minecraft.world.item.Item; | ||
| 6 | import net.minecraft.world.item.ItemStack; | 7 | import net.minecraft.world.item.ItemStack; |
| 7 | import net.minecraft.world.level.ItemLike; | 8 | import net.minecraft.world.level.ItemLike; |
| 8 | 9 | ||
| 9 | public final class ItemUtils { | 10 | public final class ItemUtils { |
| 11 | public static Item get(ResourceLocation id) { | ||
| 12 | return BuiltInRegistries.ITEM.get(id); | ||
| 13 | } | ||
| 14 | |||
| 10 | public static <I extends ItemLike> ResourceLocation getId(Holder<I> holder) { | 15 | public static <I extends ItemLike> ResourceLocation getId(Holder<I> holder) { |
| 11 | return getId(holder.value()); | 16 | return getId(holder.value()); |
| 12 | } | 17 | } |
diff --git a/src/main/java/lv/enes/mc/eris_alchemy/utils/OptionalDoubleSummer.java b/src/main/java/lv/enes/mc/eris_alchemy/utils/OptionalDoubleSummer.java new file mode 100644 index 0000000..04b6ca8 --- /dev/null +++ b/src/main/java/lv/enes/mc/eris_alchemy/utils/OptionalDoubleSummer.java | |||
| @@ -0,0 +1,47 @@ | |||
| 1 | package lv.enes.mc.eris_alchemy.utils; | ||
| 2 | |||
| 3 | import java.util.OptionalDouble; | ||
| 4 | import java.util.Set; | ||
| 5 | import java.util.function.BiConsumer; | ||
| 6 | import java.util.function.BinaryOperator; | ||
| 7 | import java.util.function.Function; | ||
| 8 | import java.util.function.Supplier; | ||
| 9 | import java.util.stream.Collector; | ||
| 10 | |||
| 11 | public class OptionalDoubleSummer implements Collector<OptionalDouble, Ref<OptionalDouble>, OptionalDouble> { | ||
| 12 | @Override | ||
| 13 | public Supplier<Ref<OptionalDouble>> supplier() { | ||
| 14 | return () -> new Ref<>(OptionalDouble.of(0)); | ||
| 15 | } | ||
| 16 | |||
| 17 | @Override | ||
| 18 | public BiConsumer<Ref<OptionalDouble>, OptionalDouble> accumulator() { | ||
| 19 | return (ref, od) -> { | ||
| 20 | if (ref.getValue().isPresent()) { | ||
| 21 | if (od.isPresent()) { | ||
| 22 | ref.setValue(OptionalDouble.of(ref.getValue().getAsDouble() + od.getAsDouble())); | ||
| 23 | } else { | ||
| 24 | ref.setValue(OptionalDouble.empty()); | ||
| 25 | } | ||
| 26 | } | ||
| 27 | }; | ||
| 28 | } | ||
| 29 | |||
| 30 | @Override | ||
| 31 | public BinaryOperator<Ref<OptionalDouble>> combiner() { | ||
| 32 | return (ref1, ref2) -> { | ||
| 33 | accumulator().accept(ref1, ref2.getValue()); | ||
| 34 | return ref1; | ||
| 35 | }; | ||
| 36 | } | ||
| 37 | |||
| 38 | @Override | ||
| 39 | public Function<Ref<OptionalDouble>, OptionalDouble> finisher() { | ||
| 40 | return Ref::getValue; | ||
| 41 | } | ||
| 42 | |||
| 43 | @Override | ||
| 44 | public Set<Characteristics> characteristics() { | ||
| 45 | return Set.of(Characteristics.UNORDERED); | ||
| 46 | } | ||
| 47 | } | ||
diff --git a/src/main/java/lv/enes/mc/eris_alchemy/utils/Ref.java b/src/main/java/lv/enes/mc/eris_alchemy/utils/Ref.java new file mode 100644 index 0000000..dc2624a --- /dev/null +++ b/src/main/java/lv/enes/mc/eris_alchemy/utils/Ref.java | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | package lv.enes.mc.eris_alchemy.utils; | ||
| 2 | |||
| 3 | import lombok.Getter; | ||
| 4 | import lombok.Setter; | ||
| 5 | |||
| 6 | @Setter | ||
| 7 | @Getter | ||
| 8 | public class Ref<T> { | ||
| 9 | private T value; | ||
| 10 | |||
| 11 | public Ref(T value) { | ||
| 12 | this.value = value; | ||
| 13 | } | ||
| 14 | } | ||