summaryrefslogtreecommitdiff
path: root/src/main/java/lv/enes/mc/eris_alchemy/Emc.java
diff options
context:
space:
mode:
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.java171
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
3import jakarta.annotation.Nullable; 3import jakarta.annotation.Nullable;
4import lv.enes.mc.eris_alchemy.ErisAlchemyRegistry.NetworkingConstants; 4import lv.enes.mc.eris_alchemy.ErisAlchemyRegistry.NetworkingConstants;
5import lv.enes.mc.eris_alchemy.utils.BufUtils; 5import lv.enes.mc.eris_alchemy.recipe.BannedRecipe;
6import lv.enes.mc.eris_alchemy.utils.ItemUtils; 6import lv.enes.mc.eris_alchemy.recipe.SimplifiedRecipe;
7import lv.enes.mc.eris_alchemy.utils.PlayerUtils; 7import lv.enes.mc.eris_alchemy.utils.*;
8import net.minecraft.client.Minecraft; 8import net.minecraft.client.Minecraft;
9import net.minecraft.core.registries.BuiltInRegistries; 9import net.minecraft.core.registries.BuiltInRegistries;
10import net.minecraft.core.registries.Registries; 10import net.minecraft.core.registries.Registries;
@@ -27,6 +27,7 @@ import org.quiltmc.qsl.networking.api.client.ClientPlayNetworking;
27 27
28import java.text.DecimalFormat; 28import java.text.DecimalFormat;
29import java.util.*; 29import java.util.*;
30import java.util.function.Supplier;
30import java.util.stream.Stream; 31import java.util.stream.Stream;
31 32
32public final class Emc { 33public 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 }