Merge pull request #823 from se7enAte9/menufixpt9000

menumanager: more menu fixes
This commit is contained in:
Tyler Bochard
2019-07-02 03:09:05 -04:00
committed by GitHub
2 changed files with 294 additions and 151 deletions

View File

@@ -27,28 +27,34 @@ package net.runelite.client.menus;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.collect.HashMultimap; import com.google.common.collect.HashMultimap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import lombok.AllArgsConstructor;
import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client; import net.runelite.api.Client;
import net.runelite.api.MenuAction; import net.runelite.api.MenuAction;
import static net.runelite.api.MenuAction.GAME_OBJECT_FIRST_OPTION; import static net.runelite.api.MenuAction.MENU_ACTION_DEPRIORITIZE_OFFSET;
import static net.runelite.api.MenuAction.WIDGET_DEFAULT;
import net.runelite.api.MenuEntry; import net.runelite.api.MenuEntry;
import net.runelite.api.NPCDefinition; import net.runelite.api.NPCDefinition;
import net.runelite.api.events.ClientTick; import net.runelite.api.events.BeforeRender;
import net.runelite.api.events.MenuEntryAdded; import net.runelite.api.events.MenuEntryAdded;
import net.runelite.api.events.MenuOpened;
import net.runelite.api.events.MenuOptionClicked; import net.runelite.api.events.MenuOptionClicked;
import net.runelite.api.events.NpcActionChanged; import net.runelite.api.events.NpcActionChanged;
import net.runelite.api.events.PlayerMenuOptionClicked; import net.runelite.api.events.PlayerMenuOptionClicked;
@@ -70,21 +76,9 @@ public class MenuManager
private static final int IDX_UPPER = 8; private static final int IDX_UPPER = 8;
static final Pattern LEVEL_PATTERN = Pattern.compile("\\(level-[0-9]*\\)"); static final Pattern LEVEL_PATTERN = Pattern.compile("\\(level-[0-9]*\\)");
private static MenuEntry CANCEL()
{
MenuEntry cancel = new MenuEntry();
cancel.setOption("Cancel");
cancel.setTarget("");
cancel.setIdentifier(0);
cancel.setType(MenuAction.CANCEL.getId());
cancel.setParam0(0);
cancel.setParam1(0);
return cancel;
}
private final Client client; private final Client client;
private final EventBus eventBus; private final EventBus eventBus;
private final Prioritizer prioritizer;
//Maps the indexes that are being used to the menu option. //Maps the indexes that are being used to the menu option.
private final Map<Integer, String> playerMenuIndexMap = new HashMap<>(); private final Map<Integer, String> playerMenuIndexMap = new HashMap<>();
@@ -95,15 +89,21 @@ public class MenuManager
private final Set<ComparableEntry> priorityEntries = new HashSet<>(); private final Set<ComparableEntry> priorityEntries = new HashSet<>();
private final Set<MenuEntry> currentPriorityEntries = new HashSet<>(); private final Set<MenuEntry> currentPriorityEntries = new HashSet<>();
private final Set<ComparableEntry> hiddenEntries = new HashSet<>(); private final Set<ComparableEntry> hiddenEntries = new HashSet<>();
private final Set<MenuEntry> currentHiddenEntries = new HashSet<>();
private final Map<ComparableEntry, ComparableEntry> swaps = new HashMap<>(); private final Map<ComparableEntry, ComparableEntry> swaps = new HashMap<>();
private EntryTypeMapping originalType; private final Map<ComparableEntry, MenuEntry> currentSwaps = new HashMap<>();
private final LinkedHashSet<MenuEntry> entries = Sets.newLinkedHashSet();
private MenuEntry leftClickEntry = null;
private int leftClickType = -1;
@Inject @Inject
private MenuManager(Client client, EventBus eventBus) private MenuManager(Client client, EventBus eventBus)
{ {
this.client = client; this.client = client;
this.eventBus = eventBus; this.eventBus = eventBus;
this.prioritizer = new Prioritizer();
} }
/** /**
@@ -143,6 +143,121 @@ public class MenuManager
return false; return false;
} }
@Subscribe
public void onMenuOpened(MenuOpened event)
{
currentPriorityEntries.clear();
currentHiddenEntries.clear();
// Need to reorder the list to normal, then rebuild with swaps
MenuEntry[] oldEntries = event.getMenuEntries();
for (MenuEntry entry : oldEntries)
{
if (entry == leftClickEntry)
{
entry.setType(leftClickType);
break;
}
}
leftClickEntry = null;
leftClickType = -1;
client.sortMenuEntries();
List<MenuEntry> newEntries = Lists.newArrayList(oldEntries);
boolean shouldDeprioritize = false;
prioritizer: for (MenuEntry entry : oldEntries)
{
// Remove hidden entries from menus
for (ComparableEntry p : hiddenEntries)
{
if (p.matches(entry))
{
newEntries.remove(entry);
continue prioritizer;
}
}
for (ComparableEntry p : priorityEntries)
{
// Create list of priority entries, and remove from menus
if (p.matches(entry))
{
// Other entries need to be deprioritized if their types are lower than 1000
if (entry.getType() >= 1000 && !shouldDeprioritize)
{
shouldDeprioritize = true;
}
currentPriorityEntries.add(entry);
newEntries.remove(entry);
continue prioritizer;
}
}
if (newEntries.size() > 0)
{
// Swap first matching entry to top
for (ComparableEntry src : swaps.keySet())
{
if (!src.matches(entry))
{
continue;
}
MenuEntry swapFrom = null;
ComparableEntry from = swaps.get(src);
for (MenuEntry e : newEntries)
{
if (from.matches(e))
{
swapFrom = e;
break;
}
}
// Do not need to swap with itself
if (swapFrom != null && swapFrom != entry)
{
// Deprioritize entries if the swaps are not in similar type groups
if ((swapFrom.getType() >= 1000 && entry.getType() < 1000) || (entry.getType() >= 1000 && swapFrom.getType() < 1000) && !shouldDeprioritize)
{
shouldDeprioritize = true;
}
int indexFrom = newEntries.indexOf(swapFrom);
int indexTo = newEntries.indexOf(entry);
Collections.swap(newEntries, indexFrom, indexTo);
}
}
}
}
if (shouldDeprioritize)
{
for (MenuEntry entry : newEntries)
{
if (entry.getType() <= MENU_ACTION_DEPRIORITIZE_OFFSET)
{
entry.setType(entry.getType() + MENU_ACTION_DEPRIORITIZE_OFFSET);
}
}
}
if (!priorityEntries.isEmpty())
{
newEntries.addAll(currentPriorityEntries);
}
event.setMenuEntries(newEntries.toArray(new MenuEntry[0]));
}
@Subscribe @Subscribe
public void onMenuEntryAdded(MenuEntryAdded event) public void onMenuEntryAdded(MenuEntryAdded event)
{ {
@@ -167,116 +282,78 @@ public class MenuManager
} }
} }
@Subscribe @Subscribe
private void onClientTick(ClientTick event) public void onBeforeRender(BeforeRender event)
{ {
originalType = null; leftClickEntry = null;
leftClickType = -1;
if (client.isMenuOpen())
{
return;
}
entries.clear();
entries.addAll(Arrays.asList(client.getMenuEntries()));
if (entries.size() < 2)
{
return;
}
currentPriorityEntries.clear(); currentPriorityEntries.clear();
client.sortMenuEntries(); currentHiddenEntries.clear();
currentSwaps.clear();
MenuEntry[] oldEntries = client.getMenuEntries(); prioritizer.prioritize();
List<MenuEntry> newEntries = Lists.newArrayList(oldEntries);
for (MenuEntry entry : oldEntries) while (prioritizer.isRunning())
{ {
for (ComparableEntry p : priorityEntries) // wait
}
entries.removeAll(currentHiddenEntries);
for (MenuEntry entry : currentPriorityEntries)
{
if (entries.contains(entry))
{ {
if (p.matches(entry)) leftClickEntry = entry;
{ leftClickType = entry.getType();
currentPriorityEntries.add(entry); entries.remove(leftClickEntry);
} leftClickEntry.setType(MenuAction.WIDGET_DEFAULT.getId());
} entries.add(leftClickEntry);
break;
// If there are entries we want to prioritize, we have to remove the rest
if (!currentPriorityEntries.isEmpty() && !client.isMenuOpen())
{
newEntries.retainAll(currentPriorityEntries);
// This is because players existing changes walk-here target
// so without this we lose track of em
if (newEntries.size() != currentPriorityEntries.size())
{
for (MenuEntry e : currentPriorityEntries)
{
if (newEntries.contains(e))
{
continue;
}
for (MenuEntry e2 : client.getMenuEntries())
{
if (e.getType() == e2.getType())
{
e.setTarget(e2.getTarget());
newEntries.add(e);
}
}
}
}
}
boolean isHidden = false;
for (ComparableEntry p : hiddenEntries)
{
if (p.matches(entry))
{
isHidden = true;
break;
}
}
if (isHidden)
{
newEntries.remove(entry);
} }
} }
if (!currentPriorityEntries.isEmpty() && !client.isMenuOpen())
if (leftClickEntry == null)
{ {
newEntries.add(0, CANCEL()); MenuEntry first = Iterables.getLast(entries);
}
MenuEntry leftClickEntry = newEntries.get(newEntries.size() - 1); for (ComparableEntry swap : currentSwaps.keySet())
for (ComparableEntry src : swaps.keySet())
{
if (!src.matches(leftClickEntry))
{ {
continue; if (swap.matches(first))
}
ComparableEntry tgt = swaps.get(src);
for (int i = newEntries.size() - 2; i > 0; i--)
{
MenuEntry e = newEntries.get(i);
if (tgt.matches(e))
{ {
newEntries.set(newEntries.size() - 1, e); leftClickEntry = currentSwaps.get(swap);
newEntries.set(i, leftClickEntry); leftClickType = leftClickEntry.getType();
entries.remove(leftClickEntry);
int type = e.getType(); leftClickEntry.setType(MenuAction.WIDGET_DEFAULT.getId());
entries.add(leftClickEntry);
if (type >= 1000)
{
int newType = getLeftClickType(type);
if (newType != -1 && newType != type)
{
MenuEntry original = MenuEntry.copy(e);
e.setType(newType);
originalType = new EntryTypeMapping(new ComparableEntry(leftClickEntry), original);
}
}
break; break;
} }
} }
} }
client.setMenuEntries(newEntries.toArray(new MenuEntry[0])); client.setMenuEntries(entries.toArray(new MenuEntry[0]));
} }
public void addPlayerMenuItem(String menuText) public void addPlayerMenuItem(String menuText)
{ {
Preconditions.checkNotNull(menuText); Preconditions.checkNotNull(menuText);
@@ -338,24 +415,6 @@ public class MenuManager
} }
} }
private int getLeftClickType(int oldType)
{
if (oldType > 2000)
{
oldType -= 2000;
}
switch (MenuAction.of(oldType))
{
case GAME_OBJECT_FIFTH_OPTION:
return GAME_OBJECT_FIRST_OPTION.getId();
case EXAMINE_ITEM_BANK_EQ:
return WIDGET_DEFAULT.getId();
default:
return oldType;
}
}
private void addNpcOption(NPCDefinition composition, String npcOption) private void addNpcOption(NPCDefinition composition, String npcOption)
{ {
String[] actions = composition.getActions(); String[] actions = composition.getActions();
@@ -399,21 +458,11 @@ public class MenuManager
@Subscribe @Subscribe
public void onMenuOptionClicked(MenuOptionClicked event) public void onMenuOptionClicked(MenuOptionClicked event)
{ {
// Type is changed in check if (leftClickEntry != null && leftClickType != -1)
if (originalType != null && originalType.check(event))
{ {
event.consume(); leftClickEntry.setType(leftClickType);
event.setMenuEntry(leftClickEntry);
client.invokeMenuAction( leftClickEntry = null;
event.getActionParam0(),
event.getActionParam1(),
event.getType(),
event.getIdentifier(),
"do not edit",
event.getTarget(),
client.getMouseCanvasPosition().getX(),
client.getMouseCanvasPosition().getY()
);
} }
if (event.getMenuAction() != MenuAction.RUNELITE) if (event.getMenuAction() != MenuAction.RUNELITE)
@@ -740,23 +789,115 @@ public class MenuManager
hiddenEntries.remove(entry); hiddenEntries.remove(entry);
} }
@AllArgsConstructor private class Prioritizer
private class EntryTypeMapping
{ {
private final ComparableEntry comparable; private MenuEntry[] entries;
private final MenuEntry target; private AtomicInteger state = new AtomicInteger(0);
private boolean check(MenuOptionClicked event) boolean isRunning()
{ {
MenuEntry entry = event.getMenuEntry(); return state.get() != 0;
}
if (event.getTarget().equals("do not edit") || !comparable.matches(entry)) void prioritize()
{
if (state.get() != 0)
{ {
return false; return;
} }
event.setMenuEntry(target); entries = client.getMenuEntries();
return true;
state.set(3);
if (!hiddenEntries.isEmpty())
{
hiddenFinder.run();
}
else
{
state.decrementAndGet();
}
if (!priorityEntries.isEmpty())
{
priorityFinder.run();
}
else
{
state.decrementAndGet();
}
if (!swaps.isEmpty())
{
swapFinder.run();
}
else
{
state.decrementAndGet();
}
} }
private Thread hiddenFinder = new Thread()
{
@Override
public void run()
{
Arrays.stream(entries).parallel().forEach(entry ->
{
for (ComparableEntry p : hiddenEntries)
{
if (p.matches(entry))
{
currentHiddenEntries.add(entry);
return;
}
}
});
state.decrementAndGet();
}
};
private Thread priorityFinder = new Thread()
{
@Override
public void run()
{
Arrays.stream(entries).parallel().forEach(entry ->
{
for (ComparableEntry p : priorityEntries)
{
if (p.matches(entry))
{
currentPriorityEntries.add(entry);
return;
}
}
});
state.decrementAndGet();
}
};
private Thread swapFinder = new Thread()
{
@Override
public void run()
{
Arrays.stream(entries).parallel().forEach(entry ->
{
for (Map.Entry<ComparableEntry, ComparableEntry> p : swaps.entrySet())
{
if (p.getValue().matches(entry))
{
currentSwaps.put(p.getKey(), entry);
return;
}
}
});
state.decrementAndGet();
}
};
} }
} }

View File

@@ -1264,7 +1264,8 @@ public abstract class RSClientMixin implements RSClient
return; return;
} }
rs$menuAction(actionParam, widgetId, menuAction, id, menuOption, menuTarget, var6, var7); rs$menuAction(menuOptionClicked.getActionParam0(), menuOptionClicked.getActionParam1(), menuOptionClicked.getType(),
menuOptionClicked.getIdentifier(), menuOptionClicked.getOption(), menuOptionClicked.getTarget(), var6, var7);
} }
@FieldHook("Login_username") @FieldHook("Login_username")
@@ -1303,6 +1304,7 @@ public abstract class RSClientMixin implements RSClient
final MenuOpened event = new MenuOpened(); final MenuOpened event = new MenuOpened();
event.setMenuEntries(getMenuEntries()); event.setMenuEntries(getMenuEntries());
callbacks.post(event); callbacks.post(event);
client.setMenuEntries(event.getMenuEntries());
} }
@Inject @Inject