From 0e3db2152ae8448bfe89c26c59a546af71eedcd2 Mon Sep 17 00:00:00 2001
From: Cyborger1 <45152844+Cyborger1@users.noreply.github.com>
Date: Wed, 16 Aug 2023 18:44:57 -0400
Subject: [PATCH 1/2] Add "Hide Predict on Voted/Flagged" setting
---
README.md | 1 +
.../com/botdetector/BotDetectorConfig.java | 12 +++++
.../com/botdetector/BotDetectorPlugin.java | 52 ++++++++++++++++---
3 files changed, 59 insertions(+), 6 deletions(-)
diff --git a/README.md b/README.md
index 8a02b827..d19c1f33 100644
--- a/README.md
+++ b/README.md
@@ -46,6 +46,7 @@ Identifies bots by sending nearby players' information to a third-party machine
| 'Predict' Settings | 'Predict' Default Color | If set, highlights unflagged/unfeedbacked players' 'Predict' option in the given color so that you can easily spot it on the in-game menu. |
| 'Predict' Settings | 'Predict' Voted/Flagged Color | If set, highlights flagged/feedbacked players' 'Predict' option in the given color so that you can easily spot it on the in-game menu. |
| 'Predict' Settings | Apply Colors to 'Report' | If set, applies the above 'Predict' color options to the in-game 'Report' option as well.
⚠️ May cause issues with other plugins that rely on the 'Report' option being unchanged.⚠️ |
+| 'Predict' Settings | Hide 'Predict' after Voted/Flagged | If set, hides flagged/feedbacked players' 'Predict' option on the in-game menu. |
| Other Settings | Enable Chat Status Messages | Inserts chat messages in your chatbox to inform you about your uploads being sent. |
| Other Settings | '!bdstats' Chat Command Detail Level | Enable processing the '!bdstats' command when it appears in the chatbox, which will fetch the message author's plugin stats and display them. Disable to reduce spam. |
diff --git a/src/main/java/com/botdetector/BotDetectorConfig.java b/src/main/java/com/botdetector/BotDetectorConfig.java
index 96f5368b..c19ec5c2 100644
--- a/src/main/java/com/botdetector/BotDetectorConfig.java
+++ b/src/main/java/com/botdetector/BotDetectorConfig.java
@@ -262,6 +262,18 @@ default boolean applyPredictColorsOnReportOption()
return false;
}
+ @ConfigItem(
+ position = 7,
+ keyName = "hidePredictOnFlag",
+ name = "Hide 'Predict' after Voted/Flagged",
+ description = "Hides the 'Predict' option on the in-game menu for players after being flagged or being given feedback.",
+ section = predictSection
+ )
+ default boolean hidePredictOnFlag()
+ {
+ return false;
+ }
+
@ConfigItem(
position = 1,
keyName = "enableChatNotifications",
diff --git a/src/main/java/com/botdetector/BotDetectorPlugin.java b/src/main/java/com/botdetector/BotDetectorPlugin.java
index fc59b5fd..ba99c4a9 100644
--- a/src/main/java/com/botdetector/BotDetectorPlugin.java
+++ b/src/main/java/com/botdetector/BotDetectorPlugin.java
@@ -58,11 +58,14 @@
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.EnumSet;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
+import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
@@ -975,6 +978,12 @@ private void onMenuEntryAdded(MenuEntryAdded event)
return;
}
+ // Don't add the option if hide on flag is set and player is flagged
+ if (config.hidePredictOnFlag() && isPlayerFeedbackedOrFlagged(event.getTarget()))
+ {
+ return;
+ }
+
// TODO: Properly use the new menu entry callbacks
client.createMenuEntry(-1)
.setOption(getPredictOption(event.getTarget()))
@@ -989,8 +998,10 @@ private void onMenuEntryAdded(MenuEntryAdded event)
@Subscribe
private void onMenuOpened(MenuOpened event)
{
- // If neither color changing options are set, this is unnecessary
- if (config.predictOptionDefaultColor() == null && config.predictOptionFlaggedColor() == null)
+ // If neither color changing options are set and hide on flag is not set, this is unnecessary
+ if (config.predictOptionDefaultColor() == null &&
+ config.predictOptionFlaggedColor() == null &&
+ !config.hidePredictOnFlag())
{
return;
}
@@ -998,7 +1009,10 @@ private void onMenuOpened(MenuOpened event)
boolean changeReportOption = config.applyPredictColorsOnReportOption();
// Do this once when the menu opens
// Avoids having to loop the menu entries on every 'added' event
- MenuEntry[] menuEntries = event.getMenuEntries();
+ MenuEntry[] menuEntries = client.getMenuEntries();
+ // Using a flag and delete later approach on hide on flag to avoid having to rebuild the array on the fly
+ // every time the event occurs. If there's no deletion, no need to create a new array.
+ Set toDelete = new HashSet<>();
for (MenuEntry entry : menuEntries)
{
int type = entry.getType().getId();
@@ -1013,7 +1027,16 @@ private void onMenuOpened(MenuOpened event)
Player player = client.getCachedPlayers()[entry.getIdentifier()];
if (player != null)
{
- entry.setOption(getPredictOption(player.getName()));
+ if (config.hidePredictOnFlag() && isPlayerFeedbackedOrFlagged(player.getName()))
+ {
+ // Flag for deletion
+ toDelete.add(entry);
+ }
+ else
+ {
+ // Set the color
+ entry.setOption(getPredictOption(player.getName()));
+ }
}
}
@@ -1028,6 +1051,13 @@ private void onMenuOpened(MenuOpened event)
}
}
}
+
+ // If entries flagged for deletion, rebuild menu entries
+ if (!toDelete.isEmpty())
+ {
+ client.setMenuEntries(
+ Arrays.stream(menuEntries).filter(e -> !toDelete.contains(e)).toArray(MenuEntry[]::new));
+ }
}
@Subscribe
@@ -1200,13 +1230,23 @@ private String getReportOption(String playerName)
*/
private String getMenuOption(String playerName, String option)
{
- CaseInsensitiveString name = normalizeAndWrapPlayerName(playerName);
- Color prepend = (feedbackedPlayers.containsKey(name) || flaggedPlayers.containsKey(name)) ?
+ Color prepend = isPlayerFeedbackedOrFlagged(playerName) ?
config.predictOptionFlaggedColor() : config.predictOptionDefaultColor();
return prepend != null ? ColorUtil.prependColorTag(option, prepend) : option;
}
+ /**
+ * Checks whether the given {@code player} has been feedbacked or flagged.
+ * @param playerName The player to check.
+ * @return True if appears in memory as flagged or feedbacked, false otherwise.
+ */
+ private boolean isPlayerFeedbackedOrFlagged(String playerName)
+ {
+ CaseInsensitiveString name = normalizeAndWrapPlayerName(playerName);
+ return feedbackedPlayers.containsKey(name) || flaggedPlayers.containsKey(name);
+ }
+
/**
* Normalizes the given {@code playerName} by sanitizing the player name string,
* removing any Jagex tags and replacing any {@code _} or {@code -} with spaces.
From ef2af2746a1cd5cb62e980bacdd5e9fccf5362f7 Mon Sep 17 00:00:00 2001
From: Cyborger1 <45152844+Cyborger1@users.noreply.github.com>
Date: Wed, 16 Aug 2023 21:54:59 -0400
Subject: [PATCH 2/2] Rework menu handling to avoid MenuOpened entirely
---
.../com/botdetector/BotDetectorPlugin.java | 194 ++++++++++--------
1 file changed, 107 insertions(+), 87 deletions(-)
diff --git a/src/main/java/com/botdetector/BotDetectorPlugin.java b/src/main/java/com/botdetector/BotDetectorPlugin.java
index ba99c4a9..42d92cf4 100644
--- a/src/main/java/com/botdetector/BotDetectorPlugin.java
+++ b/src/main/java/com/botdetector/BotDetectorPlugin.java
@@ -62,10 +62,8 @@
import java.util.Collection;
import java.util.EnumSet;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
-import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
@@ -90,7 +88,6 @@
import net.runelite.api.events.CommandExecuted;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.MenuEntryAdded;
-import net.runelite.api.events.MenuOpened;
import net.runelite.api.events.MenuOptionClicked;
import net.runelite.api.events.PlayerSpawned;
import net.runelite.api.events.WorldChanged;
@@ -955,8 +952,23 @@ private void statsChatCommand(ChatMessage chatMessage, String message)
});
}
- @Subscribe
- private void onMenuEntryAdded(MenuEntryAdded event)
+ private void onPredictClick(String playerName)
+ {
+ if (playerName == null)
+ {
+ return;
+ }
+
+ String toPredict = Text.removeTags(playerName);
+ if (config.predictOptionCopyName())
+ {
+ Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(toPredict), null);
+ }
+
+ predictPlayer(toPredict);
+ }
+
+ private void handleInterfacePredictEntry(MenuEntryAdded event)
{
if (!config.addPredictOption())
{
@@ -984,119 +996,127 @@ private void onMenuEntryAdded(MenuEntryAdded event)
return;
}
- // TODO: Properly use the new menu entry callbacks
+ final String name = event.getTarget();
client.createMenuEntry(-1)
- .setOption(getPredictOption(event.getTarget()))
- .setTarget(event.getTarget())
+ .setOption(getPredictOption(name))
+ .setTarget(name)
.setType(MenuAction.RUNELITE)
.setParam0(event.getActionParam0())
.setParam1(event.getActionParam1())
- .setIdentifier(event.getIdentifier());
+ .setIdentifier(event.getIdentifier())
+ .onClick(c -> onPredictClick(name));
}
}
- @Subscribe
- private void onMenuOpened(MenuOpened event)
+ private void handlePlayerPredictEntry(MenuEntryAdded event)
{
- // If neither color changing options are set and hide on flag is not set, this is unnecessary
- if (config.predictOptionDefaultColor() == null &&
- config.predictOptionFlaggedColor() == null &&
- !config.hidePredictOnFlag())
+ // Player menu section
+ int type = event.getType();
+ if (type >= MenuAction.MENU_ACTION_DEPRIORITIZE_OFFSET)
+ {
+ type -= MenuAction.MENU_ACTION_DEPRIORITIZE_OFFSET;
+ }
+
+ if (type != MenuAction.RUNELITE_PLAYER.getId()
+ || !event.getOption().equals(PREDICT_OPTION))
{
return;
}
- boolean changeReportOption = config.applyPredictColorsOnReportOption();
- // Do this once when the menu opens
- // Avoids having to loop the menu entries on every 'added' event
- MenuEntry[] menuEntries = client.getMenuEntries();
- // Using a flag and delete later approach on hide on flag to avoid having to rebuild the array on the fly
- // every time the event occurs. If there's no deletion, no need to create a new array.
- Set toDelete = new HashSet<>();
- for (MenuEntry entry : menuEntries)
+ Player player = client.getCachedPlayers()[event.getIdentifier()];
+ if (player == null)
{
- int type = entry.getType().getId();
- if (type >= MenuAction.MENU_ACTION_DEPRIORITIZE_OFFSET)
- {
- type -= MenuAction.MENU_ACTION_DEPRIORITIZE_OFFSET;
- }
+ return;
+ }
+ final String name = player.getName();
- if (type == MenuAction.RUNELITE_PLAYER.getId()
- && entry.getOption().equals(PREDICT_OPTION))
+ if (config.hidePredictOnFlag() && isPlayerFeedbackedOrFlagged(name))
+ {
+ MenuEntry[] entries = client.getMenuEntries();
+ // Sanity check
+ if (event.getMenuEntry() == entries[entries.length - 1])
{
- Player player = client.getCachedPlayers()[entry.getIdentifier()];
- if (player != null)
- {
- if (config.hidePredictOnFlag() && isPlayerFeedbackedOrFlagged(player.getName()))
- {
- // Flag for deletion
- toDelete.add(entry);
- }
- else
- {
- // Set the color
- entry.setOption(getPredictOption(player.getName()));
- }
- }
+ // Delete the entry
+ client.setMenuEntries(Arrays.copyOf(entries, entries.length - 1));
}
+ }
+ else
+ {
+ // Set the color and callback
+ event.getMenuEntry().setOption(getPredictOption(name));
+ event.getMenuEntry().onClick(c -> onPredictClick(name));
+ }
+ }
+
+ private void handleJagexReportEntry(MenuEntryAdded event)
+ {
+ if (!config.applyPredictColorsOnReportOption()
+ || !event.getOption().equals(REPORT_OPTION))
+ {
+ return;
+ }
- // Check for Report option
- if (changeReportOption && entry.getOption().equals(REPORT_OPTION)
- && (PLAYER_MENU_ACTIONS.contains(entry.getType()) || entry.getType() == MenuAction.CC_OP_LOW_PRIORITY))
+ final String name;
+ // Player menu version
+ if (PLAYER_MENU_ACTIONS.contains(MenuAction.of(event.getType())))
+ {
+ Player player = client.getCachedPlayers()[event.getIdentifier()];
+ if (player == null)
{
- Player player = client.getCachedPlayers()[entry.getIdentifier()];
- if (player != null)
- {
- entry.setOption(getReportOption(player.getName()));
- }
+ return;
}
+ name = player.getName();
}
-
- // If entries flagged for deletion, rebuild menu entries
- if (!toDelete.isEmpty())
+ // Interface version (e.g. Chatbox)
+ else if (event.getType() == MenuAction.CC_OP_LOW_PRIORITY.getId())
{
- client.setMenuEntries(
- Arrays.stream(menuEntries).filter(e -> !toDelete.contains(e)).toArray(MenuEntry[]::new));
+ name = event.getTarget();
+ }
+ else
+ {
+ return;
}
+
+ event.getMenuEntry().setOption(getReportOption(name));
}
@Subscribe
- private void onMenuOptionClicked(MenuOptionClicked event)
+ private void onMenuEntryAdded(MenuEntryAdded event)
{
- String optionText = Text.removeTags(event.getMenuOption());
- if (((event.getMenuAction() == MenuAction.RUNELITE || event.getMenuAction() == MenuAction.RUNELITE_PLAYER)
- && optionText.equals(PREDICT_OPTION))
- || (config.predictOnReport() && (PLAYER_MENU_ACTIONS.contains(event.getMenuAction()) || event.getMenuAction() == MenuAction.CC_OP_LOW_PRIORITY)
- && optionText.equals(REPORT_OPTION)))
- {
- String name;
- if (event.getMenuAction() == MenuAction.RUNELITE_PLAYER
- || PLAYER_MENU_ACTIONS.contains(event.getMenuAction()))
- {
- Player player = client.getCachedPlayers()[event.getId()];
-
- if (player == null)
- {
- return;
- }
+ handleInterfacePredictEntry(event);
+ handlePlayerPredictEntry(event);
+ handleJagexReportEntry(event);
+ }
- name = player.getName();
- }
- else
- {
- name = event.getMenuTarget();
- }
+ // Have to use MenuOptionClicked on Report to avoid potentially having the callback overwritten by someone else
+ @Subscribe
+ private void onMenuOptionClicked(MenuOptionClicked event)
+ {
+ if (!config.predictOnReport() || !Text.removeTags(event.getMenuOption()).equals(REPORT_OPTION))
+ {
+ return;
+ }
- if (name != null)
+ final String name;
+ if (PLAYER_MENU_ACTIONS.contains(event.getMenuAction()))
+ {
+ Player player = client.getCachedPlayers()[event.getId()];
+ if (player == null)
{
- String toPredict = Text.removeTags(name);
- if (config.predictOptionCopyName())
- {
- Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(toPredict), null);
- }
- predictPlayer(toPredict);
+ return;
}
+ name = player.getName();
}
+ else if (event.getMenuAction() == MenuAction.CC_OP_LOW_PRIORITY)
+ {
+ name = event.getMenuTarget();
+ }
+ else
+ {
+ return;
+ }
+
+ onPredictClick(name);
}
@Subscribe