Skip to content

Commit 4b5434d

Browse files
committed
enhancement: rebuild recipe cache after bulk registering/removing recipes
* instead of rebuilding it after every single recipe
1 parent bda4c70 commit 4b5434d

File tree

4 files changed

+98
-46
lines changed

4 files changed

+98
-46
lines changed

spigot/src/main/java/me/wolfyscript/customcrafting/commands/cc_subcommands/ReloadSubCommand.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import me.wolfyscript.customcrafting.gui.recipebook.ClusterRecipeBook;
2929
import me.wolfyscript.customcrafting.gui.recipebook.MenuCategoryOverview;
3030
import me.wolfyscript.customcrafting.gui.recipebook.MenuRecipeOverview;
31+
import me.wolfyscript.customcrafting.recipes.CustomRecipe;
3132
import me.wolfyscript.customcrafting.registry.RegistryRecipes;
3233
import me.wolfyscript.customcrafting.utils.ChatUtils;
3334
import me.wolfyscript.customcrafting.utils.NamespacedKeyUtils;
@@ -36,6 +37,7 @@
3637
import me.wolfyscript.lib.net.kyori.adventure.text.format.NamedTextColor;
3738
import me.wolfyscript.utilities.api.WolfyUtilities;
3839
import me.wolfyscript.utilities.registry.RegistryCustomItem;
40+
import me.wolfyscript.utilities.util.NamespacedKey;
3941
import org.bukkit.command.CommandSender;
4042
import org.bukkit.entity.Player;
4143
import org.jetbrains.annotations.NotNull;
@@ -64,7 +66,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull String var3, @N
6466
sendMessage(sender, Component.text("Unloading Recipes...", NamedTextColor.YELLOW));
6567
//Unregister recipes
6668
RegistryRecipes registryRecipes = customCrafting.getRegistries().getRecipes();
67-
registryRecipes.get(NamespacedKeyUtils.NAMESPACE).forEach(customRecipe -> registryRecipes.remove(customRecipe.getNamespacedKey()));
69+
registryRecipes.removeAll(registryRecipes.get(NamespacedKeyUtils.NAMESPACE).stream().map(CustomRecipe::getNamespacedKey).toArray(NamespacedKey[]::new));
6870
sendMessage(sender, Component.text("Unloading Items...", NamedTextColor.YELLOW));
6971
//Unregister items
7072
RegistryCustomItem registryCustomItem = customCrafting.getApi().getRegistries().getCustomItems();

spigot/src/main/java/me/wolfyscript/customcrafting/handlers/LocalStorageLoader.java

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ public void load() {
160160
} else {
161161
customCrafting.getLogger().info(PREFIX + "Using " + processors + " threads");
162162
}
163-
executor = Executors.newWorkStealingPool(processors);
163+
executor = Executors.newFixedThreadPool(processors);
164164
api.getConsole().info(PREFIX + "Looking through data folder...");
165165
String[] dirs = DATA_FOLDER.list();
166166
if (dirs != null) {
@@ -173,7 +173,6 @@ public void load() {
173173
api.getConsole().info(PREFIX + "$msg.startup.recipes.recipes$");
174174
new NewDataLoader(dirs).load();
175175
//Loading old & legacy recipes
176-
//The recipes are only loaded if they are not already loaded in previous stages! So if a new version of a recipe exists, then the older ones are ignored.
177176
new OldDataLoader(dirs).load();
178177
new LegacyDataLoader(dirs).load();
179178

@@ -195,6 +194,8 @@ public void load() {
195194
e.printStackTrace();
196195
return;
197196
}
197+
customCrafting.getRegistries().getRecipes().updateCache();
198+
198199
stopWatch.stop();
199200
int recipeCount = customCrafting.getRegistries().getRecipes().values().size();
200201
api.getConsole().getLogger().info(String.format(LOG_LOADED_RECIPES, recipeCount, stopWatch.getTime(TimeUnit.MILLISECONDS)));
@@ -213,6 +214,8 @@ public void load() {
213214

214215
@Override
215216
protected int validatePending(PluginIntegration pluginIntegration) {
217+
var validRecipes = new ArrayList<CustomRecipe<?>>(recipeDependencies.size());
218+
216219
for (CustomRecipe<?> customRecipe : recipeDependencies.keySet()) {
217220
Collection<Dependency> dependencies = recipeDependencies.get(customRecipe);
218221

@@ -224,10 +227,11 @@ protected int validatePending(PluginIntegration pluginIntegration) {
224227
validateRecipe(customRecipe).ifPresentOrElse(container -> {
225228
switch (container.type()) {
226229
case INVALID -> markInvalid(container);
227-
case VALID -> customCrafting.getRegistries().getRecipes().register(customRecipe);
230+
case VALID -> validRecipes.add(customRecipe);
228231
}
229-
}, () -> customCrafting.getRegistries().getRecipes().register(customRecipe));
232+
}, () -> validRecipes.add(customRecipe));
230233
}
234+
customCrafting.getRegistries().getRecipes().bulkRegister(validRecipes);
231235
return 0;
232236
}
233237

@@ -410,21 +414,27 @@ public boolean delete(CustomItem item) throws IOException {
410414
return false;
411415
}
412416

413-
private void checkDependenciesAndRegister(CustomRecipe<?> recipe) {
414-
Set<Dependency> dependencies = DependencyResolver.resolveDependenciesFor(recipe, recipe.getClass());
415-
dependencies.removeIf(Dependency::isAvailable);
416-
if (!dependencies.isEmpty()) {
417-
recipeDependencies.putAll(recipe, dependencies);
418-
return;
419-
}
420-
421-
// Verify and register recipe
422-
validateRecipe(recipe).ifPresentOrElse(container -> {
423-
switch (container.type()) {
424-
case INVALID -> markInvalid(container);
425-
case VALID -> customCrafting.getRegistries().getRecipes().register(recipe);
417+
private void checkDependenciesAndRegister(List<CustomRecipe<?>> recipes) {
418+
customCrafting.getLogger().info("Registering " + recipes.size() + " recipes");
419+
var filteredRecipes = recipes.stream().filter(recipe -> {
420+
Set<Dependency> dependencies = DependencyResolver.resolveDependenciesFor(recipe, recipe.getClass());
421+
dependencies.removeIf(Dependency::isAvailable);
422+
if (!dependencies.isEmpty()) {
423+
recipeDependencies.putAll(recipe, dependencies);
424+
return false;
426425
}
427-
}, () -> customCrafting.getRegistries().getRecipes().register(recipe));
426+
// Verify and register recipe
427+
return validateRecipe(recipe).map(result -> switch (result.type()) {
428+
case INVALID -> {
429+
markInvalid(result);
430+
yield false;
431+
}
432+
case VALID -> true;
433+
default -> false;
434+
}).orElse(true);
435+
}).toList();
436+
customCrafting.getLogger().info("Bulk register " + filteredRecipes.size() + " recipes");
437+
customCrafting.getRegistries().getRecipes().bulkRegister(filteredRecipes);
428438
}
429439

430440
/**
@@ -501,8 +511,7 @@ private void loadRecipesInNamespace(String namespace) {
501511
injectableValues.addValue("customcrafting", customCrafting);
502512

503513
CustomRecipe<?> recipe = objectMapper.reader(injectableValues).forType(recipeTypeRef).readValue(file.toFile());
504-
checkDependenciesAndRegister(recipe);
505-
514+
customCrafting.getRegistries().getRecipes().registerNoUpdate(namespacedKey, recipe);
506515
} catch (IOException e) {
507516
markFailed(namespacedKey);
508517

@@ -582,7 +591,7 @@ protected void loadOldOrLegacyRecipeFiles(RecipeLoader<?> loader, List<File> fil
582591
executeTask(() -> {
583592
try {
584593
CustomRecipe<?> recipe = loader.getInstance(namespacedKey, objectMapper.readTree(file));
585-
checkDependenciesAndRegister(recipe);
594+
customCrafting.getRegistries().getRecipes().registerNoUpdate(namespacedKey, recipe);
586595
} catch (IOException | InstantiationException | InvocationTargetException | NoSuchMethodException |
587596
IllegalAccessException e) {
588597
ChatUtils.sendRecipeItemLoadingError("[LOCAL_OLD] ", namespacedKey.getNamespace(), namespacedKey.getKey(), e);

spigot/src/main/java/me/wolfyscript/customcrafting/handlers/SQLDatabaseLoader.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ public void loadRecipes() {
148148
skippedError.clear();
149149
skippedAlreadyExisting.clear();
150150
api.getConsole().info(PREFIX + "$msg.startup.recipes.recipes$");
151+
var recipes = new ArrayList<CustomRecipe<?>>();
151152
try (PreparedStatement recipesQuery = dataBase.open().prepareStatement("SELECT * FROM customcrafting_recipes")) {
152153
ResultSet resultSet = recipesQuery.executeQuery();
153154
if (resultSet == null) {
@@ -160,7 +161,7 @@ public void loadRecipes() {
160161
if (isReplaceData() || !customCrafting.getRegistries().getRecipes().has(namespacedKey)) {
161162
CustomRecipe<?> recipe = getRecipe(namespacedKey);
162163
if (recipe != null) {
163-
customCrafting.getRegistries().getRecipes().register(recipe);
164+
recipes.add(recipe);
164165
loaded.add(namespacedKey);
165166
} else {
166167
skippedError.add(namespacedKey);
@@ -174,6 +175,7 @@ public void loadRecipes() {
174175
} finally {
175176
dataBase.close();
176177
}
178+
customCrafting.getRegistries().getRecipes().bulkRegister(recipes);
177179
api.getConsole().getLogger().info(String.format(PREFIX + "Loaded %d recipes; Skipped: %d error/s, %d already existing", loaded.size(), skippedError.size(), skippedAlreadyExisting.size()));
178180
}
179181

spigot/src/main/java/me/wolfyscript/customcrafting/registry/RegistryRecipes.java

Lines changed: 62 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,9 @@ public final class RegistryRecipes extends RegistrySimple<CustomRecipe<?>> {
5959

6060
private final Map<String, List<CustomRecipe<?>>> BY_NAMESPACE = new HashMap<>();
6161
private final Map<String, List<CustomRecipe<?>>> BY_GROUP = new HashMap<>();
62-
private final Map<StackReference, List<CustomRecipe<?>>> BY_RESULT = new HashMap<>();
6362
private final Map<Class<?>, List<CustomRecipe<?>>> BY_CLASS_TYPE = new HashMap<>();
6463
private final Map<RecipeType<?>, List<CustomRecipe<?>>> BY_RECIPE_TYPE = new HashMap<>();
6564
private final Map<RecipeType.Container<?>, List<CustomRecipe<?>>> BY_RECIPE_TYPE_CONTAINER = new HashMap<>();
66-
private final Map<String, Map<String, List<CustomRecipe<?>>>> BY_NAMESPACE_AND_FOLDER = new HashMap<>();
6765
private final Map<String, Map<String, List<CustomRecipe<?>>>> BY_NAMESPACE_AND_DIR = new HashMap<>();
6866
private final Set<String> NAMESPACES = new HashSet<>();
6967
private final Map<String, List<String>> FOLDERS = new HashMap<>();
@@ -78,12 +76,27 @@ public boolean has(NamespacedKey namespacedKey) {
7876
return this.map.containsKey(namespacedKey);
7977
}
8078

79+
public void removeAll(NamespacedKey... keys) {
80+
for (NamespacedKey key : keys) {
81+
removeNoUpdate(key);
82+
}
83+
updateCache(namespacedKey);
84+
}
85+
8186
public void remove(NamespacedKey namespacedKey) {
87+
removeNoUpdate(namespacedKey);
88+
updateCache(namespacedKey);
89+
}
90+
91+
private void removeNoUpdate(NamespacedKey namespacedKey) {
8292
if (get(namespacedKey) instanceof ICustomVanillaRecipe) {
8393
removeBukkitRecipe(namespacedKey);
8494
}
8595
this.map.remove(namespacedKey);
86-
clearCache(namespacedKey);
96+
}
97+
98+
public void updateCache() {
99+
updateCache(new NamespacedKey[0]);
87100
}
88101

89102
/**
@@ -93,27 +106,53 @@ public void remove(NamespacedKey namespacedKey) {
93106
* Of course, you could check if the specific caches must be cleared, but that would be at the cost of register/remove performance.<br>
94107
* (Most servers have a fixed set of recipes that they use and don't frequently change in production... well at least they shouldn't)
95108
*
96-
* @param key The key of the recipe that caused the reset.
109+
* @param keys The key of the recipe that caused the reset.
97110
*/
98-
private void clearCache(NamespacedKey key) {
99-
BY_NAMESPACE.remove(key.getNamespace());
111+
private void updateCache(NamespacedKey... keys) {
112+
for (NamespacedKey key : keys) {
113+
BY_NAMESPACE.remove(key.getNamespace());
114+
FOLDERS.remove(key.getNamespace());
115+
BY_NAMESPACE_AND_DIR.remove(key.getNamespace());
116+
}
100117
BY_GROUP.clear();
101118
NAMESPACES.clear();
102-
FOLDERS.remove(key.getNamespace());
103119
GROUPS.clear();
104-
BY_RESULT.clear();
105120
BY_CLASS_TYPE.clear();
106121
BY_RECIPE_TYPE.clear();
122+
for (RecipeType<? extends CustomRecipe<?>> type : RecipeType.values()) {
123+
BY_RECIPE_TYPE.put(type, values().stream().filter(type::isInstance).toList());
124+
}
107125
BY_RECIPE_TYPE_CONTAINER.clear();
108-
BY_NAMESPACE_AND_FOLDER.remove(key.getNamespace());
109-
BY_NAMESPACE_AND_DIR.remove(key.getNamespace());
126+
for (RecipeType.Container<? extends CustomRecipe<?>> container : RecipeType.Container.values()) {
127+
BY_RECIPE_TYPE_CONTAINER.put(container, values().stream().filter(container::isInstance).toList());
128+
}
129+
}
130+
131+
public synchronized void bulkRegister(List<CustomRecipe<?>> recipes) {
132+
var keys = new NamespacedKey[recipes.size()];
133+
for (int i = 0; i < recipes.size(); i++) {
134+
var recipe = recipes.get(i);
135+
registerNoUpdate(recipe.getNamespacedKey(), recipe);
136+
keys[i] = recipe.getNamespacedKey();
137+
}
138+
updateCache(keys);
110139
}
111140

112141
@Override
113142
public synchronized void register(NamespacedKey namespacedKey, CustomRecipe<?> value) {
143+
registerNoUpdate(namespacedKey, value);
144+
updateCache(namespacedKey);
145+
}
146+
147+
@Override
148+
public void register(CustomRecipe<?> value) {
149+
this.register(value.getNamespacedKey(), value);
150+
}
151+
152+
public void registerNoUpdate(NamespacedKey namespacedKey, CustomRecipe<?> value) {
114153
Preconditions.checkArgument(namespacedKey != null, "Invalid NamespacedKey! The namespaced key cannot be null!");
115154
Preconditions.checkArgument(!namespacedKey.getNamespace().equalsIgnoreCase("minecraft"), "Invalid NamespacedKey! Cannot register recipe under minecraft namespace!");
116-
remove(namespacedKey);
155+
removeNoUpdate(namespacedKey);
117156
super.register(namespacedKey, value);
118157
if (value instanceof ICustomVanillaRecipe<?> vanillaRecipe && !value.isDisabled()) {
119158
Bukkit.getScheduler().runTask(customCrafting, () -> {
@@ -134,12 +173,6 @@ public synchronized void register(NamespacedKey namespacedKey, CustomRecipe<?> v
134173
}
135174
});
136175
}
137-
clearCache(namespacedKey);
138-
}
139-
140-
@Override
141-
public void register(CustomRecipe<?> value) {
142-
this.register(value.getNamespacedKey(), value);
143176
}
144177

145178
/**
@@ -395,14 +428,20 @@ public List<CustomRecipe<?>> get(String namespace) {
395428
return BY_NAMESPACE.computeIfAbsent(namespace, s -> entrySet().stream().filter(entry -> entry.getKey().getNamespace().equalsIgnoreCase(s)).map(Map.Entry::getValue).collect(Collectors.toList()));
396429
}
397430

431+
/**
432+
* @deprecated Not used anywhere and fundamentally flawed. Do not use!
433+
*/
434+
@Deprecated(forRemoval = true)
398435
public List<CustomRecipe<?>> get(CustomItem result) {
399-
return BY_RESULT.computeIfAbsent(
400-
result.hasNamespacedKey() ? new StackReference(WolfyUtilCore.getInstance(), new WolfyUtilsStackIdentifier(result.getNamespacedKey()), result.getWeight(), result.getAmount(), result.getItemStack()) : result.stackReference(),
401-
item -> values().stream().filter(recipe -> recipe.getResult().choices().contains(item)).collect(Collectors.toList()));
436+
return get(result.hasNamespacedKey() ? new StackReference(WolfyUtilCore.getInstance(), new WolfyUtilsStackIdentifier(result.getNamespacedKey()), result.getWeight(), result.getAmount(), result.getItemStack()) : result.stackReference());
402437
}
403438

439+
/**
440+
* @deprecated Not used anywhere and fundamentally flawed. Do not use!
441+
*/
442+
@Deprecated(forRemoval = true)
404443
public List<CustomRecipe<?>> get(StackReference reference) {
405-
return BY_RESULT.computeIfAbsent(reference, reference1 -> values().stream().filter(recipe -> recipe.getResult().choices().contains(reference1)).collect(Collectors.toList()));
444+
return values().stream().filter(recipe -> recipe.getResult().choices().contains(reference)).collect(Collectors.toList());
406445
}
407446

408447
@SuppressWarnings("unchecked")
@@ -417,7 +456,7 @@ public <T extends CustomRecipe<?>> List<T> get(Class<T> type) {
417456
*/
418457
@SuppressWarnings("unchecked")
419458
public <T extends CustomRecipe<?>> List<T> get(RecipeType<T> type) {
420-
return (List<T>) BY_RECIPE_TYPE.computeIfAbsent(type, recipeType -> values().stream().filter(recipeType::isInstance).map(recipeType::cast).collect(Collectors.toList()));
459+
return (List<T>) BY_RECIPE_TYPE.getOrDefault(type, Collections.emptyList());
421460
}
422461

423462
/**
@@ -427,7 +466,7 @@ public <T extends CustomRecipe<?>> List<T> get(RecipeType<T> type) {
427466
*/
428467
@SuppressWarnings("unchecked")
429468
public <T extends CustomRecipe<?>> List<T> get(RecipeType.Container<T> type) {
430-
return (List<T>) BY_RECIPE_TYPE_CONTAINER.computeIfAbsent(type, container -> values().stream().filter(container::isInstance).map(container::cast).collect(Collectors.toList()));
469+
return (List<T>) BY_RECIPE_TYPE_CONTAINER.getOrDefault(type, Collections.emptyList());
431470
}
432471

433472
@SafeVarargs

0 commit comments

Comments
 (0)