Yatopia/patches/server/0049-Heavily-optimize-recipe-lookups-in-CraftingManager.patch
Ivan Pekov 7f24790a35
Updated Upstream and Sidestream(s) (Tuinity/EMC/Purpur/AirplaneLite)
Upstream/An Sidestream has released updates that appears to apply and compile correctly
This update has NOT been tested by YatopiaMC and as with ANY update, please do your own testing.

Tuinity Changes:
cf37529 Updated Upstream (Paper)

EMC Changes:
27282006 Updated Paper
456edfa4 One more try to fix more chunks on reload

Purpur Changes:
45d5e9a Fix dolphin spit and phantom flames
9e1b8e7 Clean up teleport outside border patch Add sleeping, inventory, and toWorld checks
8428580 [ci-skip] Add 'fast' argument to './purpur jar' to speed up jenkins build time
cea486c Updated Upstream (Paper)
6cd244c Fix infinite furnaces triggering without lava source
a989361 Fix infinite furnaces leaving empty buckets
6cf1672 Add config option for whether Nether Wart farming Villagers should throw extra Nether Warts at other Villagers
07d4056 Add toggle for Zombified Piglin death always counting as a player kill when angry (Zombified Piglin XP farm nerf option)
9935466 Add config option for Cleric Villagers to farm Nether Wart
47a1707 fix typos
6e42ce8 Updated Upstream (Paper)
740b7d2 [ci-skip] Update readme with downloads API info

AirplaneLite Changes:
5526dde Add download link to readme
2020-12-07 10:10:55 +02:00

166 lines
7.5 KiB
Diff

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.
Co-authored-by: Ivan Pekov <ivan@mrivanplays.com>
diff --git a/src/main/java/net/minecraft/server/CraftingManager.java b/src/main/java/net/minecraft/server/CraftingManager.java
index 58ecbe1e20581dc9e78cdd2f4ece29cfa014da8a..3da86dc56f33e4f1900f6b4f66ca6696eaf6525a 100644
--- a/src/main/java/net/minecraft/server/CraftingManager.java
+++ b/src/main/java/net/minecraft/server/CraftingManager.java
@@ -31,6 +31,10 @@ public class CraftingManager extends ResourceDataJson {
private static final Logger LOGGER = LogManager.getLogger();
public Map<Recipes<?>, Object2ObjectLinkedOpenHashMap<MinecraftKey, IRecipe<?>>> recipes = ImmutableMap.of(); // CraftBukkit
private boolean d;
+ // Yatopia start
+ private static final List<IRecipe<?>> ALL_RECIPES_CACHE = new java.util.ArrayList<>();
+ private static final Map<Recipes<?>, List<IRecipe<?>>> TYPES_CACHE = new Object2ObjectLinkedOpenHashMap<>();
+ // Yatopia end
public CraftingManager() {
super(CraftingManager.a, "recipes");
@@ -63,9 +67,17 @@ public class CraftingManager extends ResourceDataJson {
}
}
+ // Yatopia start - nuke stream & cache all recipes for constant access in b()
+ /*
this.recipes = (Map) map1.entrySet().stream().collect(ImmutableMap.toImmutableMap(Entry::getKey, (entry1) -> {
return entry1.getValue(); // CraftBukkit // Paper - decompile fix - *shrugs internally*
}));
+ */
+ this.recipes = ImmutableMap.copyOf(map1);
+ for (Object2ObjectLinkedOpenHashMap<MinecraftKey, IRecipe<?>> recipesMap : map1.values()) {
+ ALL_RECIPES_CACHE.addAll(recipesMap.values());
+ }
+ // Yatopia end
CraftingManager.LOGGER.info("Loaded {} recipes", map1.size());
}
@@ -79,33 +91,65 @@ public class CraftingManager extends ResourceDataJson {
} else {
map.putAndMoveToFirst(irecipe.getKey(), irecipe); // CraftBukkit - SPIGOT-4638: last recipe gets priority
}
+ ALL_RECIPES_CACHE.add(irecipe); // Yatopia
}
// CraftBukkit end
public <C extends IInventory, T extends IRecipe<C>> Optional<T> craft(Recipes<T> recipes, C c0, World world) {
// CraftBukkit start
+ // Yatopia start - replace stream
+ /*
Optional<T> recipe = this.b(recipes).values().stream().flatMap((irecipe) -> {
return SystemUtils.a(recipes.a(irecipe, world, c0));
}).findFirst();
+ */
+ // 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;
+ }
+ }
+ // Yatopia end
c0.setCurrentRecipe(recipe.orElse(null)); // CraftBukkit - Clear recipe when no recipe is found
// CraftBukkit end
return recipe;
}
public <C extends IInventory, T extends IRecipe<C>> List<T> a(Recipes<T> recipes) {
+ // Yatopia start - replaced logic
+ /*
return (List) this.b(recipes).values().stream().map((irecipe) -> {
return irecipe;
}).collect(Collectors.toList());
+ */
+ return (List) TYPES_CACHE.computeIfAbsent(recipes, recipes1 -> new me.jellysquid.mods.lithium.common.util.collections.HashedList<>(new java.util.ArrayList<>(getRecipesMap(recipes).values())));
+ // Yatopia end
}
public <C extends IInventory, T extends IRecipe<C>> List<T> b(Recipes<T> recipes, C c0, World world) {
+ // 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();
})).collect(Collectors.toList());
+ */
+ 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
}
+ private <C extends IInventory, T extends IRecipe<C>> Map<MinecraftKey, IRecipe<C>> getRecipesMap(Recipes<T> recipes) { return b(recipes); } // Yatopia - OBFHELPER
private <C extends IInventory, T extends IRecipe<C>> Map<MinecraftKey, IRecipe<C>> b(Recipes<T> recipes) {
return (Map) this.recipes.getOrDefault(recipes, new Object2ObjectLinkedOpenHashMap<>()); // CraftBukkit
}
@@ -127,15 +171,26 @@ public class CraftingManager extends ResourceDataJson {
}
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() {
- 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;
+ // Yatopia end
}
public Stream<MinecraftKey> d() {
@@ -155,6 +210,10 @@ public class CraftingManager extends ResourceDataJson {
// 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<>());
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);
}