diff --git a/src/codechicken/nei/ItemList.java b/src/codechicken/nei/ItemList.java index b1588855..c8131d78 100644 --- a/src/codechicken/nei/ItemList.java +++ b/src/codechicken/nei/ItemList.java @@ -98,12 +98,8 @@ public class ItemList return filters; } - private static class ThreadLoadItems extends Thread + public static final RestartableTask loadItems = new RestartableTask("NEI Item Loading") { - public ThreadLoadItems() { - super("NEI Item Loading"); - } - private void damageSearch(Item item, List permutations) { HashSet damageIconSet = new HashSet(); for (int damage = 0; damage < 16; damage++) @@ -126,99 +122,84 @@ public class ItemList } @Override - public void run() { - ThreadOperationTimer timer = ThreadOperationTimer.start(this, 500); - restart: - do { - reload = false; - LinkedList items = new LinkedList(); - LinkedList permutations = new LinkedList(); - ListMultimap itemMap = ArrayListMultimap.create(); + public void execute() { + ThreadOperationTimer timer = getTimer(500); - timer.setLimit(500); - for (Item item : (Iterable) Item.itemRegistry) { - if (reload) - continue restart; + LinkedList items = new LinkedList(); + LinkedList permutations = new LinkedList(); + ListMultimap itemMap = ArrayListMultimap.create(); - if (item == null || erroredItems.contains(item)) - continue; + timer.setLimit(500); + for (Item item : (Iterable) Item.itemRegistry) { + if (interrupted()) return; - try { - timer.reset(item); + if (item == null || erroredItems.contains(item)) + continue; - permutations.clear(); - permutations.addAll(ItemInfo.getItemOverrides(item)); - if (permutations.isEmpty()) - item.getSubItems(item, null, permutations); + try { + timer.reset(item); - if (permutations.isEmpty()) - damageSearch(item, permutations); + permutations.clear(); + permutations.addAll(ItemInfo.getItemOverrides(item)); + if (permutations.isEmpty()) + item.getSubItems(item, null, permutations); - timer.reset(); + if (permutations.isEmpty()) + damageSearch(item, permutations); - items.addAll(permutations); - itemMap.putAll(item, permutations); - } catch (Throwable t) { - NEIServerConfig.logger.error("Removing item: " + item + " from list.", t); - erroredItems.add(item); - } + timer.reset(); + + items.addAll(permutations); + itemMap.putAll(item, permutations); + } catch (Throwable t) { + NEIServerConfig.logger.error("Removing item: " + item + " from list.", t); + erroredItems.add(item); } - - ItemList.items = items; - ItemList.itemMap = itemMap; - for(ItemsLoadedCallback callback : loadCallbacks) - callback.itemsLoaded(); - - updateFilter(); } - while (reload); - loading = false; - } - } - public static class ThreadFilterItems extends Thread { - public ThreadFilterItems() { - super("NEI Item Filtering"); - } + if(interrupted()) return; + ItemList.items = items; + ItemList.itemMap = itemMap; + for(ItemsLoadedCallback callback : loadCallbacks) + callback.itemsLoaded(); + updateFilter.restart(); + } + }; + + public static final RestartableTask updateFilter = new RestartableTask("NEI Item Filtering") + { @Override - public void run() { - restart: - do { - refilter = false; - ArrayList filtered = new ArrayList(); - List filters = getItemFilters(); - for(ItemStack item : items) { - if(refilter) - continue restart; + public void execute() { + ArrayList filtered = new ArrayList(); + List filters = getItemFilters(); + for(ItemStack item : items) { + if (interrupted()) return; - if(itemMatches(item, filters)) - filtered.add(item); - } - - ItemSorter.sort(filtered); - ItemPanel.updateItemList(filtered); + if(itemMatches(item, filters)) + filtered.add(item); } - while(refilter); - filtering = false; - } - } + if(interrupted()) return; + ItemSorter.sort(filtered); + if(interrupted()) return; + ItemPanel.updateItemList(filtered); + } + }; + + /** + * @deprecated Use updateFilter.restart() + */ + @Deprecated public static void updateFilter() { - if (filtering) - refilter = true; - else { - filtering = true; - new ThreadFilterItems().start(); - } + updateFilter.restart(); } + /** + * @deprecated Use loadItems.restart() + */ + @Deprecated public static void loadItems() { - if (loading) - reload = true; - else { - loading = true; - new ThreadLoadItems().start(); - } + loadItems.restart(); } } diff --git a/src/codechicken/nei/LayoutManager.java b/src/codechicken/nei/LayoutManager.java index ed86fb2e..62c10b3c 100644 --- a/src/codechicken/nei/LayoutManager.java +++ b/src/codechicken/nei/LayoutManager.java @@ -564,7 +564,7 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH if (isEnabled()) { setInputFocused(null); - ItemList.loadItems(); + ItemList.loadItems.restart(); overlayRenderer = null; getLayoutStyle().init(); diff --git a/src/codechicken/nei/RestartableTask.java b/src/codechicken/nei/RestartableTask.java new file mode 100644 index 00000000..b8b8ef67 --- /dev/null +++ b/src/codechicken/nei/RestartableTask.java @@ -0,0 +1,75 @@ +package codechicken.nei; + +public abstract class RestartableTask +{ + public final String name; + private Thread thread; + private volatile boolean restart; + private volatile boolean stopped; + + private ThreadOperationTimer timer; + + public RestartableTask(String name) { + this.name = name; + } + + private void start() { + thread = new Thread(name) { + @Override + public void run() { + do { + while(stopped) + try { Thread.sleep(1); } catch (InterruptedException ignored) {} + if(!restart) + execute(); + } while(!finish()); + } + }; + thread.start(); + } + + private synchronized boolean finish() { + if(restart) { + restart = false; + return false; + } else { + clearTasks(); + return true; + } + } + + public void clearTasks() { + thread = null; + timer = null; + } + + public synchronized void restart() { + if(thread != null) { + stopped = false; + restart = true; + } + else + start(); + } + + /** + * Stops execution until restart is called. If a thread is running, the restart flag will be set, and the thread will be paused once the execute method exits. + */ + public synchronized void stop() { + if(thread != null) { + stopped = true; + restart = true; + } + } + + public boolean interrupted() { + return restart; + } + + public synchronized ThreadOperationTimer getTimer(int timeout) { + if (timer == null) timer = ThreadOperationTimer.start(thread, timeout); + return timer; + } + + public abstract void execute(); +} diff --git a/src/codechicken/nei/SearchField.java b/src/codechicken/nei/SearchField.java index a4429413..f21f6869 100644 --- a/src/codechicken/nei/SearchField.java +++ b/src/codechicken/nei/SearchField.java @@ -66,7 +66,7 @@ public class SearchField extends TextField implements ItemFilterProvider @Override public void onTextChange(String oldText) { NEIClientConfig.setSearchExpression(text()); - ItemList.updateFilter(); + ItemList.updateFilter.restart(); } @Override diff --git a/src/codechicken/nei/SubsetWidget.java b/src/codechicken/nei/SubsetWidget.java index 41080c9c..e962d1de 100644 --- a/src/codechicken/nei/SubsetWidget.java +++ b/src/codechicken/nei/SubsetWidget.java @@ -2,7 +2,6 @@ package codechicken.nei; import codechicken.core.gui.GuiScrollSlot; import codechicken.lib.gui.GuiDraw; -import codechicken.lib.inventory.InventoryUtils; import codechicken.lib.vec.Rectangle4i; import codechicken.nei.ItemList.ItemsLoadedCallback; import codechicken.nei.ItemList.NothingItemFilter; @@ -20,6 +19,7 @@ import net.minecraft.util.EnumChatFormatting; import java.awt.*; import java.util.*; import java.util.List; +import java.util.concurrent.atomic.AtomicReference; public class SubsetWidget extends Button implements ItemFilterProvider, ItemsLoadedCallback, ISearchProvider { @@ -373,7 +373,7 @@ public class SubsetWidget extends Button implements ItemFilterProvider, ItemsLoa * All operations on this variable should be synchronised. */ private static final ItemStackSet hiddenItems = new ItemStackSet(); - private static boolean hiddenItemsDirty = false; + private static final AtomicReference dirtyHiddenItems = new AtomicReference(); public static SubsetState getState(SubsetTag tag) { SubsetState state = subsetState.get(tag.fullname); @@ -381,9 +381,10 @@ public class SubsetWidget extends Button implements ItemFilterProvider, ItemsLoa } public static void addTag(SubsetTag tag) { + updateState.stop(); synchronized (root) { root.addTag(tag); - updateState(true); + updateState.reallocate(); } } @@ -409,7 +410,6 @@ public class SubsetWidget extends Button implements ItemFilterProvider, ItemsLoa hiddenItems.add(item); else hiddenItems.remove(item); - hiddenItemsDirty = true; } public static void showOnly(SubsetTag tag) { @@ -423,22 +423,167 @@ public class SubsetWidget extends Button implements ItemFilterProvider, ItemsLoa public static void setHidden(SubsetTag tag, boolean hidden) { synchronized (hiddenItems) { _setHidden(tag, hidden); - updateState(false); + updateHiddenItems(); } } public static void setHidden(ItemStack item, boolean hidden) { synchronized (hiddenItems) { _setHidden(item, hidden); - updateState(false); + updateHiddenItems(); } } public static void unhideAll() { synchronized (hiddenItems) { hiddenItems.clear(); - hiddenItemsDirty = true; - updateState(false); + updateHiddenItems(); + } + } + + private static void updateHiddenItems() { + prepareDirtyHiddenItems.restart(); + updateState.restart(); + } + + public static void loadHidden() { + synchronized (hiddenItems) { + hiddenItems.clear(); + } + + List itemList = new LinkedList(); + try { + NBTTagList list = NEIClientConfig.world.nbt.getTagList("hiddenItems", 10); + for(int i = 0; i < list.tagCount(); i++) + itemList.add(ItemStack.loadItemStackFromNBT(list.getCompoundTagAt(i))); + } + catch (Exception e) { + NEIClientConfig.logger.error("Error loading hiddenItems", e); + return; + } + + synchronized (hiddenItems) { + for(ItemStack item : itemList) + hiddenItems.add(item); + } + updateState.restart(); + } + + private static void saveHidden() { + NBTTagList list = dirtyHiddenItems.getAndSet(null); + if(list != null) { + NEIClientConfig.world.nbt.setTag("hiddenItems", list); + NEIClientConfig.world.saveNBT(); + } + } + + private static final RestartableTask prepareDirtyHiddenItems = new RestartableTask("NEI Subset Save Thread") + { + private List getList() { + synchronized (hiddenItems) { + return hiddenItems.values(); + } + } + + @Override + public void execute() { + NBTTagList list = new NBTTagList(); + for(ItemStack item : getList()) { + if(interrupted()) return; + NBTTagCompound tag = new NBTTagCompound(); + item.writeToNBT(tag); + list.appendTag(tag); + } + dirtyHiddenItems.set(list); + } + }; + + private static final UpdateStateTask updateState = new UpdateStateTask(); + private static class UpdateStateTask extends RestartableTask + { + private volatile boolean reallocate; + + public UpdateStateTask() { + super("NEI Subset Item Allocation"); + } + + @Override + public void clearTasks() { + super.clearTasks(); + reallocate = false; + } + + public synchronized void reallocate() { + reallocate = true; + restart(); + } + + @Override + public void execute() { + HashMap state = new HashMap(); + List tags = new LinkedList(); + synchronized (root) { + cloneStates(root, tags, state); + if(interrupted()) return; + } + + if(reallocate) { + for (ItemStack item : ItemList.items) { + if(interrupted()) return; + for (SubsetTag tag : tags) + if (tag.filter.matches(item)) + state.get(tag.fullname).items.add(item); + } + } + + synchronized (root) { + calculateVisibility(root, state); + if(interrupted()) return; + } + + subsetState = state; + ItemList.updateFilter.restart(); + } + + private void cloneStates(SubsetTag tag, List tags, HashMap state) { + for(SubsetTag child : tag.sorted) { + if(interrupted()) return; + cloneStates(child, tags, state); + } + + tags.add(tag); + SubsetState sstate = new SubsetState(); + if(!reallocate) + sstate.items = SubsetWidget.getState(tag).items; + state.put(tag.fullname, sstate); + } + + private void calculateVisibility(SubsetTag tag, Map state) { + SubsetState sstate = state.get(tag.fullname); + int hidden = 0; + for(SubsetTag child : tag.sorted) { + if(interrupted()) return; + calculateVisibility(child, state); + int cstate = state.get(child.fullname).state; + if(cstate == 1) + sstate.state = 1; + else if(cstate == 0) + hidden++; + } + + if(sstate.state == 1) return; + + List items = sstate.items; + for(ItemStack item : items) { + if(interrupted()) return; + if (isHidden(item)) + hidden++; + } + + if(hidden == tag.sorted.size()+items.size()) + sstate.state = 0; + else if(hidden > 0) + sstate.state = 1; } } @@ -568,133 +713,6 @@ public class SubsetWidget extends Button implements ItemFilterProvider, ItemsLoa @Override public void itemsLoaded() { - updateState(true); - } - - private static boolean updatingState; - private static boolean reupdate; - private static boolean reallocate; - - private static class ThreadSubsetItems extends Thread - { - public ThreadSubsetItems() { - super("NEI Subset Item Allocation"); - } - - @Override - public void run() { - restart: - do { - reupdate = false; - HashMap state = new HashMap(); - List tags = new LinkedList(); - synchronized (root) { - cloneStates(root, tags, state); - } - - if(reallocate) { - for (ItemStack item : ItemList.items) { - if (reupdate) - continue restart; - - for (SubsetTag tag : tags) - if (tag.filter.matches(item)) - state.get(tag.fullname).items.add(item); - } - } - - synchronized (root) { - if (reupdate) - continue; - reallocate = false; - - calculateVisibility(root, state); - } - - subsetState = state; - } - while(reupdate); - updatingState = false; - } - - private void cloneStates(SubsetTag tag, List tags, HashMap state) { - for(SubsetTag child : tag.sorted) - cloneStates(child, tags, state); - - tags.add(tag); - SubsetState sstate = new SubsetState(); - if(!reallocate) - sstate.items = SubsetWidget.getState(tag).items; - state.put(tag.fullname, sstate); - } - - private void calculateVisibility(SubsetTag tag, Map state) { - SubsetState sstate = state.get(tag.fullname); - int hidden = 0; - for(SubsetTag child : tag.sorted) { - calculateVisibility(child, state); - int cstate = state.get(child.fullname).state; - if(cstate == 1) - sstate.state = 1; - else if(cstate == 0) - hidden++; - } - - if(sstate.state == 1) - return; - - List items = sstate.items; - for(ItemStack item : items) - if(isHidden(item)) - hidden++; - if(hidden == tag.sorted.size()+items.size()) - sstate.state = 0; - else if(hidden > 0) - sstate.state = 1; - } - } - - public static void updateState(boolean reallocate) { - SubsetWidget.reallocate = reallocate; - if (updatingState) - reupdate = true; - else { - updatingState = true; - new ThreadSubsetItems().start(); - } - ItemList.updateFilter(); - } - - public static void loadHidden() { - synchronized (hiddenItems) { - hiddenItems.clear(); - try { - NBTTagList list = NEIClientConfig.world.nbt.getTagList("hiddenItems", 10); - for(int i = 0; i < list.tagCount(); i++) - hiddenItems.add(ItemStack.loadItemStackFromNBT(list.getCompoundTagAt(i))); - } - catch (Exception e) { - hiddenItems.clear(); - NEIClientConfig.logger.error("Error loading hiddenItems", e); - } - } - } - - private static void saveHidden() { - if(!hiddenItemsDirty) - return; - - synchronized (hiddenItems) { - NBTTagList list = new NBTTagList(); - for(ItemStack item : hiddenItems.values()) { - NBTTagCompound tag = new NBTTagCompound(); - item.writeToNBT(tag); - list.appendTag(tag); - } - NEIClientConfig.world.nbt.setTag("hiddenItems", list); - NEIClientConfig.world.saveNBT(); - - hiddenItemsDirty = false; - } + updateState.reallocate(); } }