diff --git a/src/main/java/org/ultramine/server/RecipeCache.java b/src/main/java/org/ultramine/server/RecipeCache.java index 07ec78d..73beefa 100644 --- a/src/main/java/org/ultramine/server/RecipeCache.java +++ b/src/main/java/org/ultramine/server/RecipeCache.java @@ -1,78 +1,75 @@ package org.ultramine.server; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; -import java.util.List; import java.util.Map; -import java.util.Set; +import java.util.Optional; +import cpw.mods.fml.common.FMLCommonHandler; +import cpw.mods.fml.common.eventhandler.SubscribeEvent; +import cpw.mods.fml.common.gameevent.TickEvent; import net.minecraft.inventory.InventoryCrafting; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.item.crafting.CraftingManager; import net.minecraft.item.crafting.IRecipe; +import net.minecraft.server.MinecraftServer; import net.minecraft.world.World; +import org.ultramine.server.util.ModificationControlList; + +import javax.annotation.Nullable; public class RecipeCache { - private final List originList; - private final Map cache = new HashMap(); - private final Set noRecipeSet = new HashSet(); + private static final int CACHE_SIZE = 12287; + private static final int CLEANUP_INTERVAL = 20*10; + private final ModificationControlList originList; + private final Map> cache = new HashMap<>(); private boolean enabled; - + @SuppressWarnings("unchecked") public RecipeCache() { - originList = CraftingManager.getInstance().getRecipeList(); + CraftingManager craftMgr = CraftingManager.getInstance(); + craftMgr.recipes = originList = new ModificationControlList<>(craftMgr.getRecipeList()); } - + public void setEnabled(boolean enabled) { this.enabled = enabled; } - - public IRecipe findRecipe(InventoryCrafting inv, World world) + + public @Nullable IRecipe findRecipe(InventoryCrafting inv, World world) { if(!enabled) return originalSearch(inv, world); RecipeKey key = new RecipeKeyBuilder(inv).build(); if(key.width == 0) return null; - - IRecipe rcp = cache.get(key); - if (rcp != null && rcp.matches(inv, world)) + + Optional rcp = cache.get(key); + if (rcp != null) { - return rcp; + if(!rcp.isPresent()) + return null; + if(rcp.get().matches(inv, world)) + return rcp.get(); } - else if(noRecipeSet.contains(key)) - { - return null; - } - else - { - IRecipe recipe = originalSearch(inv, world); - if(recipe != null) - { - addToCache(key, recipe); - return recipe; - } - } - - if(noRecipeSet.size() >= 1048576) - noRecipeSet.clear(); - noRecipeSet.add(key); - return null; + + IRecipe recipe = originalSearch(inv, world); + addToCache(key, recipe); + return recipe; } - - private void addToCache(RecipeKey key, IRecipe recipe) + + private void addToCache(RecipeKey key, @Nullable IRecipe recipe) { - if(cache.size() >= 1048576) + if(cache.size() >= CACHE_SIZE) cache.clear(); - cache.put(key, recipe); + cache.put(key, Optional.ofNullable(recipe)); } - - private IRecipe originalSearch(InventoryCrafting inv, World world) + + private @Nullable IRecipe originalSearch(InventoryCrafting inv, World world) { for(IRecipe recipe : originList) { @@ -81,14 +78,28 @@ return recipe; } } - + return null; } - + public void clearCache() { cache.clear(); - noRecipeSet.clear(); + } + + @SubscribeEvent + public void onTick(TickEvent.ServerTickEvent e) + { + if(e.phase != TickEvent.Phase.END) + return; + if(originList.checkModifiedAndReset()) + { + clearCache(); + } + else if(MinecraftServer.getServer().getTickCounter() % CLEANUP_INTERVAL == 0) + { + cache.values().removeAll(Collections.singleton(Optional.empty())); + } } private static class RecipeKey implements Comparable @@ -102,22 +113,22 @@ this.width = width; } + @Override public boolean equals(Object o) { - if(!(o instanceof RecipeKey)) + if(o == null || o.getClass() != RecipeKey.class) return false; RecipeKey rk = (RecipeKey)o; return width == rk.width && Arrays.equals(contents, rk.contents); } - + + @Override public int hashCode() { - int hash = 0; - for(int i = 0; i < contents.length; i++) - hash ^= contents[i]; - return hash; + return Arrays.hashCode(contents) ^ width; } - + + @Override public int compareTo(RecipeKey rk) { int c1 = width - rk.width; @@ -139,6 +150,7 @@ private static class RecipeKeyBuilder { + private static final int[] EMPTY_INT_ARRAY = new int[0]; private int[] contents; private int x; private int y; @@ -154,7 +166,7 @@ { ItemStack is = inv.getStackInSlot(i); if(is != null) - contents[i] = (Item.getIdFromItem(is.getItem()) << 16) | (is.getItemDamage() & 0xFFFF); + contents[i] = (is.getItemDamage() << 16) | Item.getIdFromItem(is.getItem()); } newWidth = width = inv.getWidth(); newHeight = height = contents.length/width; @@ -162,7 +174,7 @@ while(trimHorisontal(false)); if(y == height) { - contents = new int[0]; + contents = EMPTY_INT_ARRAY; newWidth = 0; } else diff --git a/src/main/java/org/ultramine/server/UMEventHandler.java b/src/main/java/org/ultramine/server/UMEventHandler.java index 5aa89f7..c49fbb9 100644 --- a/src/main/java/org/ultramine/server/UMEventHandler.java +++ b/src/main/java/org/ultramine/server/UMEventHandler.java @@ -325,16 +325,10 @@ e.holdings.setBalance(ConfigurationHandler.getServerConfig().tools.economy.startBalance); } - private boolean firstPlayer = true; @SideOnly(Side.SERVER) @SubscribeEvent(priority=EventPriority.LOWEST) public void onPlayerLoggedIn(PlayerLoggedInEvent e) { - if(firstPlayer) - { - firstPlayer = false; - UltramineServerModContainer.getInstance().getRecipeCache().clearCache(); - } ((EntityPlayerMP)e.player).getData().core().onLogin(); } } diff --git a/src/main/java/org/ultramine/server/UltramineServerModContainer.java b/src/main/java/org/ultramine/server/UltramineServerModContainer.java index f2c54f1..53b36cc 100644 --- a/src/main/java/org/ultramine/server/UltramineServerModContainer.java +++ b/src/main/java/org/ultramine/server/UltramineServerModContainer.java @@ -222,9 +222,11 @@ loader.addDefaultWarps(); for(String name : loader.getFastWarps()) reg.registerCommand(new FastWarpCommand(name)); - getRecipeCache().clearCache(); if(e.getSide().isServer()) + { getRecipeCache().setEnabled(ConfigurationHandler.getServerConfig().settings.other.recipeCacheEnabled); + FMLCommonHandler.instance().bus().register(getRecipeCache()); + } } catch (Throwable t) { @@ -257,6 +259,7 @@ { MinecraftForge.EVENT_BUS.post(new ForgeModIdMappingEvent(e)); FurnaceRecipes.smelting().remap(); + recipeCache.clearCache(); } @NetworkCheckHandler diff --git a/src/main/java/org/ultramine/server/util/ModificationControlList.java b/src/main/java/org/ultramine/server/util/ModificationControlList.java new file mode 100644 index 0000000..8b3e213 --- /dev/null +++ b/src/main/java/org/ultramine/server/util/ModificationControlList.java @@ -0,0 +1,45 @@ +package org.ultramine.server.util; + +import java.util.ArrayList; +import java.util.Collection; + +public class ModificationControlList extends ArrayList +{ + private int lastModCount; + + public ModificationControlList(int initialCapacity) + { + super(initialCapacity); + } + + public ModificationControlList() + { + } + + public ModificationControlList(Collection c) + { + super(c); + } + + public int getModCount() + { + return modCount; + } + + public boolean isModified() + { + return lastModCount != modCount; + } + + public void resetModified() + { + lastModCount = modCount; + } + + public boolean checkModifiedAndReset() + { + boolean isMod = isModified(); + resetModified(); + return isMod; + } +}