summaryrefslogtreecommitdiff
path: root/src/main/java/lv/enes/mc/eris_alchemy/Emc.java
diff options
context:
space:
mode:
authorGravatar Uko Kokņevičs2024-01-12 19:12:14 +0100
committerGravatar Uko Kokņevičs2024-01-12 19:12:14 +0100
commit99f70815bc5f489fede134215684057466f20338 (patch)
treeec9f6bc5345fec2ca6731ee9288aefe278a3d38e /src/main/java/lv/enes/mc/eris_alchemy/Emc.java
parentAdd lombok (diff)
downloadmc-eris-alchemy-99f70815bc5f489fede134215684057466f20338.tar.gz
mc-eris-alchemy-99f70815bc5f489fede134215684057466f20338.tar.xz
mc-eris-alchemy-99f70815bc5f489fede134215684057466f20338.zip
Make EMC be synced from server to client
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.java252
1 files changed, 147 insertions, 105 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 fdc7c0a..c12b143 100644
--- a/src/main/java/lv/enes/mc/eris_alchemy/Emc.java
+++ b/src/main/java/lv/enes/mc/eris_alchemy/Emc.java
@@ -1,11 +1,18 @@
1package lv.enes.mc.eris_alchemy; 1package lv.enes.mc.eris_alchemy;
2 2
3import jakarta.annotation.Nonnull;
4import jakarta.annotation.Nullable; 3import jakarta.annotation.Nullable;
4import lv.enes.mc.eris_alchemy.ErisAlchemyRegistry.NetworkingConstants;
5import lv.enes.mc.eris_alchemy.utils.BufUtils;
5import lv.enes.mc.eris_alchemy.utils.ItemUtils; 6import lv.enes.mc.eris_alchemy.utils.ItemUtils;
7import lv.enes.mc.eris_alchemy.utils.PlayerUtils;
8import net.minecraft.client.Minecraft;
6import net.minecraft.core.registries.BuiltInRegistries; 9import net.minecraft.core.registries.BuiltInRegistries;
7import net.minecraft.core.registries.Registries; 10import net.minecraft.core.registries.Registries;
11import net.minecraft.network.FriendlyByteBuf;
8import net.minecraft.resources.ResourceLocation; 12import net.minecraft.resources.ResourceLocation;
13import net.minecraft.server.MinecraftServer;
14import net.minecraft.server.level.ServerLevel;
15import net.minecraft.server.level.ServerPlayer;
9import net.minecraft.tags.TagKey; 16import net.minecraft.tags.TagKey;
10import net.minecraft.world.item.BlockItem; 17import net.minecraft.world.item.BlockItem;
11import net.minecraft.world.item.Item; 18import net.minecraft.world.item.Item;
@@ -13,86 +20,37 @@ import net.minecraft.world.item.ItemStack;
13import net.minecraft.world.item.crafting.Ingredient; 20import net.minecraft.world.item.crafting.Ingredient;
14import net.minecraft.world.level.Level; 21import net.minecraft.world.level.Level;
15import net.minecraft.world.level.block.Block; 22import net.minecraft.world.level.block.Block;
23import org.quiltmc.loader.api.minecraft.ClientOnly;
24import org.quiltmc.qsl.networking.api.PacketByteBufs;
25import org.quiltmc.qsl.networking.api.ServerPlayNetworking;
26import org.quiltmc.qsl.networking.api.client.ClientPlayNetworking;
16 27
17import java.text.DecimalFormat; 28import java.text.DecimalFormat;
18import java.util.*; 29import java.util.*;
19import java.util.stream.Stream; 30import java.util.stream.Stream;
20 31
21public class Emc { 32public final class Emc {
33 private Emc() {}
34
22 private static final Map<ResourceLocation, OptionalDouble> ITEM_VALUES = new HashMap<>(); 35 private static final Map<ResourceLocation, OptionalDouble> ITEM_VALUES = new HashMap<>();
23 private static final Map<TagKey<Item>, OptionalDouble> ITEM_TAG_VALUES = new HashMap<>(); 36 private static final Map<TagKey<Item>, OptionalDouble> ITEM_TAG_VALUES = new HashMap<>();
24 private static final Map<TagKey<Block>, OptionalDouble> BLOCK_TAG_VALUES = new HashMap<>(); 37 private static final Map<TagKey<Block>, OptionalDouble> BLOCK_TAG_VALUES = new HashMap<>();
25 private static final List<SimplifiedRecipe> FAKE_RECIPES = new ArrayList<>(); 38 private static final List<SimplifiedRecipe> FAKE_RECIPES = new ArrayList<>();
26 39
27 private static final Map<Level, Emc> instances = Collections.synchronizedMap(new WeakHashMap<>()); 40 private static final Map<ResourceLocation, OptionalDouble> VALUES = Collections.synchronizedMap(new HashMap<>());
28 41
29 private static final DecimalFormat formatter = new DecimalFormat("0"); 42 private static final DecimalFormat FORMATTER = new DecimalFormat("0");
30 static { 43 static {
31 formatter.setMaximumFractionDigits(1); 44 FORMATTER.setMaximumFractionDigits(1);
32 } 45 }
33 46
34 public static Emc getInstance(@Nonnull Level world) { 47 private static ServerLevel overworld = null;
35 if (instances.containsKey(world)) {
36 return instances.get(world);
37 }
38
39 var instance = new Emc(world);
40 instances.put(world, instance);
41 return instance;
42 }
43 48
44 public static String formatEmc(double value) { 49 public static String formatEmc(double value) {
45 return formatter.format(value); 50 return FORMATTER.format(value);
46 } 51 }
47 52
48 public static void reloadData( 53 public static OptionalDouble get(ItemStack stack) {
49 Map<ResourceLocation, OptionalDouble> itemValues,
50 Map<ResourceLocation, OptionalDouble> itemTagValues,
51 Map<ResourceLocation, OptionalDouble> blockTagValues,
52 List<SimplifiedRecipe> fakeRecipes
53 ) {
54 ITEM_VALUES.clear();
55 ITEM_VALUES.putAll(itemValues);
56
57 ITEM_TAG_VALUES.clear();
58 itemTagValues.forEach((id, value) -> ITEM_TAG_VALUES.put(TagKey.create(Registries.ITEM, id), value));
59
60 BLOCK_TAG_VALUES.clear();
61 blockTagValues.forEach((id, value) -> BLOCK_TAG_VALUES.put(TagKey.create(Registries.BLOCK, id), value));
62
63 FAKE_RECIPES.clear();
64 FAKE_RECIPES.addAll(fakeRecipes);
65
66 instances.clear();
67 }
68
69 private final Map<ResourceLocation, OptionalDouble> data;
70
71 private Emc(@Nonnull Level world) {
72 data = Collections.synchronizedMap(new HashMap<>(ITEM_VALUES));
73 ITEM_TAG_VALUES.forEach(
74 (tag, emcValue) -> BuiltInRegistries.ITEM
75 .getTagOrEmpty(tag)
76 .forEach(holder -> data.putIfAbsent(ItemUtils.getId(holder), emcValue))
77 );
78 BLOCK_TAG_VALUES.forEach(
79 (tag, emcValue) -> BuiltInRegistries.BLOCK
80 .getTagOrEmpty(tag)
81 .forEach(holder -> data.putIfAbsent(ItemUtils.getId(holder), emcValue))
82 );
83
84 ErisAlchemy.LOGGER.info("Calculating EMC values...");
85 var recipes = getRecipes(world);
86 var configured = new HashSet<>(data.keySet());
87 BuiltInRegistries.ITEM.keySet().forEach(item -> {
88 configEmc(recipes, configured, item);
89 if (!data.containsKey(item)) {
90 ErisAlchemy.LOGGER.warn("No EMC value for '{}' known", item);
91 }
92 });
93 }
94
95 public OptionalDouble get(ItemStack stack) {
96 if (stack.isEmpty()) { 54 if (stack.isEmpty()) {
97 return OptionalDouble.empty(); 55 return OptionalDouble.empty();
98 } 56 }
@@ -118,51 +76,66 @@ public class Emc {
118 .findFirst(); 76 .findFirst();
119 } 77 }
120 78
121 public OptionalDouble get(ResourceLocation itemId) { 79 public static OptionalDouble get(ResourceLocation itemId) {
122 return data.getOrDefault(itemId, OptionalDouble.empty()); 80 return VALUES.getOrDefault(itemId, OptionalDouble.empty());
123 } 81 }
124 82
125 private List<SimplifiedRecipe> getRecipes(@Nullable Level world) { 83 @ClientOnly
126 Stream<SimplifiedRecipe> recipes = FAKE_RECIPES.stream(); 84 public static void initClient(Minecraft ignoredClient) {
85 ClientPlayNetworking.registerGlobalReceiver(
86 NetworkingConstants.UPDATE_EMCS,
87 (client1, handler, buf, responseSender) -> {
88 var map = buf.readMap(FriendlyByteBuf::readResourceLocation, BufUtils::readOptionalDouble);
89 VALUES.clear();
90 VALUES.putAll(map);
91 }
92 );
93 }
127 94
128 if (world != null) { 95 public static void initServer(MinecraftServer server) {
129 recipes = Stream.concat( 96 overworld = server.overworld();
130 recipes, 97 reinit();
131 world.getRecipeManager() 98 warnOfMissingValues();
132 .getRecipes() 99 }
133 .stream()
134 .map(recipe -> new SimplifiedRecipe(recipe, world.registryAccess()))
135 );
136 }
137 100
138 return recipes.toList(); 101 public static void reloadData(
102 Map<ResourceLocation, OptionalDouble> itemValues,
103 Map<ResourceLocation, OptionalDouble> itemTagValues,
104 Map<ResourceLocation, OptionalDouble> blockTagValues,
105 List<SimplifiedRecipe> fakeRecipes
106 ) {
107 ITEM_VALUES.clear();
108 ITEM_VALUES.putAll(itemValues);
109
110 ITEM_TAG_VALUES.clear();
111 itemTagValues.forEach((id, value) -> ITEM_TAG_VALUES.put(TagKey.create(Registries.ITEM, id), value));
112
113 BLOCK_TAG_VALUES.clear();
114 blockTagValues.forEach((id, value) -> BLOCK_TAG_VALUES.put(TagKey.create(Registries.BLOCK, id), value));
115
116 FAKE_RECIPES.clear();
117 FAKE_RECIPES.addAll(fakeRecipes);
118
119 reinit();
120 warnOfMissingValues();
139 } 121 }
140 122
141 private OptionalDouble configEmc( 123 private static OptionalDouble calculateEmcForIngredient(
142 List<SimplifiedRecipe> recipes, 124 List<SimplifiedRecipe> recipes,
143 Set<ResourceLocation> configured, 125 Set<ResourceLocation> configured,
144 ResourceLocation itemId 126 Ingredient ingredient
145 ) { 127 ) {
146 var res = get(itemId); 128 return Arrays.stream(ingredient.getItems())
147 if (res.isPresent() || configured.contains(itemId)) { 129 .map(stack -> configEmc(recipes, configured, ItemUtils.getId(stack.getItem())).stream()
148 return res; 130 .map(x -> x * stack.getCount())
149 } 131 .findFirst())
150 132 .filter(OptionalDouble::isPresent)
151 configured.add(itemId); 133 .mapToDouble(OptionalDouble::getAsDouble)
152 var item = BuiltInRegistries.ITEM.get(itemId); 134 .filter(x -> x > 0)
153 res = recipes.stream()
154 .filter(recipe -> recipe.output().is(item))
155 .map(recipe -> calculateEmcForRecipe(recipes, configured, recipe))
156 .flatMapToDouble(OptionalDouble::stream)
157 .average(); 135 .average();
158 res.ifPresentOrElse(
159 emc -> data.put(itemId, OptionalDouble.of(emc)),
160 () -> configured.remove(itemId)
161 );
162 return res;
163 } 136 }
164 137
165 private OptionalDouble calculateEmcForRecipe( 138 private static OptionalDouble calculateEmcForRecipe(
166 List<SimplifiedRecipe> recipes, 139 List<SimplifiedRecipe> recipes,
167 Set<ResourceLocation> configured, 140 Set<ResourceLocation> configured,
168 SimplifiedRecipe recipe 141 SimplifiedRecipe recipe
@@ -197,18 +170,87 @@ public class Emc {
197 } 170 }
198 } 171 }
199 172
200 private OptionalDouble calculateEmcForIngredient( 173 private static OptionalDouble configEmc(
201 List<SimplifiedRecipe> recipes, 174 List<SimplifiedRecipe> recipes,
202 Set<ResourceLocation> configured, 175 Set<ResourceLocation> configured,
203 Ingredient ingredient 176 ResourceLocation itemId
204 ) { 177 ) {
205 return Arrays.stream(ingredient.getItems()) 178 var res = get(itemId);
206 .map(stack -> configEmc(recipes, configured, ItemUtils.getId(stack.getItem())).stream() 179 if (res.isPresent() || configured.contains(itemId)) {
207 .map(x -> x * stack.getCount()) 180 return res;
208 .findFirst()) 181 }
209 .filter(OptionalDouble::isPresent) 182
210 .mapToDouble(OptionalDouble::getAsDouble) 183 configured.add(itemId);
211 .filter(x -> x > 0) 184 var item = BuiltInRegistries.ITEM.get(itemId);
185 res = recipes.stream()
186 .filter(recipe -> recipe.output().is(item))
187 .map(recipe -> calculateEmcForRecipe(recipes, configured, recipe))
188 .flatMapToDouble(OptionalDouble::stream)
212 .average(); 189 .average();
190 res.ifPresentOrElse(
191 emc -> VALUES.put(itemId, OptionalDouble.of(emc)),
192 () -> configured.remove(itemId)
193 );
194 return res;
195 }
196
197 private static List<SimplifiedRecipe> getRecipes(@Nullable Level world) {
198 Stream<SimplifiedRecipe> recipes = FAKE_RECIPES.stream();
199
200 if (world != null) {
201 recipes = Stream.concat(
202 recipes,
203 world.getRecipeManager()
204 .getRecipes()
205 .stream()
206 .map(recipe -> new SimplifiedRecipe(recipe, world.registryAccess()))
207 );
208 }
209
210 return recipes.toList();
211 }
212
213 private static void reinit() {
214 VALUES.clear();
215 VALUES.putAll(ITEM_VALUES);
216 ITEM_TAG_VALUES.forEach(
217 (tag, emcValue) -> BuiltInRegistries.ITEM
218 .getTagOrEmpty(tag)
219 .forEach(holder -> VALUES.putIfAbsent(ItemUtils.getId(holder), emcValue))
220 );
221 BLOCK_TAG_VALUES.forEach(
222 (tag, emcValue) -> BuiltInRegistries.BLOCK
223 .getTagOrEmpty(tag)
224 .forEach(holder -> VALUES.putIfAbsent(ItemUtils.getId(holder), emcValue))
225 );
226
227 ErisAlchemy.LOGGER.info("Calculating EMC values from recipes...");
228 var recipes = getRecipes(overworld);
229 var configured = new HashSet<>(VALUES.keySet());
230 BuiltInRegistries.ITEM.keySet().forEach(item -> configEmc(recipes, configured, item));
231
232 sync();
233 }
234
235 private static void sync() {
236 syncTo(PlayerUtils.all());
237 }
238
239 private static void syncTo(Collection<ServerPlayer> player) {
240 var buf = PacketByteBufs.create();
241 buf.writeMap(VALUES, FriendlyByteBuf::writeResourceLocation, BufUtils::writeOptionalDouble);
242 ServerPlayNetworking.send(player, NetworkingConstants.UPDATE_EMCS, buf);
243 }
244
245 private static void warnOfMissingValues() {
246 if (overworld == null) {
247 return;
248 }
249
250 BuiltInRegistries.ITEM
251 .keySet()
252 .stream()
253 .filter(item -> !VALUES.containsKey(item))
254 .forEach(item -> ErisAlchemy.LOGGER.warn("No EMC value for '{}' known", item));
213 } 255 }
214} 256}