diff options
Diffstat (limited to 'src/main/java/lv/enes/mc/eris_alchemy/Emc.java')
| -rw-r--r-- | src/main/java/lv/enes/mc/eris_alchemy/Emc.java | 171 |
1 files changed, 99 insertions, 72 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 | } |