2020-10-01 16:52:27 +02:00
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Mykyta Komarn <nkomarn@hotmail.com>
Date: Thu, 1 Oct 2020 06:57:43 -0700
Subject: [PATCH] Heavily optimize recipe lookups in CraftingManager
Recipe lookups are now cached in CraftingManager, which prevent unnecessary ArrayLists being created for every lookup. Additionally, an EMPTY_MAP variable was added to prevent bottlenecks during map creation, since that map is only ever iterated.
GlueList was also used as a replacement for ArrayList as it is substantially faster.
These changes knock off an extra ~10ms of tick duration with a sample of ~7,700 running furnaces on a server.
2020-10-08 13:04:22 +02:00
Co-authored-by: Ivan Pekov <ivan@mrivanplays.com>
2020-10-01 16:52:27 +02:00
diff --git a/src/main/java/net/minecraft/server/CraftingManager.java b/src/main/java/net/minecraft/server/CraftingManager.java
2020-12-01 03:08:41 +01:00
index 58ecbe1e20581dc9e78cdd2f4ece29cfa014da8a..3da86dc56f33e4f1900f6b4f66ca6696eaf6525a 100644
2020-10-01 16:52:27 +02:00
--- a/src/main/java/net/minecraft/server/CraftingManager.java
+++ b/src/main/java/net/minecraft/server/CraftingManager.java
2020-10-08 13:04:22 +02:00
@@ -31,6 +31,10 @@ public class CraftingManager extends ResourceDataJson {
2020-10-01 16:52:27 +02:00
private static final Logger LOGGER = LogManager.getLogger();
public Map<Recipes<?>, Object2ObjectLinkedOpenHashMap<MinecraftKey, IRecipe<?>>> recipes = ImmutableMap.of(); // CraftBukkit
private boolean d;
+ // Yatopia start
2020-12-01 03:08:41 +01:00
+ private static final List<IRecipe<?>> ALL_RECIPES_CACHE = new java.util.ArrayList<>();
2020-10-10 22:55:14 +02:00
+ private static final Map<Recipes<?>, List<IRecipe<?>>> TYPES_CACHE = new Object2ObjectLinkedOpenHashMap<>();
2020-10-01 16:52:27 +02:00
+ // Yatopia end
public CraftingManager() {
super(CraftingManager.a, "recipes");
2020-10-12 17:56:34 +02:00
@@ -63,9 +67,17 @@ public class CraftingManager extends ResourceDataJson {
}
}
+ // Yatopia start - nuke stream & cache all recipes for constant access in b()
+ /*
2020-10-10 22:55:14 +02:00
this.recipes = (Map) map1.entrySet().stream().collect(ImmutableMap.toImmutableMap(Entry::getKey, (entry1) -> {
return entry1.getValue(); // CraftBukkit // Paper - decompile fix - *shrugs internally*
}));
2020-10-12 17:56:34 +02:00
+ */
+ this.recipes = ImmutableMap.copyOf(map1);
2020-10-10 22:55:14 +02:00
+ for (Object2ObjectLinkedOpenHashMap<MinecraftKey, IRecipe<?>> recipesMap : map1.values()) {
+ ALL_RECIPES_CACHE.addAll(recipesMap.values());
+ }
+ // Yatopia end
CraftingManager.LOGGER.info("Loaded {} recipes", map1.size());
}
2020-12-01 03:08:41 +01:00
@@ -79,33 +91,65 @@ public class CraftingManager extends ResourceDataJson {
2020-10-12 17:56:34 +02:00
} else {
map.putAndMoveToFirst(irecipe.getKey(), irecipe); // CraftBukkit - SPIGOT-4638: last recipe gets priority
}
+ ALL_RECIPES_CACHE.add(irecipe); // Yatopia
}
// CraftBukkit end
2020-10-07 03:36:00 +02:00
public <C extends IInventory, T extends IRecipe<C>> Optional<T> craft(Recipes<T> recipes, C c0, World world) {
// CraftBukkit start
2020-10-08 13:04:22 +02:00
+ // Yatopia start - replace stream
+ /*
Optional<T> recipe = this.b(recipes).values().stream().flatMap((irecipe) -> {
return SystemUtils.a(recipes.a(irecipe, world, c0));
}).findFirst();
+ */
2020-10-07 03:36:00 +02:00
+ // Yatopia start - replace stream
+ Collection<IRecipe<C>> allTypes = this.b(recipes).values();
+ Optional<T> recipe = Optional.empty();
+
+ for (IRecipe<C> possible : allTypes) {
+ Optional<T> possibleRecipe = recipes.a(possible, world, c0);
+ if (possibleRecipe.isPresent()) {
+ recipe = possibleRecipe;
+ break;
+ }
+ }
2020-10-08 13:04:22 +02:00
+ // Yatopia end
2020-10-07 03:36:00 +02:00
c0.setCurrentRecipe(recipe.orElse(null)); // CraftBukkit - Clear recipe when no recipe is found
// CraftBukkit end
return recipe;
2020-10-01 16:52:27 +02:00
}
public <C extends IInventory, T extends IRecipe<C>> List<T> a(Recipes<T> recipes) {
2020-10-08 13:04:22 +02:00
+ // Yatopia start - replaced logic
2020-10-01 16:52:27 +02:00
+ /*
2020-10-08 13:04:22 +02:00
return (List) this.b(recipes).values().stream().map((irecipe) -> {
return irecipe;
}).collect(Collectors.toList());
+ */
2020-12-01 03:08:41 +01:00
+ return (List) TYPES_CACHE.computeIfAbsent(recipes, recipes1 -> new me.jellysquid.mods.lithium.common.util.collections.HashedList<>(new java.util.ArrayList<>(getRecipesMap(recipes).values())));
2020-10-08 13:04:22 +02:00
+ // Yatopia end
2020-10-01 16:52:27 +02:00
}
public <C extends IInventory, T extends IRecipe<C>> List<T> b(Recipes<T> recipes, C c0, World world) {
2020-12-01 03:08:41 +01:00
+ // Yatopia start - replace stream
+ /*
return (List) this.b(recipes).values().stream().flatMap((irecipe) -> {
return SystemUtils.a(recipes.a(irecipe, world, c0));
}).sorted(Comparator.comparing((irecipe) -> {
return irecipe.getResult().j();
2020-10-08 13:04:22 +02:00
})).collect(Collectors.toList());
2020-12-01 03:08:41 +01:00
+ */
+ List<T> ret = new java.util.ArrayList<>();
+ for (IRecipe<C> recipe : this.b(recipes).values()) {
+ recipes.a(recipe, world, c0).ifPresent(ret::add);
+ }
+ ret.sort(Comparator.comparing(recipe -> recipe.getResult().getTranslationKey()));
+ return ret;
+ // Yatopia end
2020-10-01 16:52:27 +02:00
}
2020-10-08 13:04:22 +02:00
+ private <C extends IInventory, T extends IRecipe<C>> Map<MinecraftKey, IRecipe<C>> getRecipesMap(Recipes<T> recipes) { return b(recipes); } // Yatopia - OBFHELPER
2020-10-01 16:52:27 +02:00
private <C extends IInventory, T extends IRecipe<C>> Map<MinecraftKey, IRecipe<C>> b(Recipes<T> recipes) {
2020-10-10 22:55:14 +02:00
return (Map) this.recipes.getOrDefault(recipes, new Object2ObjectLinkedOpenHashMap<>()); // CraftBukkit
2020-10-01 16:52:27 +02:00
}
2020-12-01 03:08:41 +01:00
@@ -127,15 +171,26 @@ public class CraftingManager extends ResourceDataJson {
2020-10-08 13:04:22 +02:00
}
public Optional<? extends IRecipe<?>> getRecipe(MinecraftKey minecraftkey) {
+ // Yatopia start - replace stream
+ /*
return this.recipes.values().stream().map((map) -> {
return map.get(minecraftkey); // CraftBukkit - decompile error
}).filter(Objects::nonNull).findFirst();
+ */
+ for (Map<MinecraftKey, IRecipe<?>> map : recipes.values()) {
+ IRecipe<?> recipe = map.get(minecraftkey);
+ if (recipe != null) {
+ return Optional.of(recipe);
+ }
+ }
+ return Optional.empty();
+ // Yatopia end
}
public Collection<IRecipe<?>> b() {
2020-10-10 22:55:14 +02:00
- return (Collection) this.recipes.values().stream().flatMap((map) -> {
- return map.values().stream();
- }).collect(Collectors.toSet());
+ // Yatopia start - O(1) constant complexity
+ return ALL_RECIPES_CACHE;
2020-10-08 13:04:22 +02:00
+ // Yatopia end
}
2020-10-01 16:52:27 +02:00
2020-10-08 13:04:22 +02:00
public Stream<MinecraftKey> d() {
2020-12-01 03:08:41 +01:00
@@ -155,6 +210,10 @@ public class CraftingManager extends ResourceDataJson {
2020-10-10 22:55:14 +02:00
// CraftBukkit start
public void clearRecipes() {
this.recipes = Maps.newHashMap();
+ // Yatopia start - also clear cache
+ ALL_RECIPES_CACHE.clear();
+ TYPES_CACHE.clear();
+ // Yatopia end
for (Recipes<?> recipeType : IRegistry.RECIPE_TYPE) {
this.recipes.put(recipeType, new Object2ObjectLinkedOpenHashMap<>());
2020-12-01 03:08:41 +01:00
diff --git a/src/main/java/net/minecraft/server/ItemStack.java b/src/main/java/net/minecraft/server/ItemStack.java
index 3adb29f004d2fee36f3ee9b21ee5417e84b64837..376ab1d4c4f729639714b5eeedec514a2176531a 100644
--- a/src/main/java/net/minecraft/server/ItemStack.java
+++ b/src/main/java/net/minecraft/server/ItemStack.java
@@ -570,6 +570,7 @@ public final class ItemStack {
return !this.e() ? this.doMaterialsMatch(itemstack) : !itemstack.isEmpty() && this.getItem() == itemstack.getItem();
}
+ public final String getTranslationKey() { return j(); } // Yatopia - OBFHELPER
public String j() {
return this.getItem().f(this);
}