Hiscore plugin: lookup menu item (#22)

Hiscore plugin: lookup menu item
This commit is contained in:
RobinWeymans
2017-04-18 02:22:51 +02:00
committed by Adam
parent b0b1bfc780
commit 53f97ed90e
10 changed files with 406 additions and 3 deletions

View File

@@ -204,6 +204,26 @@ public class Client
return client.getWidgetPositionsY();
}
public String[] getPlayerOptions()
{
return client.getPlayerOptions();
}
public boolean[] getPlayerOptionsPriorities()
{
return client.getPlayerOptionsPriorities();
}
public int[] getPlayerMenuType()
{
return client.getPlayerMenuTypes();
}
public String[] getMenuOptions()
{
return client.getMenuOptions();
}
public int getMapScale()
{
return client.getMapScale();

View File

@@ -37,6 +37,7 @@ import javax.imageio.ImageIO;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import net.runelite.api.Client;
import net.runelite.client.menus.MenuManager;
import net.runelite.client.plugins.PluginManager;
import net.runelite.client.ui.ClientUI;
import net.runelite.client.ui.overlay.OverlayRenderer;
@@ -59,6 +60,7 @@ public class RuneLite
private ClientUI gui;
private PluginManager pluginManager;
private MenuManager menuManager = new MenuManager(this);
private OverlayRenderer renderer;
private EventBus eventBus = new EventBus(this::eventExceptionHandler);
private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(4);
@@ -89,6 +91,8 @@ public class RuneLite
{
gui = new ClientUI();
eventBus.register(menuManager);
if (SystemTray.isSupported())
{
SystemTray systemTray = SystemTray.getSystemTray();
@@ -134,6 +138,11 @@ public class RuneLite
return pluginManager;
}
public MenuManager getMenuManager()
{
return menuManager;
}
public OverlayRenderer getRenderer()
{
return renderer;

View File

@@ -0,0 +1,67 @@
/*
* Copyright (c) 2017, Robin <robin.weymans@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.client.events;
/**
*
* @author robin
*/
public class MenuOptionClicked
{
private String menuOption;
private String menuTarget;
private int menuAction;
public String getMenuOption()
{
return menuOption;
}
public void setMenuOption(String menuOption)
{
this.menuOption = menuOption;
}
public String getMenuTarget()
{
return menuTarget;
}
public void setMenuTarget(String menuTarget)
{
this.menuTarget = menuTarget;
}
public int getMenuAction()
{
return menuAction;
}
public void setMenuAction(int menuAction)
{
this.menuAction = menuAction;
}
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright (c) 2016-2017, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.client.events;
public class PlayerMenuOptionClicked
{
private String menuOption;
private String menuTarget;
public String getMenuOption()
{
return menuOption;
}
public void setMenuOption(String menuOption)
{
this.menuOption = menuOption;
}
public String getMenuTarget()
{
return menuTarget;
}
public void setMenuTarget(String menuTarget)
{
this.menuTarget = menuTarget;
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (c) 2017, Robin <robin.weymans@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.client.events;
public class PlayerMenuOptionsChanged
{
/**
* Index in playerOptions which changed
*/
private int index;
public int getIndex()
{
return index;
}
public void setIndex(int index)
{
this.index = index;
}
}

View File

@@ -0,0 +1,149 @@
/*
* Copyright (c) 2017, Robin <robin.weymans@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.client.menus;
import com.google.common.base.Preconditions;
import com.google.common.eventbus.Subscribe;
import java.util.HashMap;
import java.util.Map;
import net.runelite.api.Client;
import net.runelite.client.RuneLite;
import net.runelite.client.events.MenuOptionClicked;
import net.runelite.client.events.PlayerMenuOptionClicked;
import net.runelite.client.events.PlayerMenuOptionsChanged;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MenuManager
{
private static final Logger logger = LoggerFactory.getLogger(MenuManager.class);
/* 1007 is the highest number the rs client uses for actions. There is no way to see which ones are used,
* so im starting from 1500. Its just a number well over their maximum, so if a new action gets added, chances are little
* it interferes with the action the MenuManager uses.
*/
private static final int MENU_ACTION = 1500;
/*
* The index needs to be between 4 and 7,
*/
private static final int IDX_LOWER = 4;
private static final int IDX_UPPER = 8;
private final RuneLite runeLite;
//Maps the indexes that are being used to the menu option.
private final Map<Integer, String> playerMenuIndexMap = new HashMap<>();
public MenuManager(RuneLite runeLite)
{
this.runeLite = runeLite;
}
public void addPlayerMenuItem(String menuText)
{
Preconditions.checkNotNull(menuText);
int playerMenuIndex = findEmptyPlayerMenuIndex();
if (playerMenuIndex == IDX_UPPER)
{
return; // no more slots
}
addPlayerMenuItem(playerMenuIndex, menuText);
}
@Subscribe
public void onPlayerMenuOptionsChanged(PlayerMenuOptionsChanged event)
{
int idx = event.getIndex();
String menuText = playerMenuIndexMap.get(idx);
if (menuText == null)
{
return; // not our menu
}
// find new index for this option
int newIdx = findEmptyPlayerMenuIndex();
if (newIdx == IDX_UPPER)
{
logger.debug("Client has updated player menu index {} where option {} was, and there are no more free slots available", idx, menuText);
return;
}
logger.debug("Client has updated player menu index {} where option {} was, moving to index {}", idx, menuText, newIdx);
playerMenuIndexMap.remove(idx);
addPlayerMenuItem(newIdx, menuText);
}
@Subscribe
public void onMenuOptionClicked(MenuOptionClicked event)
{
if (event.getMenuAction() != MENU_ACTION)
{
return; // not a player menu
}
String target = event.getMenuTarget();
String username = target.split("[<>]")[2]; // <col=ffffff>username<col=40ff00> (level-42)
PlayerMenuOptionClicked playerMenuOptionClicked = new PlayerMenuOptionClicked();
playerMenuOptionClicked.setMenuOption(event.getMenuOption());
playerMenuOptionClicked.setMenuTarget(username);
runeLite.getEventBus().post(playerMenuOptionClicked);
}
private void addPlayerMenuItem(int playerOptionIndex, String menuText)
{
Client client = RuneLite.getClient();
client.getPlayerOptions()[playerOptionIndex] = menuText;
client.getPlayerOptionsPriorities()[playerOptionIndex] = true;
client.getPlayerMenuType()[playerOptionIndex] = MENU_ACTION;
playerMenuIndexMap.put(playerOptionIndex, menuText);
}
/**
* Find the next empty player menu slot index
*
* @return
*/
private int findEmptyPlayerMenuIndex()
{
int index = IDX_LOWER;
String[] playerOptions = RuneLite.getClient().getPlayerOptions();
while (index < IDX_UPPER && playerOptions[index] != null)
{
index++;
}
return index;
}
}

View File

@@ -24,12 +24,15 @@
*/
package net.runelite.client.plugins.hiscore;
import com.google.common.eventbus.Subscribe;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.util.concurrent.ScheduledExecutorService;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import net.runelite.client.RuneLite;
import net.runelite.client.events.PlayerMenuOptionClicked;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.ui.ClientUI;
import net.runelite.client.ui.NavigationButton;
@@ -41,10 +44,13 @@ public class Hiscore extends Plugin implements ActionListener
{
private static final Logger logger = LoggerFactory.getLogger(Hiscore.class);
private static final String LOOKUP = "Lookup";
private final NavigationButton navButton = new NavigationButton("Hiscore");
private final HiscorePanel hiscorePanel = new HiscorePanel();
private final ClientUI ui = RuneLite.getRunelite().getGui();
private final RuneLite runeLite = RuneLite.getRunelite();
private final ClientUI ui = runeLite.getGui();
public Hiscore()
{
@@ -61,6 +67,8 @@ public class Hiscore extends Plugin implements ActionListener
}
ui.getNavigationPanel().addNavigation(navButton);
runeLite.getMenuManager().addPlayerMenuItem(LOOKUP);
}
@Override
@@ -76,4 +84,14 @@ public class Hiscore extends Plugin implements ActionListener
ui.expand();
}
@Subscribe
public void onLookupMenuClicked(PlayerMenuOptionClicked event)
{
if (event.getMenuOption().equals(LOOKUP))
{
ScheduledExecutorService executor = runeLite.getExecutor();
executor.execute(() -> hiscorePanel.lookup(event.getMenuTarget()));
}
}
}

View File

@@ -150,6 +150,12 @@ public class HiscorePanel extends PluginPanel
return iconLevel;
}
public void lookup(String username)
{
input.setText(username);
lookup();
}
private void lookup()
{
String lookup = input.getText();

View File

@@ -27,6 +27,8 @@ package net.runelite.inject.callbacks;
import net.runelite.client.RuneLite;
import net.runelite.client.events.ExperienceChanged;
import net.runelite.client.events.MapRegionChanged;
import net.runelite.client.events.MenuOptionClicked;
import net.runelite.client.events.PlayerMenuOptionsChanged;
import net.runelite.client.events.AnimationChanged;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -61,6 +63,13 @@ public class Hooks
runelite.getEventBus().post(regionChanged);
break;
}
case "playerMenuOptionsChanged":
{
PlayerMenuOptionsChanged optionsChanged = new PlayerMenuOptionsChanged();
optionsChanged.setIndex(idx);
runelite.getEventBus().post(optionsChanged);
break;
}
case "animationChanged":
{
AnimationChanged animationChange = new AnimationChanged();
@@ -74,8 +83,32 @@ public class Hooks
}
if (object != null)
{
logger.trace("Event {} (idx {}) triggered on {}", name, idx, object);
}
else
{
logger.trace("Event {} (idx {}) triggered", name, idx);
}
}
public static void menuActionHook(int var0, int var1, int menuAction, int var3, String menuOption, String menuTarget, int var6, int var7)
{
/* Along the way, the RuneScape client may change a menuAction by incrementing it with 2000.
* I have no idea why, but it does. Their code contains the same conditional statement.
*/
if (menuAction >= 2000)
{
menuAction -= 2000;
}
logger.debug("Menu action clicked: {} ({}) on {}", menuOption, menuAction, menuTarget.isEmpty() ? "<nothing>" : menuTarget);
MenuOptionClicked playerMenuOptionClicked = new MenuOptionClicked();
playerMenuOptionClicked.setMenuOption(menuOption);
playerMenuOptionClicked.setMenuTarget(menuTarget);
playerMenuOptionClicked.setMenuAction(menuAction);
runelite.getEventBus().post(playerMenuOptionClicked);
}
}

View File

@@ -125,8 +125,14 @@ public interface Client extends GameEngine
@Import(value = "username", setter = true)
void setUsername(String username);
@Import("menuActions")
String[] getMenuActions();
@Import("playerOptions")
String[] getPlayerOptions();
@Import("playerOptionsPriorities")
boolean[] getPlayerOptionsPriorities();
@Import("playerMenuTypes")
int[] getPlayerMenuTypes();
@Import("menuTargets")
String[] getMenuTargets();