diff --git a/runelite-api/src/main/java/net/runelite/api/Actor.java b/runelite-api/src/main/java/net/runelite/api/Actor.java index e021795768..0495d48a75 100644 --- a/runelite-api/src/main/java/net/runelite/api/Actor.java +++ b/runelite-api/src/main/java/net/runelite/api/Actor.java @@ -29,6 +29,7 @@ import java.awt.Polygon; import java.awt.Shape; import java.awt.image.BufferedImage; import javax.annotation.Nullable; +import net.runelite.api.annotations.VisibleForDevtools; import net.runelite.api.coords.LocalPoint; import net.runelite.api.coords.WorldArea; import net.runelite.api.coords.WorldPoint; @@ -105,10 +106,6 @@ public interface Actor extends Renderable, Locatable */ LocalPoint getLocalLocation(); - void setIdlePoseAnimation(int animation); - - void setPoseAnimation(int animation); - /** * Get the index of the PoseFrame (the index as it appears in the sequenceDefinition "frames" array). */ @@ -138,22 +135,84 @@ public interface Actor extends Renderable, Locatable int getAnimation(); /** - * Gets the secondary animation the actor is performing. + * Gets the secondary animation the actor is performing. Usually an idle animation, or one of the walking ones. * * @return the animation ID * @see AnimationID */ int getPoseAnimation(); + @VisibleForDevtools + void setPoseAnimation(int animation); + /** - * If this is equal to the pose animation, the pose animation is ignored when - * you are doing another action. + * The idle pose animation. If the actor is not walking or otherwise animating, this will be used + * for their pose animation. * * @return the animation ID * @see AnimationID */ int getIdlePoseAnimation(); + @VisibleForDevtools + void setIdlePoseAnimation(int animation); + + /** + * Animation used for rotating left if the actor is also not walking + * + * @return the animation ID + * @see AnimationID + */ + int getIdleRotateLeft(); + + /** + * Animation used for rotating right if the actor is also not walking + * + * @return the animation ID + * @see AnimationID + */ + int getIdleRotateRight(); + + /** + * Animation used for walking + * + * @return the animation ID + * @see AnimationID + */ + int getWalkAnimation(); + + /** + * Animation used for rotating left while walking + * + * @return the animation ID + * @see AnimationID + */ + int getWalkRotateLeft(); + + /** + * Animation used for rotating right while walking + * + * @return the animation ID + * @see AnimationID + */ + int getWalkRotateRight(); + + /** + * Animation used for an about-face while walking + * + * @return the animation ID + * @see AnimationID + */ + int getWalkRotate180(); + + /** + * Animation used for running + * + * @return the animation ID + * @see AnimationID + */ + int getRunAnimation(); + /** * Sets an animation for the actor to perform. * @@ -291,16 +350,18 @@ public interface Actor extends Renderable, Locatable int getTurnRightAnimation(); - int getWalkAnimation(); - + // TODO: Remove next major + @Deprecated int getWalkBackAnimation(); + // TODO: Remove next major + @Deprecated int getWalkLeftAnimation(); + // TODO: Remove next major + @Deprecated int getWalkRightAnimation(); - int getRunAnimation(); - /** * Returns true if this NPC has died * diff --git a/runelite-api/src/main/java/net/runelite/api/Varbits.java b/runelite-api/src/main/java/net/runelite/api/Varbits.java index 0bdf355c33..b4d7471205 100644 --- a/runelite-api/src/main/java/net/runelite/api/Varbits.java +++ b/runelite-api/src/main/java/net/runelite/api/Varbits.java @@ -395,11 +395,16 @@ public enum Varbits MULTICOMBAT_AREA(4605), /** - * Kingdom Management + * Kingdom of Miscellania Management + * Kingdom Approval is represented as a 7-bit unsigned integer; 127 corresponds to 100% approval */ - KINGDOM_FAVOR(72), + KINGDOM_APPROVAL(72), KINGDOM_COFFER(74), + // TODO: Remove next major + @Deprecated + KINGDOM_FAVOR(72), + /** * The Hand in the Sand quest status */ diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/Widget.java b/runelite-api/src/main/java/net/runelite/api/widgets/Widget.java index 1a40856ad1..5e97fc069c 100644 --- a/runelite-api/src/main/java/net/runelite/api/widgets/Widget.java +++ b/runelite-api/src/main/java/net/runelite/api/widgets/Widget.java @@ -26,6 +26,7 @@ package net.runelite.api.widgets; import java.awt.Rectangle; import java.util.Collection; +import javax.annotation.Nullable; import net.runelite.api.FontTypeFace; import net.runelite.api.Point; import net.runelite.api.SpritePixels; @@ -117,6 +118,7 @@ public interface Widget /** * Gets the dynamic children of this widget in a sparse array */ + @Nullable Widget[] getChildren(); /** diff --git a/runelite-client/runelite-client.gradle.kts b/runelite-client/runelite-client.gradle.kts index ed1dcc996b..1dc31f0c1e 100644 --- a/runelite-client/runelite-client.gradle.kts +++ b/runelite-client/runelite-client.gradle.kts @@ -53,6 +53,7 @@ dependencies { compileOnly(group = "net.runelite", name = "orange-extensions", version = "1.0") implementation(project(":http-api")) + implementation(project(":runelite-jshell")) implementation(group = "ch.qos.logback", name = "logback-classic", version = "1.2.3") implementation(group = "com.google.code.gson", name = "gson", version = "2.8.5") implementation(group = "com.google.guava", name = "guava", version = "23.2-jre") diff --git a/runelite-client/src/main/java/net/runelite/client/eventbus/EventBus.java b/runelite-client/src/main/java/net/runelite/client/eventbus/EventBus.java index 816c61f2b8..f653439391 100644 --- a/runelite-client/src/main/java/net/runelite/client/eventbus/EventBus.java +++ b/runelite-client/src/main/java/net/runelite/client/eventbus/EventBus.java @@ -51,26 +51,20 @@ import net.runelite.client.util.ReflectUtil; @ThreadSafe public class EventBus { - @FunctionalInterface - public interface SubscriberMethod - { - void invoke(Object event); - } - @Value - private static class Subscriber + public static class Subscriber { private final Object object; private final Method method; private final float priority; @EqualsAndHashCode.Exclude - private final SubscriberMethod lamda; + private final Consumer lambda; void invoke(final Object arg) throws Exception { - if (lamda != null) + if (lambda != null) { - lamda.invoke(arg); + lambda.accept(arg); } else { @@ -80,7 +74,9 @@ public class EventBus } private final Consumer exceptionHandler; - private ImmutableMultimap subscribers = ImmutableMultimap.of(); + + @Nonnull + private ImmutableMultimap, Subscriber> subscribers = ImmutableMultimap.of(); /** * Instantiates EventBus with default exception handler @@ -99,13 +95,8 @@ public class EventBus */ public synchronized void register(@Nonnull final Object object) { - final ImmutableMultimap.Builder builder = ImmutableMultimap.builder(); - - if (subscribers != null) - { - builder.putAll(subscribers); - } - + final ImmutableMultimap.Builder, Subscriber> builder = ImmutableMultimap.builder(); + builder.putAll(subscribers); builder.orderValuesBy(Comparator.comparing(Subscriber::getPriority).reversed() .thenComparing(s -> s.object.getClass().getName())); @@ -138,7 +129,7 @@ public class EventBus } method.setAccessible(true); - SubscriberMethod lambda = null; + Consumer lambda = null; try { @@ -147,14 +138,14 @@ public class EventBus final MethodHandle target = caller.findVirtual(clazz, method.getName(), subscription); final CallSite site = LambdaMetafactory.metafactory( caller, - "invoke", - MethodType.methodType(SubscriberMethod.class, clazz), + "accept", + MethodType.methodType(Consumer.class, clazz), subscription.changeParameterType(0, Object.class), target, subscription); final MethodHandle factory = site.getTarget(); - lambda = (SubscriberMethod) factory.bindTo(object).invokeExact(); + lambda = (Consumer) factory.bindTo(object).invokeExact(); } catch (Throwable e) { @@ -170,6 +161,21 @@ public class EventBus subscribers = builder.build(); } + public synchronized Subscriber register(Class clazz, Consumer subFn, float priority) + { + final ImmutableMultimap.Builder, Subscriber> builder = ImmutableMultimap.builder(); + builder.putAll(subscribers); + builder.orderValuesBy(Comparator.comparing(Subscriber::getPriority).reversed() + .thenComparing(s -> s.object.getClass().getName())); + + Subscriber sub = new Subscriber(subFn, null, priority, (Consumer) subFn); + builder.put(clazz, sub); + + subscribers = builder.build(); + + return sub; + } + /** * Unregisters all subscribed methods from provided subscriber object. * @@ -177,12 +183,7 @@ public class EventBus */ public synchronized void unregister(@Nonnull final Object object) { - if (subscribers == null) - { - return; - } - - final Multimap map = HashMultimap.create(); + final Multimap, Subscriber> map = HashMultimap.create(); map.putAll(subscribers); for (Class clazz = object.getClass(); clazz != null; clazz = clazz.getSuperclass()) @@ -204,6 +205,21 @@ public class EventBus subscribers = ImmutableMultimap.copyOf(map); } + public synchronized void unregister(Subscriber sub) + { + if (sub == null) + { + return; + } + + final Multimap, Subscriber> map = HashMultimap.create(); + map.putAll(subscribers); + + map.values().remove(sub); + + subscribers = ImmutableMultimap.copyOf(map); + } + /** * Posts provided event to all registered subscribers. Subscriber calls are invoked immediately, * ordered by priority then their declaring class' name. diff --git a/runelite-client/src/main/java/net/runelite/client/game/ItemMapping.java b/runelite-client/src/main/java/net/runelite/client/game/ItemMapping.java index f6aae9b375..db600bc120 100644 --- a/runelite-client/src/main/java/net/runelite/client/game/ItemMapping.java +++ b/runelite-client/src/main/java/net/runelite/client/game/ItemMapping.java @@ -253,6 +253,22 @@ public enum ItemMapping ITEM_VOLATILE_ORB(VOLATILE_ORB, VOLATILE_NIGHTMARE_STAFF), ITEM_NIGHTMARE_STAFF(NIGHTMARE_STAFF, ELDRITCH_NIGHTMARE_STAFF, HARMONISED_NIGHTMARE_STAFF, VOLATILE_NIGHTMARE_STAFF), + // Trouver Parchment + ITEM_TROUVER_PARCHMENT( + TROUVER_PARCHMENT, INFERNAL_MAX_CAPE_L, FIRE_MAX_CAPE_L, ASSEMBLER_MAX_CAPE_L, BRONZE_DEFENDER_L, IRON_DEFENDER_L, STEEL_DEFENDER_L, BLACK_DEFENDER_L, MITHRIL_DEFENDER_L, ADAMANT_DEFENDER_L, + RUNE_DEFENDER_L, DRAGON_DEFENDER_L, DECORATIVE_SWORD_L, DECORATIVE_ARMOUR_L, DECORATIVE_ARMOUR_L_24159, DECORATIVE_HELM_L, DECORATIVE_SHIELD_L, DECORATIVE_ARMOUR_L_24162, DECORATIVE_ARMOUR_L_24163, DECORATIVE_ARMOUR_L_24164, + DECORATIVE_ARMOUR_L_24165, DECORATIVE_ARMOUR_L_24166, DECORATIVE_ARMOUR_L_24167, DECORATIVE_ARMOUR_L_24168, SARADOMIN_HALO_L, ZAMORAK_HALO_L, GUTHIX_HALO_L, HEALER_HAT_L, FIGHTER_HAT_L, RANGER_HAT_L, + FIGHTER_TORSO_L, PENANCE_SKIRT_L, VOID_KNIGHT_TOP_L, ELITE_VOID_TOP_L, VOID_KNIGHT_ROBE_L, ELITE_VOID_ROBE_L, VOID_KNIGHT_MACE_L, VOID_KNIGHT_GLOVES_L, VOID_MAGE_HELM_L, VOID_RANGER_HELM_L, + VOID_MELEE_HELM_L, AVERNIC_DEFENDER_L, ARMADYL_HALO_L, BANDOS_HALO_L, SEREN_HALO_L, ANCIENT_HALO_L, BRASSICA_HALO_L, AVAS_ASSEMBLER_L, FIRE_CAPE_L, INFERNAL_CAPE_L, IMBUED_SARADOMIN_MAX_CAPE_L, + IMBUED_ZAMORAK_MAX_CAPE_L, IMBUED_GUTHIX_MAX_CAPE_L, IMBUED_SARADOMIN_CAPE_L, IMBUED_ZAMORAK_CAPE_L, IMBUED_GUTHIX_CAPE_L, RUNE_POUCH_L, RUNNER_HAT_L, DECORATIVE_BOOTS_L, DECORATIVE_FULL_HELM_L), + ITEM_TROUVER_PARCHMENT_REFUND_LARGE( + COINS_995, 475000L, INFERNAL_MAX_CAPE_L, FIRE_MAX_CAPE_L, ASSEMBLER_MAX_CAPE_L, RUNE_DEFENDER_L, DRAGON_DEFENDER_L, DECORATIVE_SWORD_L, DECORATIVE_ARMOUR_L, DECORATIVE_ARMOUR_L_24159, DECORATIVE_HELM_L, DECORATIVE_SHIELD_L, + DECORATIVE_ARMOUR_L_24162, DECORATIVE_ARMOUR_L_24163, DECORATIVE_ARMOUR_L_24164, DECORATIVE_ARMOUR_L_24165, DECORATIVE_ARMOUR_L_24166, DECORATIVE_ARMOUR_L_24167, DECORATIVE_ARMOUR_L_24168, SARADOMIN_HALO_L, + ZAMORAK_HALO_L, GUTHIX_HALO_L, HEALER_HAT_L, FIGHTER_HAT_L, RANGER_HAT_L, FIGHTER_TORSO_L, PENANCE_SKIRT_L, VOID_KNIGHT_TOP_L, ELITE_VOID_TOP_L, VOID_KNIGHT_ROBE_L, ELITE_VOID_ROBE_L, VOID_KNIGHT_MACE_L, + VOID_KNIGHT_GLOVES_L, VOID_MAGE_HELM_L, VOID_RANGER_HELM_L, VOID_MELEE_HELM_L, AVERNIC_DEFENDER_L, ARMADYL_HALO_L, BANDOS_HALO_L, SEREN_HALO_L, ANCIENT_HALO_L, BRASSICA_HALO_L, AVAS_ASSEMBLER_L, + FIRE_CAPE_L, INFERNAL_CAPE_L, IMBUED_SARADOMIN_MAX_CAPE_L, IMBUED_ZAMORAK_MAX_CAPE_L, IMBUED_GUTHIX_MAX_CAPE_L, IMBUED_SARADOMIN_CAPE_L, IMBUED_ZAMORAK_CAPE_L, IMBUED_GUTHIX_CAPE_L, RUNE_POUCH_L, RUNNER_HAT_L, DECORATIVE_BOOTS_L, DECORATIVE_FULL_HELM_L), + ITEM_TROUVER_PARCHMENT_REFUND_SMALL(COINS_995, 47500L, BRONZE_DEFENDER_L, IRON_DEFENDER_L, STEEL_DEFENDER_L, BLACK_DEFENDER_L, MITHRIL_DEFENDER_L, ADAMANT_DEFENDER_L), + // Crystal items ITEM_CRYSTAL_TOOL_SEED(CRYSTAL_TOOL_SEED, CRYSTAL_AXE, CRYSTAL_AXE_INACTIVE, CRYSTAL_HARPOON, CRYSTAL_HARPOON_INACTIVE, CRYSTAL_PICKAXE, CRYSTAL_PICKAXE_INACTIVE), ITEM_CRYSTAL_AXE(DRAGON_AXE, CRYSTAL_AXE, CRYSTAL_AXE_INACTIVE), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/bank/BankPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/bank/BankPlugin.java index e0161503ae..bf9289f602 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/bank/BankPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/bank/BankPlugin.java @@ -70,7 +70,7 @@ import net.runelite.client.util.QuantityFormatter; @PluginDescriptor( name = "Bank", description = "Modifications to the banking interface", - tags = {"grand", "exchange", "high", "alchemy", "prices", "deposit"} + tags = {"grand", "exchange", "high", "alchemy", "prices", "deposit", "pin"} ) @Slf4j public class BankPlugin extends Plugin @@ -238,12 +238,15 @@ public class BankPlugin extends Plugin log.debug("Bank pin keypress"); - final String input = client.getVar(VarClientStr.CHATBOX_TYPED_TEXT); + final String chatboxTypedText = client.getVar(VarClientStr.CHATBOX_TYPED_TEXT); + final String inputText = client.getVar(VarClientStr.INPUT_TEXT); clientThread.invokeLater(() -> { // reset chatbox input to avoid pin going to chatbox.. - client.setVar(VarClientStr.CHATBOX_TYPED_TEXT, input); + client.setVar(VarClientStr.CHATBOX_TYPED_TEXT, chatboxTypedText); client.runScript(ScriptID.CHAT_PROMPT_INIT); + client.setVar(VarClientStr.INPUT_TEXT, inputText); + client.runScript(ScriptID.CHAT_TEXT_INPUT_REBUILD, ""); client.runScript(onOpListener); }); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsPlugin.java index 8c06945a66..7d877cb7aa 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsPlugin.java @@ -481,10 +481,14 @@ public class ChatCommandsPlugin extends Plugin advLogLoaded = false; Widget adventureLog = client.getWidget(WidgetInfo.ADVENTURE_LOG); - Matcher advLogExploitsText = ADVENTURE_LOG_TITLE_PATTERN.matcher(adventureLog.getChild(ADV_LOG_EXPLOITS_TEXT_INDEX).getText()); - if (advLogExploitsText.find()) + + if (adventureLog != null) { - pohOwner = advLogExploitsText.group(1); + Matcher advLogExploitsText = ADVENTURE_LOG_TITLE_PATTERN.matcher(adventureLog.getChild(ADV_LOG_EXPLOITS_TEXT_INDEX).getText()); + if (advLogExploitsText.find()) + { + pohOwner = advLogExploitsText.group(1); + } } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/chatnotifications/ChatNotificationsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/chatnotifications/ChatNotificationsPlugin.java index 092b8cf376..a869f7136b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/chatnotifications/ChatNotificationsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/chatnotifications/ChatNotificationsPlugin.java @@ -172,6 +172,18 @@ public class ChatNotificationsPlugin extends Plugin notifier.notify(Text.removeTags(chatMessage.getName()) + ": " + chatMessage.getMessage()); } break; + case PRIVATECHATOUT: + return; + case MODCHAT: + case PUBLICCHAT: + case FRIENDSCHAT: + case AUTOTYPER: + case MODAUTOTYPER: + if (client.getLocalPlayer() != null && Text.toJagexName(Text.removeTags(chatMessage.getName())).equals(client.getLocalPlayer().getName())) + { + return; + } + break; case CONSOLE: // Don't notify for notification messages if (chatMessage.getName().equals(runeliteTitle)) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/SkillChallengeClue.java b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/SkillChallengeClue.java index 19ec7b04a1..a36b967fae 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/SkillChallengeClue.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/SkillChallengeClue.java @@ -159,7 +159,7 @@ public class SkillChallengeClue extends ClueScroll implements NpcClueScroll, Nam new SkillChallengeClue("Craft multiple cosmic runes from a single essence.", item(ItemID.PURE_ESSENCE)), new SkillChallengeClue("Plant a watermelon seed.", item(ItemID.RAKE), item(ItemID.SEED_DIBBER), xOfItem(ItemID.WATERMELON_SEED, 3)), new SkillChallengeClue("Activate the Chivalry prayer."), - new SkillChallengeClue("Hand in a Tier 2 or higher set of Shayzien supply armour", "take the lovakengj armourers a boxed set of shayzien supply armour at tier 2 or above.", any("Shayzien Supply Set (Tier 2 or higher)", item(ItemID.SHAYZIEN_SUPPLY_SET_2), item(ItemID.SHAYZIEN_SUPPLY_SET_3), item(ItemID.SHAYZIEN_SUPPLY_SET_4), item(ItemID.SHAYZIEN_SUPPLY_SET_5))), + new SkillChallengeClue("Hand in a Tier 2 or higher set of Shayzien supply armour. (Requires 11 lovakite bars)", "take the lovakengj armourers a boxed set of shayzien supply armour at tier 2 or above.", any("Shayzien Supply Set (Tier 2 or higher)", item(ItemID.SHAYZIEN_SUPPLY_SET_2), item(ItemID.SHAYZIEN_SUPPLY_SET_3), item(ItemID.SHAYZIEN_SUPPLY_SET_4), item(ItemID.SHAYZIEN_SUPPLY_SET_5))), // Master Sherlock Tasks new SkillChallengeClue("Equip an abyssal whip in front of the abyssal demons of the Slayer Tower.", true, any("Abyssal Whip", item(ItemID.ABYSSAL_WHIP), item(ItemID.FROZEN_ABYSSAL_WHIP), item(ItemID.VOLCANIC_ABYSSAL_WHIP))), new SkillChallengeClue("Smith a runite med helm.", item(ItemID.HAMMER), item(ItemID.RUNITE_BAR)), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdLocation.java b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdLocation.java index 287ca3844a..3baeee926c 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdLocation.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdLocation.java @@ -62,7 +62,7 @@ public enum HotColdLocation ASGARNIA_CRAFT_GUILD(new WorldPoint(2917, 3295, 0), ASGARNIA, "Outside the Crafting Guild cow pen.", BRASSICAN_MAGE), ASGARNIA_RIMMINGTON(new WorldPoint(2976, 3239, 0), ASGARNIA, "In the centre of the Rimmington mine.", BRASSICAN_MAGE), ASGARNIA_MUDSKIPPER(new WorldPoint(2987, 3110, 0), ASGARNIA, "Mudskipper Point, near the starfish in the south-west corner.", BRASSICAN_MAGE), - ASGARNIA_TROLL(new WorldPoint(2910, 3616, 0), ASGARNIA, "The Troll arena, where the player fights Dad during the Troll Stronghold quest. Bring climbing boots if travelling from Burthorpe.", BRASSICAN_MAGE), + ASGARNIA_TROLL(new WorldPoint(2910, 3615, 0), ASGARNIA, "The Troll arena, where the player fights Dad during the Troll Stronghold quest. Bring climbing boots if travelling from Burthorpe.", BRASSICAN_MAGE), DESERT_GENIE(new WorldPoint(3359, 2912, 0), DESERT, "West of Nardah genie cave.", BRASSICAN_MAGE), DESERT_ALKHARID_MINE(new WorldPoint(3279, 3263, 0), DESERT, "West of Al Kharid mine.", BRASSICAN_MAGE), DESERT_MENAPHOS_GATE(new WorldPoint(3223, 2820, 0), DESERT, "North of Menaphos gate.", BRASSICAN_MAGE), @@ -117,7 +117,7 @@ public enum HotColdLocation KARAMJA_MUSA_POINT(new WorldPoint(2913, 3169, 0), KARAMJA, "Musa Point, banana plantation.", BRASSICAN_MAGE), KARAMJA_BRIMHAVEN_FRUIT_TREE(new WorldPoint(2782, 3215, 0), KARAMJA, "Brimhaven, east of the fruit tree patch.", BRASSICAN_MAGE), KARAMJA_WEST_BRIMHAVEN(new WorldPoint(2718, 3167, 0), KARAMJA, "West of Brimhaven.", BRASSICAN_MAGE), - KARAMJA_GLIDER(new WorldPoint(2966, 2975, 0), KARAMJA, "West of the gnome glider.", BRASSICAN_MAGE), + KARAMJA_GLIDER(new WorldPoint(2966, 2976, 0), KARAMJA, "West of the gnome glider.", BRASSICAN_MAGE), KARAMJA_KHARAZI_NE(new WorldPoint(2904, 2925, 0), KARAMJA, "North-eastern part of Kharazi Jungle.", BRASSICAN_MAGE), KARAMJA_KHARAZI_SW(new WorldPoint(2786, 2899, 0), KARAMJA, "South-western part of Kharazi Jungle.", BRASSICAN_MAGE), KARAMJA_CRASH_ISLAND(new WorldPoint(2909, 2737, 0), KARAMJA, "Northern part of Crash Island.", BRASSICAN_MAGE), @@ -146,7 +146,7 @@ public enum HotColdLocation WESTERN_PROVINCE_PISCATORIS_HUNTER_AREA(new WorldPoint(2359, 3564, 0), WESTERN_PROVINCE, "Eastern part of Piscatoris Hunter area, south-west of the Falconry.", BRASSICAN_MAGE), WESTERN_PROVINCE_ARANDAR(new WorldPoint(2370, 3319, 0), WESTERN_PROVINCE, "South-west of the crystal gate to Arandar.", ANCIENT_WIZARDS), WESTERN_PROVINCE_ELF_CAMP_EAST(new WorldPoint(2268, 3242, 0), WESTERN_PROVINCE, "East of Iorwerth Camp.", BRASSICAN_MAGE), - WESTERN_PROVINCE_ELF_CAMP_NW(new WorldPoint(2174, 3280, 0), WESTERN_PROVINCE, "North-west of Iorwerth Camp.", BRASSICAN_MAGE), + WESTERN_PROVINCE_ELF_CAMP_NW(new WorldPoint(2177, 3282, 0), WESTERN_PROVINCE, "North-west of Iorwerth Camp.", BRASSICAN_MAGE), WESTERN_PROVINCE_LLETYA(new WorldPoint(2337, 3166, 0), WESTERN_PROVINCE, "In Lletya.", BRASSICAN_MAGE), WESTERN_PROVINCE_TYRAS(new WorldPoint(2206, 3158, 0), WESTERN_PROVINCE, "Near Tyras Camp.", BRASSICAN_MAGE), WESTERN_PROVINCE_ZULANDRA(new WorldPoint(2196, 3057, 0), WESTERN_PROVINCE, "The northern house at Zul-Andra.", BRASSICAN_MAGE), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cooking/CookingPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/cooking/CookingPlugin.java index 19dacaf7b7..979e609a6f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cooking/CookingPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cooking/CookingPlugin.java @@ -196,7 +196,7 @@ public class CookingPlugin extends Plugin } else if (message.startsWith("You accidentally burn") - || message.startsWith("You burn") + || message.equals("You burn the mushroom in the fire.") || message.startsWith("Unfortunately the Jubbly") || message.startsWith("You accidentally spoil")) { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/cooking/CrowdsourcingCooking.java b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/cooking/CrowdsourcingCooking.java index 3cad5274a7..24e82b70f5 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/cooking/CrowdsourcingCooking.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/cooking/CrowdsourcingCooking.java @@ -80,7 +80,7 @@ public class CrowdsourcingCooking || message.startsWith("You roast a") || message.startsWith("You spit-roast") || message.startsWith("You cook") - || message.startsWith("You burn") + || message.equals("You burn the mushroom in the fire.") || message.startsWith("Eventually the Jubbly") || message.startsWith("Unfortunately the Jubbly") || message.startsWith("You accidentally burn") diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsButton.java b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsButton.java index e21af75f5a..e2a2f6c7ff 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsButton.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsButton.java @@ -28,7 +28,7 @@ import java.awt.Color; import javax.swing.JButton; import lombok.Getter; -class DevToolsButton extends JButton +public class DevToolsButton extends JButton { @Getter private boolean active; @@ -53,4 +53,20 @@ class DevToolsButton extends JButton setBackground(null); } } + + void addFrame(DevToolsFrame frame) + { + frame.setDevToolsButton(this); + addActionListener(ev -> + { + if (isActive()) + { + frame.close(); + } + else + { + frame.open(); + } + }); + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsFrame.java b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsFrame.java new file mode 100644 index 0000000000..94554e9863 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsFrame.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2021 Abex + * 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.plugins.devtools; + +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import javax.swing.JFrame; +import lombok.AccessLevel; +import lombok.Setter; +import net.runelite.client.ui.ClientUI; + +public class DevToolsFrame extends JFrame +{ + @Setter(AccessLevel.PACKAGE) + protected DevToolsButton devToolsButton; + + public DevToolsFrame() + { + setIconImage(ClientUI.ICON); + + setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); + addWindowListener(new WindowAdapter() + { + @Override + public void windowClosing(WindowEvent e) + { + close(); + devToolsButton.setActive(false); + } + }); + } + + public void open() + { + setVisible(true); + toFront(); + repaint(); + } + + public void close() + { + setVisible(false); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPanel.java index 54f0037639..9ba1efd092 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPanel.java @@ -25,6 +25,7 @@ */ package net.runelite.client.plugins.devtools; +import com.google.inject.ProvisionException; import java.awt.GridLayout; import java.awt.TrayIcon; import java.util.concurrent.ScheduledExecutorService; @@ -32,6 +33,7 @@ import java.util.concurrent.TimeUnit; import javax.inject.Inject; import javax.swing.JButton; import javax.swing.JPanel; +import lombok.extern.slf4j.Slf4j; import net.runelite.api.Client; import net.runelite.api.GameState; import net.runelite.api.MenuAction; @@ -44,6 +46,7 @@ import net.runelite.client.ui.overlay.infobox.Counter; import net.runelite.client.ui.overlay.infobox.InfoBoxManager; import net.runelite.client.util.ImageUtil; +@Slf4j class DevToolsPanel extends PluginPanel { private final Client client; @@ -129,30 +132,10 @@ class DevToolsPanel extends PluginPanel }); container.add(plugin.getWidgetInspector()); - plugin.getWidgetInspector().addActionListener((ev) -> - { - if (plugin.getWidgetInspector().isActive()) - { - widgetInspector.close(); - } - else - { - widgetInspector.open(); - } - }); + plugin.getWidgetInspector().addFrame(widgetInspector); container.add(plugin.getVarInspector()); - plugin.getVarInspector().addActionListener((ev) -> - { - if (plugin.getVarInspector().isActive()) - { - varInspector.close(); - } - else - { - varInspector.open(); - } - }); + plugin.getVarInspector().addFrame(varInspector); container.add(plugin.getSoundEffects()); @@ -164,17 +147,7 @@ class DevToolsPanel extends PluginPanel container.add(notificationBtn); container.add(plugin.getScriptInspector()); - plugin.getScriptInspector().addActionListener((ev) -> - { - if (plugin.getScriptInspector().isActive()) - { - scriptInspector.close(); - } - else - { - scriptInspector.open(); - } - }); + plugin.getScriptInspector().addFrame(scriptInspector); final JButton newInfoboxBtn = new JButton("Infobox"); newInfoboxBtn.addActionListener(e -> @@ -198,22 +171,27 @@ class DevToolsPanel extends PluginPanel container.add(clearInfoboxBtn); container.add(plugin.getInventoryInspector()); - plugin.getInventoryInspector().addActionListener((ev) -> - { - if (plugin.getInventoryInspector().isActive()) - { - inventoryInspector.close(); - } - else - { - inventoryInspector.open(); - } - }); + plugin.getInventoryInspector().addFrame(inventoryInspector); final JButton disconnectBtn = new JButton("Disconnect"); disconnectBtn.addActionListener(e -> clientThread.invoke(() -> client.setGameState(GameState.CONNECTION_LOST))); container.add(disconnectBtn); + try + { + ShellFrame sf = plugin.getInjector().getInstance(ShellFrame.class); + container.add(plugin.getShell()); + plugin.getShell().addFrame(sf); + } + catch (LinkageError | ProvisionException e) + { + log.debug("Shell is not supported", e); + } + catch (Exception e) + { + log.info("Shell couldn't be loaded", e); + } + return container; } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPlugin.java index 424c26e4c3..b690f54e71 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPlugin.java @@ -143,6 +143,7 @@ public class DevToolsPlugin extends Plugin private DevToolsButton soundEffects; private DevToolsButton scriptInspector; private DevToolsButton inventoryInspector; + private DevToolsButton shell; private NavigationButton navButton; @Provides @@ -187,6 +188,7 @@ public class DevToolsPlugin extends Plugin soundEffects = new DevToolsButton("Sound Effects"); scriptInspector = new DevToolsButton("Script Inspector"); inventoryInspector = new DevToolsButton("Inventory Inspector"); + shell = new DevToolsButton("Shell"); overlayManager.add(overlay); overlayManager.add(locationOverlay); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/InventoryInspector.java b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/InventoryInspector.java index 921ad34b68..9da9e19de8 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/InventoryInspector.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/InventoryInspector.java @@ -28,8 +28,6 @@ import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.event.AdjustmentEvent; import java.awt.event.AdjustmentListener; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -39,7 +37,6 @@ import javax.annotation.Nullable; import javax.inject.Inject; import javax.inject.Singleton; import javax.swing.JButton; -import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JScrollBar; import javax.swing.JScrollPane; @@ -65,7 +62,7 @@ import net.runelite.client.ui.ColorScheme; @Slf4j @Singleton -class InventoryInspector extends JFrame +class InventoryInspector extends DevToolsFrame { private static final int MAX_LOG_ENTRIES = 25; @@ -80,7 +77,7 @@ class InventoryInspector extends JFrame private final InventoryDeltaPanel deltaPanel; @Inject - InventoryInspector(Client client, EventBus eventBus, DevToolsPlugin plugin, ItemManager itemManager, ClientThread clientThread) + InventoryInspector(Client client, EventBus eventBus, ItemManager itemManager, ClientThread clientThread) { this.client = client; this.eventBus = eventBus; @@ -92,18 +89,6 @@ class InventoryInspector extends JFrame setTitle("OpenOSRS Inventory Inspector"); setIconImage(ClientUI.ICON); - setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); - // Reset highlight on close - addWindowListener(new WindowAdapter() - { - @Override - public void windowClosing(WindowEvent e) - { - close(); - plugin.getInventoryInspector().setActive(false); - } - }); - tree.setBorder(new EmptyBorder(2, 2, 2, 2)); tree.setRootVisible(false); tree.setShowsRootHandles(true); @@ -175,19 +160,19 @@ class InventoryInspector extends JFrame pack(); } + @Override public void open() { eventBus.register(this); - setVisible(true); - toFront(); - repaint(); + super.open(); } + @Override public void close() { eventBus.unregister(this); clearTracker(); - setVisible(false); + super.close(); } @Subscribe diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/ScriptInspector.java b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/ScriptInspector.java index d0a1692c6c..c728680344 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/ScriptInspector.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/ScriptInspector.java @@ -31,8 +31,6 @@ import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.event.AdjustmentEvent; import java.awt.event.AdjustmentListener; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashSet; @@ -42,7 +40,6 @@ import javax.swing.BorderFactory; import javax.swing.DefaultListModel; import javax.swing.JButton; import javax.swing.JFormattedTextField; -import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JPanel; @@ -68,14 +65,13 @@ import static net.runelite.api.widgets.WidgetInfo.TO_GROUP; import net.runelite.client.config.ConfigManager; import net.runelite.client.eventbus.EventBus; import net.runelite.client.eventbus.Subscribe; -import net.runelite.client.ui.ClientUI; import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.DynamicGridLayout; import net.runelite.client.ui.FontManager; import net.runelite.client.util.Text; @Slf4j -public class ScriptInspector extends JFrame +public class ScriptInspector extends DevToolsFrame { // These scripts are the only ones that fire every client tick regardless of location. private final static String DEFAULT_BLACKLIST = "3174,1004"; @@ -139,28 +135,16 @@ public class ScriptInspector extends JFrame } @Inject - ScriptInspector(Client client, EventBus eventBus, DevToolsPlugin plugin, ConfigManager configManager) + ScriptInspector(Client client, EventBus eventBus, ConfigManager configManager) { this.eventBus = eventBus; this.client = client; this.configManager = configManager; setTitle("OpenOSRS Script Inspector"); - setIconImage(ClientUI.ICON); setLayout(new BorderLayout()); - setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); - addWindowListener(new WindowAdapter() - { - @Override - public void windowClosing(WindowEvent e) - { - close(); - plugin.getScriptInspector().setActive(false); - } - }); - tracker.setLayout(new DynamicGridLayout(0, 1, 0, 3)); final JPanel leftSide = new JPanel(); @@ -344,14 +328,14 @@ public class ScriptInspector extends JFrame } } + @Override public void open() { eventBus.register(this); - setVisible(true); - toFront(); - repaint(); + super.open(); } + @Override public void close() { configManager.setConfiguration("devtools", "highlights", @@ -360,7 +344,7 @@ public class ScriptInspector extends JFrame Text.toCSV(Lists.transform(new ArrayList<>(blacklist), String::valueOf))); currentNode = null; eventBus.unregister(this); - setVisible(false); + super.close(); } private void addScriptLog(ScriptTreeNode treeNode) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/ShellFrame.java b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/ShellFrame.java new file mode 100644 index 0000000000..3bc6eb0fef --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/ShellFrame.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2021 Abex + * 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.plugins.devtools; + +import java.util.concurrent.ScheduledExecutorService; +import javax.inject.Inject; +import javax.inject.Singleton; +import net.runelite.client.RuneLite; +import net.runelite.client.callback.ClientThread; +import net.runelite.jshell.ShellPanel; + +@Singleton +class ShellFrame extends DevToolsFrame +{ + private final ShellPanel shellPanel; + + @Inject + ShellFrame(ClientThread clientThread, ScheduledExecutorService executor) + { + this.shellPanel = new ShellPanel(executor) + { + @Override + protected void invokeOnClientThread(Runnable r) + { + clientThread.invoke(r); + } + }; + setContentPane(shellPanel); + + setTitle("OpenOSRS Shell"); + + pack(); + } + + @Override + public void open() + { + shellPanel.switchContext(RuneLite.getInjector()); + super.open(); + } + + @Override + public void close() + { + super.close(); + shellPanel.freeContext(); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/VarInspector.java b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/VarInspector.java index bdbf98ad1e..8b104e2035 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/VarInspector.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/VarInspector.java @@ -32,15 +32,12 @@ import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.event.AdjustmentEvent; import java.awt.event.AdjustmentListener; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; import java.util.HashMap; import java.util.Map; import java.util.Objects; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JCheckBox; -import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollBar; @@ -62,13 +59,12 @@ import net.runelite.api.events.VarbitChanged; import net.runelite.client.callback.ClientThread; import net.runelite.client.eventbus.EventBus; import net.runelite.client.eventbus.Subscribe; -import net.runelite.client.ui.ClientUI; import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.DynamicGridLayout; import net.runelite.client.ui.FontManager; @Slf4j -class VarInspector extends JFrame +class VarInspector extends DevToolsFrame { @Getter private enum VarType @@ -106,28 +102,16 @@ class VarInspector extends JFrame private Map varcs = null; @Inject - VarInspector(Client client, ClientThread clientThread, EventBus eventBus, DevToolsPlugin plugin) + VarInspector(Client client, ClientThread clientThread, EventBus eventBus) { this.client = client; this.clientThread = clientThread; this.eventBus = eventBus; setTitle("OpenOSRS Var Inspector"); - setIconImage(ClientUI.ICON); setLayout(new BorderLayout()); - setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); - addWindowListener(new WindowAdapter() - { - @Override - public void windowClosing(WindowEvent e) - { - close(); - plugin.getVarInspector().setActive(false); - } - }); - tracker.setLayout(new DynamicGridLayout(0, 1, 0, 3)); final JPanel trackerWrapper = new JPanel(); @@ -332,6 +316,7 @@ class VarInspector extends JFrame } } + @Override public void open() { if (oldVarps == null) @@ -361,16 +346,15 @@ class VarInspector extends JFrame }); eventBus.register(this); - setVisible(true); - toFront(); - repaint(); + super.open(); } + @Override public void close() { + super.close(); tracker.removeAll(); eventBus.unregister(this); - setVisible(false); varcs = null; varbits = null; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/WidgetInspector.java b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/WidgetInspector.java index 88ec8358cd..5d1184a9a9 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/WidgetInspector.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/WidgetInspector.java @@ -32,8 +32,6 @@ import com.google.inject.Singleton; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; import java.util.Collection; import java.util.Comparator; import java.util.Enumeration; @@ -43,7 +41,6 @@ import java.util.Stack; import java.util.stream.Stream; import javax.swing.JButton; import javax.swing.JCheckBox; -import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; @@ -73,13 +70,12 @@ import net.runelite.api.widgets.WidgetType; import net.runelite.client.callback.ClientThread; import net.runelite.client.eventbus.EventBus; import net.runelite.client.eventbus.Subscribe; -import net.runelite.client.ui.ClientUI; import net.runelite.client.ui.overlay.OverlayManager; import net.runelite.client.util.ColorUtil; @Slf4j @Singleton -class WidgetInspector extends JFrame +class WidgetInspector extends DevToolsFrame { private static final Map widgetIdMap = new HashMap<>(); @@ -123,7 +119,6 @@ class WidgetInspector extends JFrame ClientThread clientThread, WidgetInfoTableModel infoTableModel, DevToolsConfig config, - DevToolsPlugin plugin, EventBus eventBus, Provider overlay, OverlayManager overlayManager) @@ -138,18 +133,6 @@ class WidgetInspector extends JFrame eventBus.register(this); setTitle("OpenOSRS Widget Inspector"); - setIconImage(ClientUI.ICON); - - // Reset highlight on close - addWindowListener(new WindowAdapter() - { - @Override - public void windowClosing(WindowEvent e) - { - close(); - plugin.getWidgetInspector().setActive(false); - } - }); setLayout(new BorderLayout()); @@ -402,21 +385,21 @@ class WidgetInspector extends JFrame return widgetIdMap.get(packedId); } + @Override public void open() { - setVisible(true); - toFront(); - repaint(); + super.open(); overlayManager.add(this.overlay.get()); clientThread.invokeLater(this::addPickerWidget); } + @Override public void close() { overlayManager.remove(this.overlay.get()); clientThread.invokeLater(this::removePickerWidget); setSelectedWidget(null, -1, false); - setVisible(false); + super.close(); } private void removePickerWidget() diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordConfig.java index 770fe49cde..f751cf05af 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordConfig.java @@ -72,11 +72,22 @@ public interface DiscordConfig extends Config return 5; } + @ConfigItem( + keyName = "showMainMenu", + name = "Main Menu", + description = "Show status when in main menu", + position = 3 + ) + default boolean showMainMenu() + { + return true; + } + @ConfigItem( keyName = "showSkillActivity", name = "Skilling", description = "Show your activity while training skills", - position = 3 + position = 4 ) default boolean showSkillingActivity() { @@ -87,7 +98,7 @@ public interface DiscordConfig extends Config keyName = "showBossActivity", name = "Bosses", description = "Show your activity and location while at bosses", - position = 4 + position = 5 ) default boolean showBossActivity() { @@ -98,7 +109,7 @@ public interface DiscordConfig extends Config keyName = "showCityActivity", name = "Cities", description = "Show your activity and location while in cities", - position = 5 + position = 6 ) default boolean showCityActivity() { @@ -109,7 +120,7 @@ public interface DiscordConfig extends Config keyName = "showDungeonActivity", name = "Dungeons", description = "Show your activity and location while in dungeons", - position = 6 + position = 7 ) default boolean showDungeonActivity() { @@ -120,7 +131,7 @@ public interface DiscordConfig extends Config keyName = "showMinigameActivity", name = "Minigames", description = "Show your activity and location while in minigames", - position = 7 + position = 8 ) default boolean showMinigameActivity() { @@ -131,7 +142,7 @@ public interface DiscordConfig extends Config keyName = "showRaidingActivity", name = "Raids", description = "Show your activity and location while in Raids", - position = 8 + position = 9 ) default boolean showRaidingActivity() { @@ -142,7 +153,7 @@ public interface DiscordConfig extends Config keyName = "showRegionsActivity", name = "Regions", description = "Show your activity and location while in other regions", - position = 9 + position = 10 ) default boolean showRegionsActivity() { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordPlugin.java index f6469dbac5..75c076b4c9 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordPlugin.java @@ -358,9 +358,14 @@ public class DiscordPlugin extends Plugin private void checkForGameStateUpdate() { - discordState.triggerEvent(client.getGameState() == GameState.LOGGED_IN - ? DiscordGameEventType.IN_GAME - : DiscordGameEventType.IN_MENU); + final boolean isLoggedIn = client.getGameState() == GameState.LOGGED_IN; + + if (config.showMainMenu() || isLoggedIn) + { + discordState.triggerEvent(isLoggedIn + ? DiscordGameEventType.IN_GAME + : DiscordGameEventType.IN_MENU); + } } private void checkForAreaUpdate() diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/friendschat/FriendsChatPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/friendschat/FriendsChatPlugin.java index 6263518015..eda35b6578 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/friendschat/FriendsChatPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/friendschat/FriendsChatPlugin.java @@ -289,11 +289,17 @@ public class FriendsChatPlugin extends Plugin { chatTitleWidget.setText(TITLE + " (" + friendsChatManager.getCount() + "/100)"); } - else if (config.recentChats() && chatList.getChildren() == null && !Strings.isNullOrEmpty(owner.getText())) + else if (chatList.getChildren() == null && !Strings.isNullOrEmpty(owner.getText())) { - chatTitleWidget.setText(RECENT_TITLE); - - loadFriendsChats(); + if (config.recentChats()) + { + chatTitleWidget.setText(RECENT_TITLE); + loadFriendsChats(); + } + else + { + chatTitleWidget.setText(TITLE); + } } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerConfig.java index bc2a65f0d9..7a6538e22d 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerConfig.java @@ -31,9 +31,13 @@ import net.runelite.client.config.Config; import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigItem; -@ConfigGroup("groundMarker") +@ConfigGroup(GroundMarkerConfig.GROUND_MARKER_CONFIG_GROUP) public interface GroundMarkerConfig extends Config { + String GROUND_MARKER_CONFIG_GROUP = "groundMarker"; + String SHOW_IMPORT_EXPORT_KEY_NAME = "showImportExport"; + String SHOW_CLEAR_KEY_NAME = "showClear"; + @Alpha @ConfigItem( keyName = "markerColor", @@ -64,4 +68,24 @@ public interface GroundMarkerConfig extends Config { return false; } + + @ConfigItem( + keyName = SHOW_IMPORT_EXPORT_KEY_NAME, + name = "Show Import/Export options", + description = "Show the Import/Export options on the minimap right-click menu" + ) + default boolean showImportExport() + { + return true; + } + + @ConfigItem( + keyName = SHOW_CLEAR_KEY_NAME, + name = "Show Clear option", + description = "Show the Clear option on the minimap right-click menu, which deletes all currently loaded markers" + ) + default boolean showClear() + { + return false; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerPlugin.java index 81ac210bfc..850f383439 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerPlugin.java @@ -54,6 +54,7 @@ import net.runelite.api.events.MenuOptionClicked; import net.runelite.client.config.ConfigManager; import net.runelite.client.eventbus.EventBus; import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.events.ConfigChanged; import net.runelite.client.game.chatbox.ChatboxPanelManager; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; @@ -190,7 +191,14 @@ public class GroundMarkerPlugin extends Plugin { overlayManager.add(overlay); overlayManager.add(minimapOverlay); - sharingManager.addMenuOptions(); + if (config.showImportExport()) + { + sharingManager.addImportExportMenuOptions(); + } + if (config.showClear()) + { + sharingManager.addClearMenuOption(); + } loadPoints(); eventBus.register(sharingManager); } @@ -280,6 +288,27 @@ public class GroundMarkerPlugin extends Plugin } } + @Subscribe + public void onConfigChanged(ConfigChanged event) + { + if (event.getGroup().equals(GroundMarkerConfig.GROUND_MARKER_CONFIG_GROUP) + && (event.getKey().equals(GroundMarkerConfig.SHOW_IMPORT_EXPORT_KEY_NAME) + || event.getKey().equals(GroundMarkerConfig.SHOW_CLEAR_KEY_NAME))) + { + // Maintain consistent menu option order by removing everything then adding according to config + sharingManager.removeMenuOptions(); + + if (config.showImportExport()) + { + sharingManager.addImportExportMenuOptions(); + } + if (config.showClear()) + { + sharingManager.addClearMenuOption(); + } + } + } + private void markTile(LocalPoint localPoint) { if (localPoint == null) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerSharingManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerSharingManager.java index 91c0e800ac..056ed509f2 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerSharingManager.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerSharingManager.java @@ -59,6 +59,7 @@ class GroundMarkerSharingManager { private static final WidgetMenuOption EXPORT_MARKERS_OPTION = new WidgetMenuOption("Export", "Ground Markers", WORLD_MAP_OPTION); private static final WidgetMenuOption IMPORT_MARKERS_OPTION = new WidgetMenuOption("Import", "Ground Markers", WORLD_MAP_OPTION); + private static final WidgetMenuOption CLEAR_MARKERS_OPTION = new WidgetMenuOption("Clear", "Ground Markers", WORLD_MAP_OPTION); private final GroundMarkerPlugin plugin; private final Client client; @@ -79,16 +80,22 @@ class GroundMarkerSharingManager this.gson = gson; } - void addMenuOptions() + void addImportExportMenuOptions() { menuManager.addManagedCustomMenu(EXPORT_MARKERS_OPTION); menuManager.addManagedCustomMenu(IMPORT_MARKERS_OPTION); } + void addClearMenuOption() + { + menuManager.addManagedCustomMenu(CLEAR_MARKERS_OPTION); + } + void removeMenuOptions() { menuManager.removeManagedCustomMenu(EXPORT_MARKERS_OPTION); menuManager.removeManagedCustomMenu(IMPORT_MARKERS_OPTION); + menuManager.removeManagedCustomMenu(CLEAR_MARKERS_OPTION); } private boolean widgetMenuClickedEquals(final WidgetMenuOptionClicked event, final WidgetMenuOption target) @@ -114,6 +121,10 @@ class GroundMarkerSharingManager { promptForImport(); } + else if (widgetMenuClickedEquals(event, CLEAR_MARKERS_OPTION)) + { + promptForClear(); + } } private void exportGroundMarkers() @@ -233,6 +244,41 @@ class GroundMarkerSharingManager sendChatMessage(importPoints.size() + " ground markers were imported from the clipboard."); } + private void promptForClear() + { + int[] regions = client.getMapRegions(); + if (regions == null) + { + return; + } + + long numActivePoints = Arrays.stream(regions) + .mapToLong(regionId -> plugin.getPoints(regionId).size()) + .sum(); + + if (numActivePoints == 0) + { + sendChatMessage("You have no ground markers to clear."); + return; + } + + chatboxPanelManager.openTextMenuInput("Are you sure you want to clear the
" + numActivePoints + " currently loaded ground markers?") + .option("Yes", () -> + { + for (int regionId : regions) + { + plugin.savePoints(regionId, null); + } + + plugin.loadPoints(); + sendChatMessage(numActivePoints + " ground marker" + + (numActivePoints == 1 ? " was cleared." : "s were cleared.")); + + }) + .option("No", Runnables::doNothing) + .build(); + } + private void sendChatMessage(final String message) { chatMessageManager.queue(QueuedMessage.builder() diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemidentification/ItemIdentificationPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemidentification/ItemIdentificationPlugin.java index 1eb4158076..df74c10bcd 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/itemidentification/ItemIdentificationPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemidentification/ItemIdentificationPlugin.java @@ -34,6 +34,7 @@ import net.runelite.client.ui.overlay.OverlayManager; @PluginDescriptor( name = "Item Identification", description = "Show identifying text over items with difficult to distinguish sprites", + tags = {"abbreviations", "labels", "seeds", "herbs", "saplings", "seedlings"}, enabledByDefault = false ) public class ItemIdentificationPlugin extends Plugin diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/kingdomofmiscellania/KingdomConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/kingdomofmiscellania/KingdomConfig.java new file mode 100755 index 0000000000..064d260fd7 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/kingdomofmiscellania/KingdomConfig.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2020, Brandt Hill + * 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.plugins.kingdomofmiscellania; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; +import net.runelite.client.config.Range; + +@ConfigGroup(KingdomConfig.CONFIG_GROUP_NAME) +public interface KingdomConfig extends Config +{ + String CONFIG_GROUP_NAME = "kingdomofmiscellania"; + int MAX_COFFER = 7_500_000; + int MAX_APPROVAL_PERCENT = 100; + + @ConfigItem( + position = 1, + keyName = "sendNotifications", + name = "Send Notifications", + description = "Send chat notifications upon login showing current estimated coffer and approval" + ) + default boolean shouldSendNotifications() + { + return false; + } + + @Range( + max = MAX_COFFER + ) + @ConfigItem( + position = 2, + keyName = "cofferThreshold", + name = "Coffer Threshold", + description = "Send notifications if coffer is below this value" + ) + default int getCofferThreshold() + { + return MAX_COFFER; + } + + @Range( + max = MAX_APPROVAL_PERCENT + ) + @ConfigItem( + position = 3, + keyName = "approvalThreshold", + name = "Approval Threshold", + description = "Send notifications if approval percentage is below this value" + ) + default int getApprovalThreshold() + { + return MAX_APPROVAL_PERCENT; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/kingdomofmiscellania/KingdomCounter.java b/runelite-client/src/main/java/net/runelite/client/plugins/kingdomofmiscellania/KingdomCounter.java index aaaf0468b0..7653b57a53 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/kingdomofmiscellania/KingdomCounter.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/kingdomofmiscellania/KingdomCounter.java @@ -34,20 +34,20 @@ public class KingdomCounter extends Counter KingdomCounter(BufferedImage image, KingdomPlugin plugin) { - super(image, plugin, plugin.getFavor()); + super(image, plugin, plugin.getApproval()); this.plugin = plugin; } @Override public String getText() { - return KingdomPlugin.getFavorPercent(plugin.getFavor()) + "%"; + return KingdomPlugin.getApprovalPercent(plugin.getApproval()) + "%"; } @Override public String getTooltip() { - return "Favor: " + plugin.getFavor() + "/127" + "
" + return "Approval: " + plugin.getApproval() + "/" + KingdomPlugin.MAX_APPROVAL + "
" + "Coffer: " + QuantityFormatter.quantityToStackSize(plugin.getCoffer()); } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/kingdomofmiscellania/KingdomPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/kingdomofmiscellania/KingdomPlugin.java index 17347b3aac..bae9b177bf 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/kingdomofmiscellania/KingdomPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/kingdomofmiscellania/KingdomPlugin.java @@ -25,32 +25,55 @@ package net.runelite.client.plugins.kingdomofmiscellania; import com.google.common.collect.ImmutableSet; +import com.google.inject.Provides; +import java.time.Duration; +import java.time.Instant; import javax.inject.Inject; -import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import net.runelite.api.ChatMessageType; import net.runelite.api.Client; import net.runelite.api.GameState; import static net.runelite.api.ItemID.TEAK_CHEST; +import net.runelite.api.Quest; +import net.runelite.api.QuestState; import net.runelite.api.VarPlayer; import net.runelite.api.Varbits; import net.runelite.api.events.GameStateChanged; +import net.runelite.api.events.GameTick; import net.runelite.api.events.VarbitChanged; +import net.runelite.client.chat.ChatColorType; +import net.runelite.client.chat.ChatMessageBuilder; +import net.runelite.client.chat.ChatMessageManager; +import net.runelite.client.chat.QueuedMessage; +import net.runelite.client.config.ConfigManager; import net.runelite.client.eventbus.Subscribe; import net.runelite.client.game.ItemManager; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.ui.overlay.infobox.InfoBoxManager; +import net.runelite.client.util.QuantityFormatter; @PluginDescriptor( name = "Kingdom of Miscellania", - description = "Show amount of favor when inside Miscellania", - tags = {"favor", "favour", "managing", "overlay"}, + description = "Show amount of approval when inside Miscellania", + tags = {"favor", "favour", "managing", "overlay", "approval", "coffer"}, enabledByDefault = false ) @Slf4j public class KingdomPlugin extends Plugin { private static final ImmutableSet KINGDOM_REGION = ImmutableSet.of(10044, 10300); + private static final String CONFIG_LAST_CHANGED_KEY = "lastChanged"; + private static final String CONFIG_COFFER_KEY = "coffer"; + private static final String CONFIG_APPROVAL_KEY = "approval"; + private static final String CHAT_MESSAGE_FORMAT = "Your Kingdom of Miscellania approval is %d%%, and your coffer has %s coins."; + private static final int MAX_WITHDRAWAL_BASE = 50_000; + private static final int MAX_WITHDRAWAL_ROYAL_TROUBLE = 75_000; + private static final float APPROVAL_DECREMENT_BASE = 0.025f; + private static final float APPROVAL_DECREMENT_ROYAL_TROUBLE = 0.010f; + static final int MAX_APPROVAL = 127; + + private boolean loggingIn; @Inject private Client client; @@ -61,8 +84,14 @@ public class KingdomPlugin extends Plugin @Inject private ItemManager itemManager; - @Getter - private int favor = 0, coffer = 0; + @Inject + private KingdomConfig config; + + @Inject + private ConfigManager configManager; + + @Inject + private ChatMessageManager chatMessageManager; private KingdomCounter counter; @@ -72,11 +101,28 @@ public class KingdomPlugin extends Plugin removeKingdomInfobox(); } + @Provides + KingdomConfig getConfig(ConfigManager configManager) + { + return configManager.getConfig(KingdomConfig.class); + } + @Subscribe public void onVarbitChanged(VarbitChanged event) { - favor = client.getVar(Varbits.KINGDOM_FAVOR); - coffer = client.getVar(Varbits.KINGDOM_COFFER); + final int coffer = client.getVar(Varbits.KINGDOM_COFFER); + final int approval = client.getVar(Varbits.KINGDOM_APPROVAL); + + if (client.getGameState() == GameState.LOGGED_IN + && isThroneOfMiscellaniaCompleted() + && (isInKingdom() || coffer > 0 && approval > 0) + && (getCoffer() != coffer || getApproval() != approval)) + { + setLastChanged(Instant.now()); + setCoffer(coffer); + setApproval(approval); + } + processInfobox(); } @@ -87,11 +133,25 @@ public class KingdomPlugin extends Plugin { processInfobox(); } + else if (event.getGameState() == GameState.LOGGING_IN) + { + loggingIn = true; + } + } + + @Subscribe + public void onGameTick(GameTick gameTick) + { + if (loggingIn) + { + loggingIn = false; + createNotification(); + } } private void processInfobox() { - if (client.getGameState() == GameState.LOGGED_IN && hasCompletedQuest() && isInKingdom()) + if (client.getGameState() == GameState.LOGGED_IN && isThroneOfMiscellaniaCompleted() && isInKingdom()) { addKingdomInfobox(); } @@ -99,7 +159,33 @@ public class KingdomPlugin extends Plugin { removeKingdomInfobox(); } + } + private void createNotification() + { + if (!config.shouldSendNotifications() || !isThroneOfMiscellaniaCompleted()) + { + return; + } + + if (getLastChanged() == null) + { + log.debug("Kingdom Of Miscellania values not yet set. Visit Miscellania to automatically set values."); + return; + } + + Instant lastChanged = getLastChanged(); + int lastCoffer = getCoffer(); + int lastApproval = getApproval(); + int estimatedCoffer = estimateCoffer(lastChanged, lastCoffer); + int estimatedApproval = estimateApproval(lastChanged, lastApproval); + if (estimatedCoffer < config.getCofferThreshold() || getApprovalPercent(estimatedApproval) < config.getApprovalThreshold()) + { + sendChatMessage(String.format( + CHAT_MESSAGE_FORMAT, + getApprovalPercent(estimatedApproval), + QuantityFormatter.quantityToStackSize(estimatedCoffer))); + } } private void addKingdomInfobox() @@ -122,20 +208,92 @@ public class KingdomPlugin extends Plugin } } + private int estimateCoffer(Instant lastChanged, int lastCoffer) + { + int daysSince = (int) Duration.between(lastChanged, Instant.now()).toDays(); + int maxDailyWithdrawal = isRoyalTroubleCompleted() ? MAX_WITHDRAWAL_ROYAL_TROUBLE : MAX_WITHDRAWAL_BASE; + int maxDailyThreshold = maxDailyWithdrawal * 10; + + for (int i = 0; i < daysSince; i++) + { + lastCoffer -= (lastCoffer > maxDailyThreshold) ? maxDailyWithdrawal : lastCoffer / 10; + } + return lastCoffer; + } + + private int estimateApproval(Instant lastChanged, int lastApproval) + { + int daysSince = (int) Duration.between(lastChanged, Instant.now()).toDays(); + float dailyPercentage = isRoyalTroubleCompleted() ? APPROVAL_DECREMENT_ROYAL_TROUBLE : APPROVAL_DECREMENT_BASE; + + lastApproval -= (int) (daysSince * dailyPercentage * MAX_APPROVAL); + return Math.max(lastApproval, 0); + } + private boolean isInKingdom() { return client.getLocalPlayer() != null && KINGDOM_REGION.contains(client.getLocalPlayer().getWorldLocation().getRegionID()); } - private boolean hasCompletedQuest() + private boolean isThroneOfMiscellaniaCompleted() { return client.getVar(VarPlayer.THRONE_OF_MISCELLANIA) > 0; } - static int getFavorPercent(int favor) + private boolean isRoyalTroubleCompleted() { - return (favor * 100) / 127; + return Quest.ROYAL_TROUBLE.getState(client) == QuestState.FINISHED; } + static int getApprovalPercent(int approval) + { + return (approval * 100) / MAX_APPROVAL; + } + + private void sendChatMessage(String chatMessage) + { + final String message = new ChatMessageBuilder() + .append(ChatColorType.HIGHLIGHT) + .append(chatMessage) + .build(); + + chatMessageManager.queue( + QueuedMessage.builder() + .type(ChatMessageType.CONSOLE) + .runeLiteFormattedMessage(message) + .build()); + } + + private Instant getLastChanged() + { + return configManager.getRSProfileConfiguration(KingdomConfig.CONFIG_GROUP_NAME, CONFIG_LAST_CHANGED_KEY, Instant.class); + } + + private void setLastChanged(Instant lastChanged) + { + configManager.setRSProfileConfiguration(KingdomConfig.CONFIG_GROUP_NAME, CONFIG_LAST_CHANGED_KEY, lastChanged); + } + + int getCoffer() + { + Integer coffer = configManager.getRSProfileConfiguration(KingdomConfig.CONFIG_GROUP_NAME, CONFIG_COFFER_KEY, int.class); + return coffer == null ? 0 : coffer; + } + + private void setCoffer(int coffer) + { + configManager.setRSProfileConfiguration(KingdomConfig.CONFIG_GROUP_NAME, CONFIG_COFFER_KEY, coffer); + } + + int getApproval() + { + Integer approval = configManager.getRSProfileConfiguration(KingdomConfig.CONFIG_GROUP_NAME, CONFIG_APPROVAL_KEY, int.class); + return approval == null ? 0 : approval; + } + + private void setApproval(int approval) + { + configManager.setRSProfileConfiguration(KingdomConfig.CONFIG_GROUP_NAME, CONFIG_APPROVAL_KEY, approval); + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/music/MusicPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/music/MusicPlugin.java index a8ef79401f..4fce105ff8 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/music/MusicPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/music/MusicPlugin.java @@ -557,11 +557,16 @@ public class MusicPlugin extends Plugin return; } - for (Widget w : track.getChildren()) + Widget[] trackChildren = track.getChildren(); + + if (trackChildren != null) { - if (w != null) + for (Widget w : trackChildren) { - w.setAction(0, null); + if (w != null) + { + w.setAction(0, null); + } } } @@ -588,7 +593,11 @@ public class MusicPlugin extends Plugin public void shutDown() { super.shutDown(); - handle.setSpriteId(SpriteID.SETTINGS_SLIDER_HANDLE_BLUE); + + if (this.handle != null) + { + handle.setSpriteId(SpriteID.SETTINGS_SLIDER_HANDLE_BLUE); + } this.icon.setOnOpListener((Object[]) null); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/npchighlight/NpcIndicatorsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/npchighlight/NpcIndicatorsPlugin.java index d8fa6e81de..a68a76d184 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/npchighlight/NpcIndicatorsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/npchighlight/NpcIndicatorsPlugin.java @@ -57,6 +57,7 @@ import net.runelite.api.events.GameTick; import net.runelite.api.events.GraphicsObjectCreated; import net.runelite.api.events.MenuEntryAdded; import net.runelite.api.events.MenuOptionClicked; +import net.runelite.api.events.NpcChanged; import net.runelite.api.events.NpcDespawned; import net.runelite.api.events.NpcSpawned; import net.runelite.client.callback.ClientThread; @@ -409,6 +410,26 @@ public class NpcIndicatorsPlugin extends Plugin highlightedNpcs.remove(npc); } + @Subscribe + public void onNpcChanged(NpcChanged event) + { + final NPC npc = event.getNpc(); + final String npcName = npc.getName(); + + highlightedNpcs.remove(npc); + + if (npcName == null) + { + return; + } + + if (npcTags.contains(npc.getIndex()) + || highlightMatchesNPCName(npcName)) + { + highlightedNpcs.add(npc); + } + } + @Subscribe public void onGraphicsObjectCreated(GraphicsObjectCreated event) { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerConfig.java index 3dbbf4564c..fb01be8487 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerConfig.java @@ -32,9 +32,21 @@ import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigItem; import net.runelite.client.config.Units; -@ConfigGroup("slayer") +@ConfigGroup(SlayerConfig.GROUP_NAME) public interface SlayerConfig extends Config { + String GROUP_NAME = "slayer"; + + // Key names for stored task values + String TASK_NAME_KEY = "taskName"; + String AMOUNT_KEY = "amount"; + String INIT_AMOUNT_KEY = "initialAmount"; + String TASK_LOC_KEY = "taskLocation"; + String STREAK_KEY = "streak"; + String POINTS_KEY = "points"; + String EXPEDITIOUS_CHARGES_KEY = "expeditious"; + String SLAUGHTER_CHARGES_KEY = "slaughter"; + @ConfigItem( position = 1, keyName = "infobox", @@ -124,148 +136,4 @@ public interface SlayerConfig extends Config { return true; } - - // Stored data - @ConfigItem( - keyName = "taskName", - name = "", - description = "", - hidden = true - ) - default String taskName() - { - return ""; - } - - @ConfigItem( - keyName = "taskName", - name = "", - description = "" - ) - void taskName(String key); - - @ConfigItem( - keyName = "amount", - name = "", - description = "", - hidden = true - ) - default int amount() - { - return -1; - } - - @ConfigItem( - keyName = "amount", - name = "", - description = "" - ) - void amount(int amt); - - @ConfigItem( - keyName = "initialAmount", - name = "", - description = "", - hidden = true - ) - default int initialAmount() - { - return -1; - } - @ConfigItem( - keyName = "initialAmount", - name = "", - description = "" - ) - void initialAmount(int initialAmount); - - @ConfigItem( - keyName = "taskLocation", - name = "", - description = "", - hidden = true - ) - default String taskLocation() - { - return ""; - } - - @ConfigItem( - keyName = "taskLocation", - name = "", - description = "" - ) - void taskLocation(String key); - - @ConfigItem( - keyName = "streak", - name = "", - description = "", - hidden = true - ) - default int streak() - { - return -1; - } - - @ConfigItem( - keyName = "streak", - name = "", - description = "" - ) - void streak(int streak); - - @ConfigItem( - keyName = "points", - name = "", - description = "", - hidden = true - ) - default int points() - { - return -1; - } - - @ConfigItem( - keyName = "points", - name = "", - description = "" - ) - void points(int points); - - @ConfigItem( - keyName = "expeditious", - name = "", - description = "", - hidden = true - ) - default int expeditious() - { - return -1; - } - - @ConfigItem( - keyName = "expeditious", - name = "", - description = "" - ) - void expeditious(int expeditious); - - @ConfigItem( - keyName = "slaughter", - name = "", - description = "", - hidden = true - ) - default int slaughter() - { - return -1; - } - - @ConfigItem( - keyName = "slaughter", - name = "", - description = "" - ) - void slaughter(int slaughter); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerPlugin.java index db9b247fe3..24816d559e 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerPlugin.java @@ -139,6 +139,9 @@ public class SlayerPlugin extends Plugin @Inject private SlayerConfig config; + @Inject + private ConfigManager configManager; + @Inject private OverlayManager overlayManager; @@ -227,12 +230,17 @@ public class SlayerPlugin extends Plugin { cachedXp = client.getSkillExperience(SLAYER); - if (config.amount() != -1 - && !config.taskName().isEmpty()) + migrateConfig(); + + if (getIntProfileConfig(SlayerConfig.AMOUNT_KEY) != -1 + && !getStringProfileConfig(SlayerConfig.TASK_NAME_KEY).isEmpty()) { - setExpeditiousChargeCount(config.expeditious()); - setSlaughterChargeCount(config.slaughter()); - clientThread.invoke(() -> setTask(config.taskName(), config.amount(), config.initialAmount(), config.taskLocation(), false)); + setExpeditiousChargeCount(getIntProfileConfig(SlayerConfig.EXPEDITIOUS_CHARGES_KEY)); + setSlaughterChargeCount(getIntProfileConfig(SlayerConfig.SLAUGHTER_CHARGES_KEY)); + clientThread.invoke(() -> setTask(getStringProfileConfig(SlayerConfig.TASK_NAME_KEY), + getIntProfileConfig(SlayerConfig.AMOUNT_KEY), + getIntProfileConfig(SlayerConfig.INIT_AMOUNT_KEY), + getStringProfileConfig(SlayerConfig.TASK_LOC_KEY), false)); } } @@ -275,27 +283,50 @@ public class SlayerPlugin extends Plugin taggedNpcs.clear(); break; case LOGGED_IN: - if (config.amount() != -1 - && !config.taskName().isEmpty() + migrateConfig(); + if (getIntProfileConfig(SlayerConfig.AMOUNT_KEY) != -1 + && !getStringProfileConfig(SlayerConfig.TASK_NAME_KEY).isEmpty() && loginFlag) { - setExpeditiousChargeCount(config.expeditious()); - setSlaughterChargeCount(config.slaughter()); - setTask(config.taskName(), config.amount(), config.initialAmount(), config.taskLocation(), false); + setExpeditiousChargeCount(getIntProfileConfig(SlayerConfig.EXPEDITIOUS_CHARGES_KEY)); + setSlaughterChargeCount(getIntProfileConfig(SlayerConfig.SLAUGHTER_CHARGES_KEY)); + setTask(getStringProfileConfig(SlayerConfig.TASK_NAME_KEY), + getIntProfileConfig(SlayerConfig.AMOUNT_KEY), + getIntProfileConfig(SlayerConfig.INIT_AMOUNT_KEY), + getStringProfileConfig(SlayerConfig.TASK_LOC_KEY), false); loginFlag = false; } break; } } + @VisibleForTesting + int getIntProfileConfig(String key) + { + Integer value = configManager.getRSProfileConfiguration(SlayerConfig.GROUP_NAME, key, int.class); + return value == null ? -1 : value; + } + + @VisibleForTesting + String getStringProfileConfig(String key) + { + String value = configManager.getRSProfileConfiguration(SlayerConfig.GROUP_NAME, key, String.class); + return value == null ? "" : value; + } + + private void setProfileConfig(String key, Object value) + { + configManager.setRSProfileConfiguration(SlayerConfig.GROUP_NAME, key, value); + } + private void save() { - config.amount(amount); - config.initialAmount(initialAmount); - config.taskName(taskName); - config.taskLocation(taskLocation); - config.expeditious(expeditiousChargeCount); - config.slaughter(slaughterChargeCount); + setProfileConfig(SlayerConfig.AMOUNT_KEY, amount); + setProfileConfig(SlayerConfig.INIT_AMOUNT_KEY, initialAmount); + setProfileConfig(SlayerConfig.TASK_NAME_KEY, taskName); + setProfileConfig(SlayerConfig.TASK_LOC_KEY, taskLocation); + setProfileConfig(SlayerConfig.EXPEDITIOUS_CHARGES_KEY, expeditiousChargeCount); + setProfileConfig(SlayerConfig.SLAUGHTER_CHARGES_KEY, slaughterChargeCount); } @Subscribe @@ -345,7 +376,7 @@ public class SlayerPlugin extends Plugin int amount = Integer.parseInt(mAssignBoss.group(2)); setTask(mAssignBoss.group(1), amount, amount); int points = Integer.parseInt(mAssignBoss.group(3).replaceAll(",", "")); - config.points(points); + setProfileConfig(SlayerConfig.POINTS_KEY, points); } else if (mCurrent.find()) { @@ -363,12 +394,12 @@ public class SlayerPlugin extends Plugin if (braceletText.contains("bracelet of slaughter")) { slaughterChargeCount = SLAUGHTER_CHARGE; - config.slaughter(slaughterChargeCount); + setProfileConfig(SlayerConfig.SLAUGHTER_CHARGES_KEY, slaughterChargeCount); } else if (braceletText.contains("expeditious bracelet")) { expeditiousChargeCount = EXPEDITIOUS_CHARGE; - config.expeditious(expeditiousChargeCount); + setProfileConfig(SlayerConfig.EXPEDITIOUS_CHARGES_KEY, expeditiousChargeCount); } } @@ -380,12 +411,12 @@ public class SlayerPlugin extends Plugin Matcher mPoints = REWARD_POINTS.matcher(w.getText()); if (mPoints.find()) { - final int prevPoints = config.points(); + final int prevPoints = getIntProfileConfig(SlayerConfig.POINTS_KEY); int points = Integer.parseInt(mPoints.group(1).replaceAll(",", "")); if (prevPoints != points) { - config.points(points); + setProfileConfig(SlayerConfig.POINTS_KEY, points); removeCounter(); addCounter(); } @@ -426,7 +457,7 @@ public class SlayerPlugin extends Plugin amount++; slaughterChargeCount = mSlaughter.find() ? Integer.parseInt(mSlaughter.group(1)) : SLAUGHTER_CHARGE; - config.slaughter(slaughterChargeCount); + setProfileConfig(SlayerConfig.SLAUGHTER_CHARGES_KEY, slaughterChargeCount); } if (chatMsg.startsWith(CHAT_BRACELET_EXPEDITIOUS)) @@ -435,7 +466,7 @@ public class SlayerPlugin extends Plugin amount--; expeditiousChargeCount = mExpeditious.find() ? Integer.parseInt(mExpeditious.group(1)) : EXPEDITIOUS_CHARGE; - config.expeditious(expeditiousChargeCount); + setProfileConfig(SlayerConfig.EXPEDITIOUS_CHARGES_KEY, expeditiousChargeCount); } if (chatMsg.startsWith(CHAT_BRACELET_EXPEDITIOUS_CHARGE)) @@ -448,7 +479,7 @@ public class SlayerPlugin extends Plugin } expeditiousChargeCount = Integer.parseInt(mExpeditious.group(1)); - config.expeditious(expeditiousChargeCount); + setProfileConfig(SlayerConfig.EXPEDITIOUS_CHARGES_KEY, expeditiousChargeCount); } if (chatMsg.startsWith(CHAT_BRACELET_SLAUGHTER_CHARGE)) @@ -460,7 +491,7 @@ public class SlayerPlugin extends Plugin } slaughterChargeCount = Integer.parseInt(mSlaughter.group(1)); - config.slaughter(slaughterChargeCount); + setProfileConfig(SlayerConfig.SLAUGHTER_CHARGES_KEY, slaughterChargeCount); } if (chatMsg.startsWith("You've completed") && (chatMsg.contains("Slayer master") || chatMsg.contains("Slayer Master"))) @@ -479,12 +510,12 @@ public class SlayerPlugin extends Plugin if (mTasks != null) { int streak = Integer.parseInt(mTasks.replace(",", "")); - config.streak(streak); + setProfileConfig(SlayerConfig.STREAK_KEY, streak); } if (mPoints != null) { int points = Integer.parseInt(mPoints.replace(",", "")); - config.points(points); + setProfileConfig(SlayerConfig.POINTS_KEY, points); } } @@ -597,7 +628,7 @@ public class SlayerPlugin extends Plugin @Subscribe private void onConfigChanged(ConfigChanged event) { - if (!event.getGroup().equals("slayer") || !event.getKey().equals("infobox")) + if (!event.getGroup().equals(SlayerConfig.GROUP_NAME) || !event.getKey().equals("infobox")) { return; } @@ -627,7 +658,8 @@ public class SlayerPlugin extends Plugin amount--; } - config.amount(amount); // save changed value + // save changed value + setProfileConfig(SlayerConfig.AMOUNT_KEY, amount); if (!config.showInfobox()) { @@ -772,7 +804,7 @@ public class SlayerPlugin extends Plugin } counter = new TaskCounter(taskImg, this, amount); - counter.setTooltip(String.format(taskTooltip, capsString(taskName), config.points(), config.streak())); + counter.setTooltip(String.format(taskTooltip, capsString(taskName), getIntProfileConfig(SlayerConfig.POINTS_KEY), getIntProfileConfig(SlayerConfig.STREAK_KEY))); infoBoxManager.addInfoBox(counter); } @@ -891,4 +923,26 @@ public class SlayerPlugin extends Plugin { return str.substring(0, 1).toUpperCase() + str.substring(1); } + + private void migrateConfig() + { + migrateConfigKey(SlayerConfig.TASK_NAME_KEY); + migrateConfigKey(SlayerConfig.AMOUNT_KEY); + migrateConfigKey(SlayerConfig.INIT_AMOUNT_KEY); + migrateConfigKey(SlayerConfig.TASK_LOC_KEY); + migrateConfigKey(SlayerConfig.STREAK_KEY); + migrateConfigKey(SlayerConfig.POINTS_KEY); + migrateConfigKey(SlayerConfig.EXPEDITIOUS_CHARGES_KEY); + migrateConfigKey(SlayerConfig.SLAUGHTER_CHARGES_KEY); + } + + private void migrateConfigKey(String key) + { + Object value = configManager.getConfiguration(SlayerConfig.GROUP_NAME, key); + if (value != null) + { + configManager.unsetConfiguration(SlayerConfig.GROUP_NAME, key); + configManager.setRSProfileConfiguration(SlayerConfig.GROUP_NAME, key, value); + } + } } diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/chatnotifications/ChatNotificationsPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/chatnotifications/ChatNotificationsPluginTest.java index 61606df725..053e5775aa 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/chatnotifications/ChatNotificationsPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/chatnotifications/ChatNotificationsPluginTest.java @@ -44,7 +44,10 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import org.mockito.junit.MockitoJUnitRunner; @@ -286,4 +289,46 @@ public class ChatNotificationsPluginTest // set value uses our player name, which has nbsp replaced verify(messageNode).setValue("Logic Knot received a drop: Adamant longsword"); } -} \ No newline at end of file + + @Test + public void testLocalPlayerSelfMention() + { + final String localPlayerName = "Broo klyn"; + + MessageNode messageNode = mock(MessageNode.class); + + Player localPlayer = mock(Player.class); + when(client.getLocalPlayer()).thenReturn(localPlayer); + when(localPlayer.getName()).thenReturn(localPlayerName); + + lenient().when(config.highlightOwnName()).thenReturn(true); + lenient().when(messageNode.getValue()).thenReturn("Spread love it's the Broo klyn way"); + + ChatMessage chatMessage = new ChatMessage(); + chatMessage.setType(ChatMessageType.PUBLICCHAT); + chatMessage.setName("Broo\u00a0klyn"); + chatMessage.setMessageNode(messageNode); + + chatNotificationsPlugin.onChatMessage(chatMessage); + + verify(messageNode, times(0)).setValue(any()); + } + + @Test + public void testPrivateChatOutReturn() + { + MessageNode messageNode = mock(MessageNode.class); + + lenient().when(config.highlightWordsString()).thenReturn("Brooklyn"); + lenient().when(messageNode.getValue()).thenReturn("Spread love it's the Brooklyn way"); + + ChatMessage chatMessage = new ChatMessage(); + chatMessage.setType(ChatMessageType.PRIVATECHATOUT); + chatMessage.setMessageNode(messageNode); + + chatNotificationsPlugin.startUp(); + chatNotificationsPlugin.onChatMessage(chatMessage); + + verify(messageNode, times(0)).setValue(any()); + } +} diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/cooking/CookingPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/cooking/CookingPluginTest.java new file mode 100644 index 0000000000..3c470e0586 --- /dev/null +++ b/runelite-client/src/test/java/net/runelite/client/plugins/cooking/CookingPluginTest.java @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2019, Adam + * 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.plugins.cooking; + +import com.google.inject.Guice; +import com.google.inject.testing.fieldbinder.Bind; +import com.google.inject.testing.fieldbinder.BoundFieldModule; +import javax.inject.Inject; +import net.runelite.api.ChatMessageType; +import net.runelite.api.Client; +import net.runelite.api.GraphicID; +import net.runelite.api.Player; +import net.runelite.api.events.ChatMessage; +import net.runelite.api.events.GraphicChanged; +import net.runelite.client.game.ItemManager; +import net.runelite.client.ui.overlay.OverlayManager; +import net.runelite.client.ui.overlay.infobox.InfoBoxManager; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.mockito.ArgumentMatchers.any; +import org.mockito.Mock; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class CookingPluginTest +{ + private static final String[] COOKING_MESSAGES = { + "You successfully cook a shark.", + "You successfully cook an anglerfish.", + "You manage to cook a tuna.", + "You cook the karambwan. It looks delicious.", + "You roast a lobster.", + "You cook a bass.", + "You successfully bake a tasty garden pie.", + "You dry a piece of meat and extract the sinew." + }; + + private static final String incenseBurnerMessage = "You burn some marrentill in the incense burner."; + + @Inject + CookingPlugin cookingPlugin; + + @Mock + @Bind + Client client; + + @Mock + @Bind + InfoBoxManager infoBoxManager; + + @Mock + @Bind + ItemManager itemManager; + + @Mock + @Bind + CookingConfig config; + + @Mock + @Bind + CookingOverlay cookingOverlay; + + @Mock + @Bind + OverlayManager overlayManager; + + @Before + public void before() + { + Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this); + } + + @Test + public void testOnChatMessage() + { + for (String message : COOKING_MESSAGES) + { + ChatMessage chatMessage = new ChatMessage(null, ChatMessageType.SPAM, "", message, "", 0); + cookingPlugin.onChatMessage(chatMessage); + } + + ChatMessage chatMessage = new ChatMessage(null, ChatMessageType.SPAM, "", incenseBurnerMessage, "", 0); + cookingPlugin.onChatMessage(chatMessage); + + CookingSession cookingSession = cookingPlugin.getSession(); + assertNotNull(cookingSession); + assertEquals(COOKING_MESSAGES.length, cookingSession.getCookAmount()); + assertEquals(0, cookingSession.getBurnAmount()); + } + + @Test + public void testOnGraphicChanged() + { + Player player = mock(Player.class); + when(player.getGraphic()).thenReturn(GraphicID.WINE_MAKE); + + when(config.fermentTimer()).thenReturn(true); + when(client.getLocalPlayer()).thenReturn(player); + + GraphicChanged graphicChanged = new GraphicChanged(); + graphicChanged.setActor(player); + cookingPlugin.onGraphicChanged(graphicChanged); + + verify(infoBoxManager).addInfoBox(any(FermentTimer.class)); + } +} \ No newline at end of file diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/npchighlight/NpcIndicatorsPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/npchighlight/NpcIndicatorsPluginTest.java new file mode 100644 index 0000000000..13c3f572d9 --- /dev/null +++ b/runelite-client/src/test/java/net/runelite/client/plugins/npchighlight/NpcIndicatorsPluginTest.java @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2018, Tomas Slusny + * 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.plugins.npchighlight; + +import com.google.inject.Guice; +import com.google.inject.testing.fieldbinder.Bind; +import com.google.inject.testing.fieldbinder.BoundFieldModule; +import java.awt.Color; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.ScheduledExecutorService; +import javax.inject.Inject; +import net.runelite.api.Client; +import net.runelite.api.MenuAction; +import net.runelite.api.MenuEntry; +import net.runelite.api.NPC; +import net.runelite.api.events.MenuEntryAdded; +import net.runelite.api.events.NpcChanged; +import net.runelite.api.events.NpcSpawned; +import net.runelite.client.ui.overlay.OverlayManager; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class NpcIndicatorsPluginTest +{ + @Mock + @Bind + private Client client; + + @Mock + @Bind + private ScheduledExecutorService executorService; + + @Mock + @Bind + private NpcIndicatorsConfig npcIndicatorsConfig; + + @Inject + private NpcIndicatorsPlugin npcIndicatorsPlugin; + + @Mock + @Bind + private OverlayManager overlayManager; + + @Before + public void setUp() + { + Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this); + } + + @Test + public void getHighlights() + { + when(npcIndicatorsConfig.getNpcToHighlight()).thenReturn("goblin, , zulrah , *wyvern, ,"); + final List highlightedNpcs = npcIndicatorsPlugin.getHighlights(); + assertEquals("Length of parsed NPCs is incorrect", 3, highlightedNpcs.size()); + + final Iterator iterator = highlightedNpcs.iterator(); + assertEquals("goblin", iterator.next()); + assertEquals("zulrah", iterator.next()); + assertEquals("*wyvern", iterator.next()); + } + + @Test + @Ignore + public void testDeadNpcMenuHighlight() + { + when(npcIndicatorsConfig.getNpcToHighlight()).thenReturn("goblin"); + when(npcIndicatorsConfig.deadNpcMenuColor()).thenReturn(Color.RED); + + npcIndicatorsPlugin.rebuildAllNpcs(); + + NPC npc = mock(NPC.class); + when(npc.getName()).thenReturn("Goblin"); + when(npc.isDead()).thenReturn(true); + npcIndicatorsPlugin.onNpcSpawned(new NpcSpawned(npc)); + + when(client.getCachedNPCs()).thenReturn(new NPC[]{npc}); // id 0 + + when(client.getMenuEntries()).thenReturn(new MenuEntry[]{new MenuEntry()}); + MenuEntryAdded menuEntryAdded = new MenuEntryAdded("", "Goblin", MenuAction.NPC_FIRST_OPTION.getId(), 0, -1, -1, false); + npcIndicatorsPlugin.onMenuEntryAdded(menuEntryAdded); + + MenuEntry target = new MenuEntry(); + target.setTarget("Goblin"); // red + verify(client).setMenuEntries(new MenuEntry[]{target}); + } + + @Test + @Ignore + public void testAliveNpcMenuHighlight() + { + when(npcIndicatorsConfig.getNpcToHighlight()).thenReturn("goblin"); + when(npcIndicatorsConfig.highlightMenuNames()).thenReturn(true); + when(npcIndicatorsConfig.getHighlightColor()).thenReturn(Color.BLUE); + + npcIndicatorsPlugin.rebuildAllNpcs(); + + NPC npc = mock(NPC.class); + when(npc.getName()).thenReturn("Goblin"); + npcIndicatorsPlugin.onNpcSpawned(new NpcSpawned(npc)); + + when(client.getCachedNPCs()).thenReturn(new NPC[]{npc}); // id 0 + + when(client.getMenuEntries()).thenReturn(new MenuEntry[]{new MenuEntry()}); + MenuEntryAdded menuEntryAdded = new MenuEntryAdded("", "Goblin", MenuAction.NPC_FIRST_OPTION.getId(), 0, -1, -1, false); + npcIndicatorsPlugin.onMenuEntryAdded(menuEntryAdded); + + MenuEntry target = new MenuEntry(); + target.setTarget("Goblin"); // blue + verify(client).setMenuEntries(new MenuEntry[]{target}); + } + + @Test + public void taggedNpcChanges() + { + when(npcIndicatorsConfig.getNpcToHighlight()).thenReturn("Joseph"); + + npcIndicatorsPlugin.rebuildAllNpcs(); + + NPC npc = mock(NPC.class); + when(npc.getName()).thenReturn("Joseph"); + npcIndicatorsPlugin.onNpcSpawned(new NpcSpawned(npc)); + + assertTrue(npcIndicatorsPlugin.getHighlightedNpcs().contains(npc)); + + when(npc.getName()).thenReturn("Werewolf"); + npcIndicatorsPlugin.onNpcChanged(new NpcChanged(npc, null)); + + assertFalse(npcIndicatorsPlugin.getHighlightedNpcs().contains(npc)); + } + + @Test + public void npcChangesToTagged() + { + when(npcIndicatorsConfig.getNpcToHighlight()).thenReturn("Werewolf"); + + npcIndicatorsPlugin.rebuildAllNpcs(); + + NPC npc = mock(NPC.class); + when(npc.getName()).thenReturn("Joseph"); + npcIndicatorsPlugin.onNpcSpawned(new NpcSpawned(npc)); + + assertFalse(npcIndicatorsPlugin.getHighlightedNpcs().contains(npc)); + + when(npc.getName()).thenReturn("Werewolf"); + npcIndicatorsPlugin.onNpcChanged(new NpcChanged(npc, null)); + + assertTrue(npcIndicatorsPlugin.getHighlightedNpcs().contains(npc)); + } +} diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/slayer/SlayerPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/slayer/SlayerPluginTest.java index 967802b2cf..21af0f0538 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/slayer/SlayerPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/slayer/SlayerPluginTest.java @@ -53,6 +53,7 @@ import net.runelite.api.widgets.WidgetInfo; import net.runelite.client.Notifier; import net.runelite.client.chat.ChatCommandManager; import net.runelite.client.chat.ChatMessageManager; +import net.runelite.client.config.ConfigManager; import net.runelite.client.game.ItemManager; import net.runelite.client.ui.overlay.OverlayManager; import net.runelite.client.ui.overlay.infobox.InfoBoxManager; @@ -64,6 +65,7 @@ import org.junit.runner.RunWith; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import org.mockito.Mock; +import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -137,6 +139,10 @@ public class SlayerPluginTest @Bind Client client; + @Mock + @Bind + ConfigManager configManager; + @Mock @Bind SlayerConfig slayerConfig; @@ -283,7 +289,7 @@ public class SlayerPluginTest assertEquals("Vet'ion", slayerPlugin.getTaskName()); assertEquals(3, slayerPlugin.getAmount()); - verify(slayerConfig).points(914); + verify(configManager).setRSProfileConfiguration(SlayerConfig.GROUP_NAME, SlayerConfig.POINTS_KEY, 914); } @Test @@ -296,7 +302,7 @@ public class SlayerPluginTest assertEquals("Chaos Elemental", slayerPlugin.getTaskName()); assertEquals(3, slayerPlugin.getAmount()); - verify(slayerConfig).points(914); + verify(configManager).setRSProfileConfiguration(SlayerConfig.GROUP_NAME, SlayerConfig.POINTS_KEY, 914); } @Test @@ -309,7 +315,7 @@ public class SlayerPluginTest assertEquals("Alchemical Hydra", slayerPlugin.getTaskName()); assertEquals(35, slayerPlugin.getAmount()); - verify(slayerConfig).points(724); + verify(configManager).setRSProfileConfiguration(SlayerConfig.GROUP_NAME, SlayerConfig.POINTS_KEY, 724); } @Test @@ -440,7 +446,7 @@ public class SlayerPluginTest when(client.getWidget(WidgetInfo.SLAYER_REWARDS_TOPBAR)).thenReturn(rewardBar); slayerPlugin.onGameTick(new GameTick()); - verify(slayerConfig).points(17566); + verify(configManager).setRSProfileConfiguration(SlayerConfig.GROUP_NAME, SlayerConfig.POINTS_KEY, 17566); } @Test @@ -449,7 +455,7 @@ public class SlayerPluginTest ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "Perterter", TASK_ONE, null, 0); slayerPlugin.onChatMessage(chatMessageEvent); - verify(slayerConfig).streak(1); + verify(configManager).setRSProfileConfiguration(SlayerConfig.GROUP_NAME, SlayerConfig.STREAK_KEY, 1); assertEquals("", slayerPlugin.getTaskName()); assertEquals(0, slayerPlugin.getAmount()); } @@ -460,7 +466,7 @@ public class SlayerPluginTest ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "Perterter", TASK_COMPLETE_NO_POINTS, null, 0); slayerPlugin.onChatMessage(chatMessageEvent); - verify(slayerConfig).streak(3); + verify(configManager).setRSProfileConfiguration(SlayerConfig.GROUP_NAME, SlayerConfig.STREAK_KEY, 3); assertEquals("", slayerPlugin.getTaskName()); assertEquals(0, slayerPlugin.getAmount()); } @@ -471,10 +477,10 @@ public class SlayerPluginTest ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "Perterter", TASK_POINTS, null, 0); slayerPlugin.onChatMessage(chatMessageEvent); - verify(slayerConfig).streak(9); + verify(configManager).setRSProfileConfiguration(SlayerConfig.GROUP_NAME, SlayerConfig.STREAK_KEY, 9); assertEquals("", slayerPlugin.getTaskName()); assertEquals(0, slayerPlugin.getAmount()); - verify(slayerConfig).points(18_000); + verify(configManager).setRSProfileConfiguration(SlayerConfig.GROUP_NAME, SlayerConfig.POINTS_KEY, 18_000); } @Test @@ -483,10 +489,10 @@ public class SlayerPluginTest ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "Perterter", TASK_LARGE_STREAK, null, 0); slayerPlugin.onChatMessage(chatMessageEvent); - verify(slayerConfig).streak(2465); + verify(configManager).setRSProfileConfiguration(SlayerConfig.GROUP_NAME, SlayerConfig.STREAK_KEY, 2465); assertEquals("", slayerPlugin.getTaskName()); assertEquals(0, slayerPlugin.getAmount()); - verify(slayerConfig).points(131_071); + verify(configManager).setRSProfileConfiguration(SlayerConfig.GROUP_NAME, SlayerConfig.POINTS_KEY, 131_071); } @Test @@ -495,7 +501,7 @@ public class SlayerPluginTest ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "Perterter", TASK_COMPETE_TURAEL, null, 0); slayerPlugin.onChatMessage(chatMessageEvent); - verify(slayerConfig).streak(104); + verify(configManager).setRSProfileConfiguration(SlayerConfig.GROUP_NAME, SlayerConfig.STREAK_KEY, 104); assertEquals("", slayerPlugin.getTaskName()); assertEquals(0, slayerPlugin.getAmount()); } @@ -506,8 +512,8 @@ public class SlayerPluginTest ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "", TASK_MAX_STREAK, null, 0); slayerPlugin.onChatMessage(chatMessageEvent); - verify(slayerConfig).streak(16_000); - verify(slayerConfig).points(131_071); + verify(configManager).setRSProfileConfiguration(SlayerConfig.GROUP_NAME, SlayerConfig.STREAK_KEY, 16000); + verify(configManager).setRSProfileConfiguration(SlayerConfig.GROUP_NAME, SlayerConfig.POINTS_KEY, 131_071); assertEquals("", slayerPlugin.getTaskName()); assertEquals(0, slayerPlugin.getAmount()); } @@ -518,8 +524,8 @@ public class SlayerPluginTest ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "", TASK_MAX_POINTS, null, 0); slayerPlugin.onChatMessage(chatMessageEvent); - verify(slayerConfig).streak(9); - verify(slayerConfig).points(131_071); + verify(configManager).setRSProfileConfiguration(SlayerConfig.GROUP_NAME, SlayerConfig.STREAK_KEY, 9); + verify(configManager).setRSProfileConfiguration(SlayerConfig.GROUP_NAME, SlayerConfig.POINTS_KEY, 131_071); assertEquals("", slayerPlugin.getTaskName()); assertEquals(0, slayerPlugin.getAmount()); } @@ -530,8 +536,8 @@ public class SlayerPluginTest ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "", TASK_WILDERNESS, null, 0); slayerPlugin.onChatMessage(chatMessageEvent); - verify(slayerConfig).streak(9); - verify(slayerConfig).points(18_000); + verify(configManager).setRSProfileConfiguration(SlayerConfig.GROUP_NAME, SlayerConfig.STREAK_KEY, 9); + verify(configManager).setRSProfileConfiguration(SlayerConfig.GROUP_NAME, SlayerConfig.POINTS_KEY, 18_000); assertEquals("", slayerPlugin.getTaskName()); assertEquals(0, slayerPlugin.getAmount()); } @@ -919,7 +925,10 @@ public class SlayerPluginTest @Test public void infoboxNotAddedOnLogin() { - when(slayerConfig.taskName()).thenReturn(Task.BLOODVELD.getName()); + when(slayerPlugin.getStringProfileConfig(SlayerConfig.TASK_NAME_KEY)).thenReturn(Task.BLOODVELD.getName()); + when(slayerPlugin.getIntProfileConfig(SlayerConfig.AMOUNT_KEY)).thenReturn(50); + // Lenient required as this is not called assuming correct plugin logic + lenient().when(slayerConfig.showInfobox()).thenReturn(true); GameStateChanged loggingIn = new GameStateChanged(); loggingIn.setGameState(GameState.LOGGING_IN); diff --git a/runelite-jshell/runelite-jshell.gradle.kts b/runelite-jshell/runelite-jshell.gradle.kts new file mode 100644 index 0000000000..cf3a3e54f9 --- /dev/null +++ b/runelite-jshell/runelite-jshell.gradle.kts @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2019 Owain van Brakel + * 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. + */ + +description = "RuneLite JShell" + +dependencies { + annotationProcessor(group = "org.projectlombok", name = "lombok", version = "1.18.4") + + compileOnly(group = "org.projectlombok", name = "lombok", version = "1.18.4") + + implementation(group = "com.google.code.findbugs", name = "jsr305", version = "3.0.2") + implementation(group = "com.google.inject", name = "guice", version = "4.1.0", classifier = "no_aop") + implementation(group = "com.fifesoft", name = "rsyntaxtextarea", version = "3.1.2") + implementation(group = "com.fifesoft", name = "autocomplete", version = "3.1.1") + implementation(group = "org.slf4j", name = "slf4j-api", version = "1.7.12") +} \ No newline at end of file diff --git a/runelite-jshell/src/main/java/net/runelite/jshell/JShellAutocompleteProvider.java b/runelite-jshell/src/main/java/net/runelite/jshell/JShellAutocompleteProvider.java new file mode 100644 index 0000000000..2a0a2d8418 --- /dev/null +++ b/runelite-jshell/src/main/java/net/runelite/jshell/JShellAutocompleteProvider.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2021 Abex + * 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.jshell; + +import java.awt.Point; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import javax.swing.text.JTextComponent; +import jdk.jshell.JShell; +import jdk.jshell.SourceCodeAnalysis; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.fife.ui.autocomplete.BasicCompletion; +import org.fife.ui.autocomplete.Completion; +import org.fife.ui.autocomplete.CompletionProviderBase; +import org.fife.ui.autocomplete.ParameterizedCompletion; + +@Slf4j +@RequiredArgsConstructor +public class JShellAutocompleteProvider extends CompletionProviderBase +{ + private final JShell shell; + private String anchorText; + private List completions; + + @Override + protected List getCompletionsImpl(JTextComponent comp) + { + return completions; + } + + @Override + public String getAlreadyEnteredText(JTextComponent comp) + { + complete(comp); + return anchorText; + } + + private void complete(JTextComponent comp) + { + completions = Collections.emptyList(); + + String src = comp.getText(); + int cursor = comp.getCaretPosition(); + + for (int offset = 0; offset < src.length() && cursor >= offset; ) + { + var snipSrc = src.substring(offset); + int thisOffset = offset; + var ci = shell.sourceCodeAnalysis().analyzeCompletion(snipSrc); + offset = src.length() - ci.remaining().length(); + boolean mayHaveMore = ci.completeness() == SourceCodeAnalysis.Completeness.COMPLETE_WITH_SEMI + || ci.completeness() == SourceCodeAnalysis.Completeness.COMPLETE; + + if (cursor <= offset || !mayHaveMore) + { + var anchor = new int[1]; + + completions = shell.sourceCodeAnalysis() + .completionSuggestions(snipSrc, cursor - thisOffset, anchor) + .stream() + .filter(v -> !v.continuation().startsWith("$")) + .map(s -> + { + return new BasicCompletion(this, s.continuation()); + }) + .collect(Collectors.toList()); + anchorText = snipSrc.substring(anchor[0], cursor - thisOffset); + break; + } + } + + if (completions.isEmpty()) + { + anchorText = null; + } + } + + @Override + public List getCompletionsAt(JTextComponent comp, Point p) + { + return Collections.emptyList(); + } + + @Override + public boolean isAutoActivateOkay(JTextComponent comp) + { + // try not to start autocomplete when it has no useful context + String text = comp.getText(); + for (int i = comp.getCaretPosition(); i >= 0; i--) + { + char c = text.charAt(i); + if (Character.isJavaIdentifierPart(c) || c == '.' || c == '(') + { + return true; + } + if (Character.isWhitespace(c)) + { + continue; + } + + return false; + } + + return false; + } + + @Override + public List getParameterizedCompletions(JTextComponent tc) + { + return Collections.emptyList(); + } +} + diff --git a/runelite-jshell/src/main/java/net/runelite/jshell/RLShellExecutionControl.java b/runelite-jshell/src/main/java/net/runelite/jshell/RLShellExecutionControl.java new file mode 100644 index 0000000000..76a32c6aac --- /dev/null +++ b/runelite-jshell/src/main/java/net/runelite/jshell/RLShellExecutionControl.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021 Abex + * 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.jshell; + +import java.util.Map; +import jdk.jshell.execution.DirectExecutionControl; +import jdk.jshell.spi.ExecutionControl; +import jdk.jshell.spi.ExecutionControlProvider; +import jdk.jshell.spi.ExecutionEnv; + +public class RLShellExecutionControl extends DirectExecutionControl implements ExecutionControlProvider +{ + public RLShellExecutionControl() + { + } + + @Override + public String name() + { + return getClass().getName(); + } + + @Override + public ExecutionControl generate(ExecutionEnv env, Map parameters) throws Throwable + { + return this; + } +} diff --git a/runelite-jshell/src/main/java/net/runelite/jshell/RemappingThrowable.java b/runelite-jshell/src/main/java/net/runelite/jshell/RemappingThrowable.java new file mode 100644 index 0000000000..7fc5e6342d --- /dev/null +++ b/runelite-jshell/src/main/java/net/runelite/jshell/RemappingThrowable.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2021 Abex + * 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.jshell; + +import com.google.common.base.Strings; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Stream; +import jdk.jshell.EvalException; + +class RemappingThrowable extends Throwable +{ + private final String source; + private final Map offsets; + private final Throwable wrapped; + private final Map dejaVu; + + public RemappingThrowable(String source, Map offsets, Throwable other) + { + this(source, offsets, other, new HashMap<>()); + } + + private RemappingThrowable(String source, Map offsets, Throwable other, Map dejaVu) + { + super(); + + this.source = source; + this.offsets = offsets; + this.wrapped = other; + this.dejaVu = dejaVu; + + dejaVu.put(wrapped, this); + + setStackTrace(Stream.of(wrapped.getStackTrace()) + .map(e -> + { + Integer boxOffset = offsets.get(e.getFileName()); + if (boxOffset == null) + { + return e; + } + + int offset = boxOffset; + int line = e.getLineNumber(); + for (int i = 0; i <= offset && i < source.length(); i++) + { + if (source.charAt(i) == '\n') + { + line++; + } + } + return new StackTraceElement( + Strings.isNullOrEmpty(e.getClassName()) ? "Shell" : e.getClassName(), + Strings.isNullOrEmpty(e.getMethodName()) ? "global" : e.getMethodName(), + "", + line); + }) + .toArray(StackTraceElement[]::new)); + + if (wrapped.getCause() != null) + { + initCause(remap(wrapped.getCause())); + } + + for (Throwable suppressed : wrapped.getSuppressed()) + { + addSuppressed(remap(suppressed)); + } + } + + private Throwable remap(Throwable other) + { + Throwable remap = dejaVu.get(other); + if (remap == null) + { + remap = new RemappingThrowable(source, offsets, other, dejaVu); + // ctor inserts into the map + } + return remap; + } + + @Override + public String getMessage() + { + return wrapped.getMessage(); + } + + @Override + public String getLocalizedMessage() + { + return wrapped.getLocalizedMessage(); + } + + @Override + public synchronized Throwable fillInStackTrace() + { + return this; + } + + @Override + public String toString() + { + String className; + if (wrapped instanceof EvalException) + { + className = ((EvalException) wrapped).getExceptionClassName(); + } + else + { + className = wrapped.getClass().getName(); + } + + String message = wrapped.getLocalizedMessage(); + if (message == null) + { + return className; + } + return className + ": " + message; + } +} diff --git a/runelite-jshell/src/main/java/net/runelite/jshell/ShellPanel.java b/runelite-jshell/src/main/java/net/runelite/jshell/ShellPanel.java new file mode 100644 index 0000000000..1fd17a5711 --- /dev/null +++ b/runelite-jshell/src/main/java/net/runelite/jshell/ShellPanel.java @@ -0,0 +1,447 @@ +/* + * Copyright (c) 2021 Abex + * 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.jshell; + +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableSet; +import com.google.inject.Injector; +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.RenderingHints; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.io.IOException; +import java.lang.reflect.Method; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Set; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.Semaphore; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javax.swing.JButton; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.JTextArea; +import javax.swing.SwingUtilities; +import javax.swing.text.BadLocationException; +import javax.swing.text.Segment; +import jdk.jshell.Diag; +import jdk.jshell.JShell; +import jdk.jshell.Snippet; +import jdk.jshell.SnippetEvent; +import jdk.jshell.SourceCodeAnalysis; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.fife.ui.autocomplete.AutoCompletion; +import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; +import org.fife.ui.rsyntaxtextarea.SyntaxConstants; +import org.fife.ui.rsyntaxtextarea.Theme; +import org.fife.ui.rtextarea.RTextScrollPane; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Slf4j +public abstract class ShellPanel extends JPanel +{ + private final ScheduledExecutorService executor; + + private final RSyntaxTextArea textArea; + private final JTextArea console = new JTextArea(); + + @Getter + private final Logger shellLogger; + + private final List cleanup = new ArrayList<>(); + + private RLShellExecutionControl exec; + private JShell shell; + private Set prelude; + private Injector injector; + private AutoCompletion autoCompletion; + + public static ShellPanel INSTANCE; + + public ShellPanel(ScheduledExecutorService executor) + { + this.executor = executor; + + Font codeFont = Stream.of( + "Source code pro", + "DejaVu Sans Code", + "Consolas", + Font.MONOSPACED) + .map(name -> new Font(name, Font.PLAIN, 12)) + .filter(f -> !"Dialog.plain".equals(f.getFontName())) + .findFirst() + .get(); + + setLayout(new BorderLayout()); + + JPanel topPanel = new JPanel(); + topPanel.setLayout(new FlowLayout(FlowLayout.RIGHT)); + + JButton run = new JButton("⯈"); + run.setToolTipText("Run"); + run.addActionListener(ev -> run()); + topPanel.add(run); + + JButton clear = new JButton("🗑"); + run.setToolTipText("Clear console"); + clear.addActionListener(ev -> console.setText("")); + topPanel.add(clear); + + add(topPanel, BorderLayout.NORTH); + + textArea = new RSyntaxTextArea(); + + try + { + // RSyntaxTextArea::setAntiAliasingEnabled actually forces it to match the platform's + // default, which is pointless + var map = new HashMap(); + map.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + + var f = RSyntaxTextArea.class.getDeclaredField("aaHints"); + f.setAccessible(true); + f.set(textArea, map); + } + catch (ReflectiveOperationException e) + { + throw new RuntimeException(e); + } + + textArea.setFont(codeFont); + textArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA); + textArea.setAutoIndentEnabled(true); + textArea.setPaintTabLines(true); + textArea.setShowMatchedBracketPopup(true); + textArea.setCloseCurlyBraces(false); + textArea.setTabSize(2); + textArea.setMarkOccurrences(true); + textArea.setMarkOccurrencesDelay(200); + textArea.addKeyListener(new KeyAdapter() + { + @Override + public void keyPressed(KeyEvent e) + { + if (e.getKeyCode() == KeyEvent.VK_R && (e.getModifiersEx() & KeyEvent.CTRL_DOWN_MASK) != 0) + { + run(); + e.consume(); + } + if (e.getKeyCode() == KeyEvent.VK_F10) + { + run(); + e.consume(); + } + } + }); + + var textScrollArea = new RTextScrollPane(textArea); + + try + { + Theme.load(ShellPanel.class.getResourceAsStream("darcula.xml"), codeFont) + .apply(textArea); + + try (var is = ShellPanel.class.getResourceAsStream("default.jsh")) + { + textArea.setText(new String(is.readAllBytes(), StandardCharsets.UTF_8)); + } + } + catch (IOException e) + { + throw new RuntimeException(e); + } + + console.setFont(codeFont); + console.setFocusable(false); + console.setEditable(false); + console.setOpaque(false); // this turns off the hover effect for some reason + + var split = new JSplitPane(JSplitPane.VERTICAL_SPLIT, textScrollArea, new JScrollPane(console)); + split.setResizeWeight(.8); + split.setPreferredSize(new Dimension(800, 800)); + add(split, BorderLayout.CENTER); + + shellLogger = new TeeLogger(LoggerFactory.getLogger("Shell"), this::logToConsole); + INSTANCE = this; + + // make sure jshell is on the classpath + JShell.builder(); + } + + public void switchContext(Injector injector) + { + freeContext(); + + this.injector = injector; + + exec = new RLShellExecutionControl() + { + @Override + protected String invoke(Method doitMethod) throws Exception + { + var result = new AtomicReference<>(); + var sema = new Semaphore(0); + invokeOnClientThread(() -> + { + try + { + result.set(super.invoke(doitMethod)); + } + catch (Exception e) + { + result.set(e); + } + finally + { + sema.release(); + } + }); + sema.acquire(); + if (result.get() instanceof String) + { + return (String) result.get(); + } + throw (Exception) result.get(); + } + }; + + shell = JShell.builder() + .executionEngine(exec, null) + .build(); + + String preludeStr; + try (var is = ShellPanel.class.getResourceAsStream("prelude.jsh")) + { + preludeStr = new String(is.readAllBytes(), StandardCharsets.UTF_8); + } + catch (IOException e) + { + throw new RuntimeException(e); + } + prelude = ImmutableSet.copyOf(eval(preludeStr, false)); + + var cp = new JShellAutocompleteProvider(shell); + autoCompletion = new AutoCompletion(cp); + autoCompletion.setAutoActivationDelay(200); + autoCompletion.setAutoActivationEnabled(true); + autoCompletion.setAutoCompleteSingleChoices(false); + autoCompletion.install(this.textArea); + } + + public void logToConsole(String message) + { + SwingUtilities.invokeLater(() -> + { + try + { + var doc = console.getDocument(); + if (doc.getLength() > 100_000) + { + Segment seg = new Segment(); + int i = doc.getLength() - 75_000; + for (; i < doc.getLength(); i++) + { + doc.getText(i, 1, seg); + if (seg.array[0] == '\n') + { + break; + } + } + doc.remove(0, i); + } + doc.insertString(doc.getLength(), message + "\n", null); + console.setCaretPosition(doc.getLength()); + } + catch (BadLocationException e) + { + throw new RuntimeException(e); + } + }); + } + + private List eval(String src, boolean isUserCode) + { + var out = new ArrayList(); + var offsets = new HashMap(); + String output = null; + evaluation: + for (int offset = 0; offset < src.length(); ) + { + // Workaround a jdk bug + for (; src.charAt(offset) == '\n'; offset++); + + var ci = shell.sourceCodeAnalysis().analyzeCompletion(src.substring(offset)); + int thisOffset = offset; + offset = src.length() - ci.remaining().length(); + if (ci.completeness() == SourceCodeAnalysis.Completeness.EMPTY) + { + break; + } + + List evs = shell.eval(ci.source()); + for (var ev : evs) + { + Snippet snip = ev.snippet(); + offsets.put("#" + snip.id(), thisOffset); + if (ev.status() != Snippet.Status.VALID && ev.status() != Snippet.Status.RECOVERABLE_DEFINED) + { + var diags = shell.diagnostics(snip).collect(Collectors.toList()); + for (var diag : diags) + { + String msg = toStringDiagnostic(src, thisOffset, diag); + if (isUserCode) + { + logToConsole(msg); + // It might be nice to highlight stuff here + } + else + { + throw new RuntimeException("prelude error: " + msg); + } + } + if (diags.isEmpty()) + { + logToConsole("bad snippet" + ev.status()); + } + break evaluation; + } + if (ev.exception() != null) + { + if (isUserCode) + { + shellLogger.error("", new RemappingThrowable(src, offsets, ev.exception())); + } + else + { + throw new RuntimeException("prelude error", ev.exception()); + } + } + output = ev.value(); + + out.add(snip); + } + } + + if (isUserCode && !Strings.isNullOrEmpty(output)) + { + logToConsole("[OUTPUT] " + output); + } + + return out; + } + + private String toStringDiagnostic(String source, int offset, Diag diag) + { + int line = 1; + int column = 1; + offset += (int) diag.getPosition(); + for (int i = 0; i < offset && i < source.length(); i++) + { + if (source.charAt(i) == '\n') + { + line++; + column = 1; + } + else + { + column++; + } + } + + return line + ":" + column + ": " + diag.getMessage(Locale.getDefault()); + } + + protected void run() + { + String text = textArea.getText(); + executor.submit(() -> + { + shell.snippets() + .filter(v -> !prelude.contains(v)) + .forEach(shell::drop); + + cleanup(); + + eval(text, true); + }); + } + + public void freeContext() + { + cleanup(); + + exec = null; + shell = null; + prelude = null; + injector = null; + + if (autoCompletion != null) + { + autoCompletion.uninstall(); + } + autoCompletion = null; + + console.setText(""); + } + + private void cleanup() + { + for (var c : cleanup) + { + try + { + c.run(); + } + catch (Exception e) + { + shellLogger.error("Cleanup threw:", e); + } + } + cleanup.clear(); + } + + protected abstract void invokeOnClientThread(Runnable r); + + public T inject(Class clazz) + { + return injector.getInstance(clazz); + } + + public void cleanup(Runnable r) + { + cleanup.add(r); + } +} diff --git a/runelite-jshell/src/main/java/net/runelite/jshell/TeeLogger.java b/runelite-jshell/src/main/java/net/runelite/jshell/TeeLogger.java new file mode 100644 index 0000000000..bb74f84b14 --- /dev/null +++ b/runelite-jshell/src/main/java/net/runelite/jshell/TeeLogger.java @@ -0,0 +1,483 @@ +/* + * Copyright (c) 2021 Abex + * 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.jshell; + +import java.io.CharArrayWriter; +import java.io.PrintWriter; +import java.util.function.Consumer; +import lombok.RequiredArgsConstructor; +import org.slf4j.Logger; +import org.slf4j.Marker; +import org.slf4j.helpers.FormattingTuple; +import org.slf4j.helpers.MessageFormatter; + +@SuppressWarnings("PlaceholderCountMatchesArgumentCount") +@RequiredArgsConstructor +public class TeeLogger implements Logger +{ + private static final String TRACE = "[TRACE] "; + private static final String DEBUG = "[DEBUG] "; + private static final String INFO = "[INFO] "; + private static final String WARN = "[WARN] "; + private static final String ERROR = "[ERROR] "; + + private final Logger delegate; + private final Consumer messageConsumer; + + @Override + public String getName() + { + return "OPRSShell"; + } + + @Override + public boolean isTraceEnabled() + { + return true; + } + + private void log(String level, String message, Object... format) + { + FormattingTuple fmt = MessageFormatter.arrayFormat(message, format); + StringBuilder msg = new StringBuilder(); + msg.append(level).append(fmt.getMessage()); + Throwable throwable = fmt.getThrowable(); + if (throwable != null) + { + msg.append("\n"); + var caw = new CharArrayWriter(); + try (PrintWriter pw = new PrintWriter(caw)) + { + throwable.printStackTrace(pw); + } + msg.append(caw.toString()); + } + messageConsumer.accept(msg.toString()); + } + + @Override + public void trace(String msg) + { + delegate.trace(msg); + log(TRACE, msg); + } + + @Override + public void trace(String format, Object arg) + { + delegate.trace(format, arg); + log(TRACE, format, arg); + } + + @Override + public void trace(String format, Object arg1, Object arg2) + { + delegate.trace(format, arg1, arg2); + log(TRACE, format, arg1, arg2); + } + + @Override + public void trace(String format, Object... arguments) + { + delegate.trace(format, arguments); + log(TRACE, format, arguments); + } + + @Override + public void trace(String msg, Throwable t) + { + delegate.trace(msg, t); + log(TRACE, msg, t); + } + + @Override + public boolean isTraceEnabled(Marker marker) + { + return true; + } + + @Override + public void trace(Marker marker, String msg) + { + delegate.trace(marker, msg); + log(TRACE, msg); + } + + @Override + public void trace(Marker marker, String format, Object arg) + { + delegate.trace(marker, format, arg); + log(TRACE, format, arg); + } + + @Override + public void trace(Marker marker, String format, Object arg1, Object arg2) + { + delegate.trace(marker, format, arg1, arg2); + log(TRACE, format, arg1, arg2); + } + + @Override + public void trace(Marker marker, String format, Object... argArray) + { + delegate.trace(marker, format, argArray); + log(TRACE, format, argArray); + } + + @Override + public void trace(Marker marker, String msg, Throwable t) + { + delegate.trace(marker, msg, t); + log(TRACE, msg, t); + } + + @Override + public boolean isDebugEnabled() + { + return true; + } + + @Override + public void debug(String msg) + { + delegate.debug(msg); + log(DEBUG, msg); + } + + @Override + public void debug(String format, Object arg) + { + delegate.debug(format, arg); + log(DEBUG, format, arg); + } + + @Override + public void debug(String format, Object arg1, Object arg2) + { + delegate.debug(format, arg1, arg2); + log(DEBUG, format, arg1, arg2); + } + + @Override + public void debug(String format, Object... arguments) + { + delegate.debug(format, arguments); + log(DEBUG, format, arguments); + } + + @Override + public void debug(String msg, Throwable t) + { + delegate.debug(msg, t); + log(DEBUG, msg, t); + } + + @Override + public boolean isDebugEnabled(Marker marker) + { + return true; + } + + @Override + public void debug(Marker marker, String msg) + { + delegate.debug(marker, msg); + log(DEBUG, msg); + } + + @Override + public void debug(Marker marker, String format, Object arg) + { + delegate.debug(marker, format, arg); + log(DEBUG, format, arg); + } + + @Override + public void debug(Marker marker, String format, Object arg1, Object arg2) + { + delegate.debug(marker, format, arg1, arg2); + log(DEBUG, format, arg1, arg2); + } + + @Override + public void debug(Marker marker, String format, Object... arguments) + { + delegate.debug(marker, format, arguments); + log(DEBUG, format, arguments); + } + + @Override + public void debug(Marker marker, String msg, Throwable t) + { + delegate.debug(marker, msg, t); + log(DEBUG, msg, t); + } + + @Override + public boolean isInfoEnabled() + { + return true; + } + + @Override + public void info(String msg) + { + delegate.info(msg); + log(INFO, msg); + } + + @Override + public void info(String format, Object arg) + { + delegate.info(format, arg); + log(INFO, format, arg); + } + + @Override + public void info(String format, Object arg1, Object arg2) + { + delegate.info(format, arg1, arg2); + log(INFO, format, arg1, arg2); + } + + @Override + public void info(String format, Object... arguments) + { + delegate.info(format, arguments); + log(INFO, format, arguments); + } + + @Override + public void info(String msg, Throwable t) + { + delegate.info(msg, t); + log(INFO, msg, t); + } + + @Override + public boolean isInfoEnabled(Marker marker) + { + return true; + } + + @Override + public void info(Marker marker, String msg) + { + delegate.info(marker, msg); + log(INFO, msg); + } + + @Override + public void info(Marker marker, String format, Object arg) + { + delegate.info(marker, format, arg); + log(INFO, format, arg); + } + + @Override + public void info(Marker marker, String format, Object arg1, Object arg2) + { + delegate.info(marker, format, arg1, arg2); + log(INFO, format, arg1, arg2); + } + + @Override + public void info(Marker marker, String format, Object... arguments) + { + delegate.info(marker, format, arguments); + log(INFO, format, arguments); + } + + @Override + public void info(Marker marker, String msg, Throwable t) + { + delegate.info(marker, msg, t); + log(INFO, msg, t); + } + + @Override + public boolean isWarnEnabled() + { + return true; + } + + @Override + public void warn(String msg) + { + delegate.warn(msg); + log(WARN, msg); + } + + @Override + public void warn(String format, Object arg) + { + delegate.warn(format, arg); + log(WARN, format, arg); + } + + @Override + public void warn(String format, Object... arguments) + { + delegate.warn(format, arguments); + log(WARN, format, arguments); + } + + @Override + public void warn(String format, Object arg1, Object arg2) + { + delegate.warn(format, arg1, arg2); + log(WARN, format, arg1, arg2); + } + + @Override + public void warn(String msg, Throwable t) + { + delegate.warn(msg, t); + log(WARN, msg, t); + } + + @Override + public boolean isWarnEnabled(Marker marker) + { + return true; + } + + @Override + public void warn(Marker marker, String msg) + { + delegate.warn(marker, msg); + log(WARN, msg); + } + + @Override + public void warn(Marker marker, String format, Object arg) + { + delegate.warn(marker, format, arg); + log(WARN, format, arg); + } + + @Override + public void warn(Marker marker, String format, Object arg1, Object arg2) + { + delegate.warn(marker, format, arg1, arg2); + log(WARN, format, arg1, arg2); + } + + @Override + public void warn(Marker marker, String format, Object... arguments) + { + delegate.warn(marker, format, arguments); + log(WARN, format, arguments); + } + + @Override + public void warn(Marker marker, String msg, Throwable t) + { + delegate.warn(marker, msg, t); + log(WARN, msg, t); + } + + @Override + public boolean isErrorEnabled() + { + return true; + } + + @Override + public void error(String msg) + { + delegate.error(msg); + log(ERROR, msg); + } + + @Override + public void error(String format, Object arg) + { + delegate.error(format, arg); + log(ERROR, format, arg); + } + + @Override + public void error(String format, Object arg1, Object arg2) + { + delegate.error(format, arg1, arg2); + log(ERROR, format, arg1, arg2); + } + + @Override + public void error(String format, Object... arguments) + { + delegate.error(format, arguments); + log(ERROR, format, arguments); + } + + @Override + public void error(String msg, Throwable t) + { + delegate.error(msg, t); + log(ERROR, msg, t); + } + + @Override + public boolean isErrorEnabled(Marker marker) + { + return true; + } + + @Override + public void error(Marker marker, String msg) + { + delegate.error(marker, msg); + log(ERROR, msg); + } + + @Override + public void error(Marker marker, String format, Object arg) + { + delegate.error(marker, format, arg); + log(ERROR, format, arg); + } + + @Override + public void error(Marker marker, String format, Object arg1, Object arg2) + { + delegate.error(marker, format, arg1, arg2); + log(ERROR, format, arg1, arg2); + } + + @Override + public void error(Marker marker, String format, Object... arguments) + { + delegate.error(marker, format, arguments); + log(ERROR, format, arguments); + } + + @Override + public void error(Marker marker, String msg, Throwable t) + { + delegate.error(marker, msg, t); + log(ERROR, msg, t); + } +} diff --git a/runelite-jshell/src/main/resources/net/runelite/jshell/darcula.xml b/runelite-jshell/src/main/resources/net/runelite/jshell/darcula.xml new file mode 100644 index 0000000000..43729730ad --- /dev/null +++ b/runelite-jshell/src/main/resources/net/runelite/jshell/darcula.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +