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 e7aba556ea..f830814827 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 @@ -41,21 +41,20 @@ public class DevToolsPanel extends PluginPanel private final Client client; private final DevToolsPlugin plugin; - private final VarTracker varTracker; - - private WidgetInspector widgetInspector; + private final WidgetInspector widgetInspector; + private final VarInspector varInspector; @Inject - public DevToolsPanel(Client client, DevToolsPlugin plugin, WidgetInspector widgetInspector) + public DevToolsPanel(Client client, DevToolsPlugin plugin, WidgetInspector widgetInspector, VarInspector varInspector) { super(); this.client = client; this.plugin = plugin; this.widgetInspector = widgetInspector; + this.varInspector = varInspector; setBackground(ColorScheme.DARK_GRAY_COLOR); - varTracker = new VarTracker(client); add(createOptionsPanel()); } @@ -137,14 +136,6 @@ public class DevToolsPanel extends PluginPanel }); container.add(renderProjectilesBtn); - final JButton varSnapshotBtn = new JButton("Snapshot Vars"); - varSnapshotBtn.addActionListener(varTracker::snapshot); - container.add(varSnapshotBtn); - - final JButton varClearBtn = new JButton("Clear Vars"); - varClearBtn.addActionListener(varTracker::clear); - container.add(varClearBtn); - final JButton renderLocationBtn = new JButton("Location"); renderLocationBtn.addActionListener(e -> { @@ -153,7 +144,7 @@ public class DevToolsPanel extends PluginPanel }); container.add(renderLocationBtn); - final JButton widgetInspectorBtn = new JButton("Inspector"); + final JButton widgetInspectorBtn = new JButton("Widget Tools"); widgetInspectorBtn.addActionListener(e -> { widgetInspector.setVisible(true); @@ -162,6 +153,13 @@ public class DevToolsPanel extends PluginPanel }); container.add(widgetInspectorBtn); + final JButton varInspectorBtn = new JButton("Var Tools"); + varInspectorBtn.addActionListener(e -> + { + varInspector.open(); + }); + container.add(varInspectorBtn); + final JButton chunkBordersBtn = new JButton("Chunk borders"); chunkBordersBtn.addActionListener(e -> { 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 new file mode 100644 index 0000000000..bcaa66036f --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/VarInspector.java @@ -0,0 +1,350 @@ +/* + * Copyright (c) 2018 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 com.google.common.eventbus.EventBus; +import com.google.common.eventbus.Subscribe; +import com.google.inject.Inject; +import java.awt.BorderLayout; +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.Objects; +import javax.swing.BorderFactory; +import javax.swing.JCheckBox; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollBar; +import javax.swing.JScrollPane; +import javax.swing.SwingUtilities; +import javax.swing.border.CompoundBorder; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Client; +import net.runelite.api.VarClientInt; +import net.runelite.api.VarClientStr; +import net.runelite.api.VarPlayer; +import net.runelite.api.Varbits; +import net.runelite.api.events.VarClientIntChanged; +import net.runelite.api.events.VarClientStrChanged; +import net.runelite.api.events.VarbitChanged; +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 +{ + @Getter + private enum VarType + { + VARBIT("Varbit"), + VARP("VarPlayer"), + VARCINT("VarClientInt"), + VARCSTR("VarClientStr"); + + private final String name; + private final JCheckBox checkBox; + + VarType(String name) + { + this.name = name; + checkBox = new JCheckBox(name, true); + } + } + + private final static int MAX_LOG_ENTRIES = 10_000; + + private final Client client; + private final EventBus eventBus; + + private final JPanel tracker = new JPanel(); + + private int lastTick = 0; + + private final int[] oldVarps; + private final int[] oldVarps2; + private int numVarbits = 10000; + + private final int[] oldIntVarcs; + private final String[] oldStrVarcs; + + @Inject + VarInspector(Client client, EventBus eventBus) + { + this.eventBus = eventBus; + this.client = client; + oldVarps = new int[client.getVarps().length]; + oldVarps2 = new int[client.getVarps().length]; + oldIntVarcs = new int[client.getIntVarcs().length]; + oldStrVarcs = new String[client.getStrVarcs().length]; + + setTitle("RuneLite Var Inspector"); + setIconImage(ClientUI.ICON); + + setLayout(new BorderLayout()); + + setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); + addWindowListener(new WindowAdapter() + { + @Override + public void windowClosing(WindowEvent e) + { + close(); + } + }); + + tracker.setLayout(new DynamicGridLayout(0, 1, 0, 3)); + + final JPanel trackerWrapper = new JPanel(); + trackerWrapper.setLayout(new BorderLayout()); + trackerWrapper.add(tracker, BorderLayout.NORTH); + + final JScrollPane trackerScroller = new JScrollPane(trackerWrapper); + trackerScroller.setPreferredSize(new Dimension(400, 400)); + + final JScrollBar vertical = trackerScroller.getVerticalScrollBar(); + vertical.addAdjustmentListener(new AdjustmentListener() + { + int lastMaximum = actualMax(); + + private int actualMax() + { + return vertical.getMaximum() - vertical.getModel().getExtent(); + } + + @Override + public void adjustmentValueChanged(AdjustmentEvent e) + { + if (vertical.getValue() >= lastMaximum) + { + vertical.setValue(actualMax()); + } + lastMaximum = actualMax(); + } + }); + + add(trackerScroller, BorderLayout.CENTER); + + final JPanel trackerOpts = new JPanel(); + trackerOpts.setLayout(new FlowLayout()); + for (VarType cb : VarType.values()) + { + trackerOpts.add(cb.getCheckBox()); + } + add(trackerOpts, BorderLayout.SOUTH); + + pack(); + } + + private void addVarLog(VarType type, String name, int old, int neew) + { + addVarLog(type, name, Integer.toString(old), Integer.toString(neew)); + } + + private void addVarLog(VarType type, String name, String old, String neew) + { + if (!type.getCheckBox().isSelected()) + { + return; + } + + int tick = client.getTickCount(); + SwingUtilities.invokeLater(() -> + { + if (tick != lastTick) + { + lastTick = tick; + JLabel header = new JLabel("Tick " + tick); + header.setFont(FontManager.getRunescapeSmallFont()); + header.setBorder(new CompoundBorder( + BorderFactory.createMatteBorder(0, 0, 1, 0, ColorScheme.LIGHT_GRAY_COLOR), + BorderFactory.createEmptyBorder(3, 6, 0, 0) + )); + tracker.add(header); + } + tracker.add(new JLabel(String.format("%s %s changed: %s -> %s", type.getName(), name, old, neew))); + + // Cull very old stuff + for (; tracker.getComponentCount() > MAX_LOG_ENTRIES; ) + { + tracker.remove(0); + } + + tracker.revalidate(); + }); + } + + @Subscribe + public void onVarbitChanged(VarbitChanged ev) + { + int[] varps = client.getVarps(); + + // Check varbits + for (int i = 0; i < numVarbits; i++) + { + try + { + int old = client.getVarbitValue(oldVarps, i); + int neew = client.getVarbitValue(varps, i); + if (old != neew) + { + // Set the varbit so it doesn't show in the varp changes + // However, some varbits share common bits, so we only do it in oldVarps2 + // Example: 4101 collides with 4104-4129 + client.setVarbitValue(oldVarps2, i, neew); + + String name = String.format("%d", i); + for (Varbits varbit : Varbits.values()) + { + if (varbit.getId() == i) + { + name = String.format("%s(%d)", varbit.name(), i); + break; + } + } + addVarLog(VarType.VARBIT, name, old, neew); + } + } + catch (IndexOutOfBoundsException e) + { + // We don't know what the last varbit is, so we just hit the end, then set it for future iterations + log.debug("Hit OOB at varbit {}", i); + numVarbits = i; + break; + } + } + + // Check varps + for (int i = 0; i < varps.length; i++) + { + int old = oldVarps2[i]; + int neew = varps[i]; + if (old != neew) + { + String name = String.format("%d", i); + for (VarPlayer varp : VarPlayer.values()) + { + if (varp.getId() == i) + { + name = String.format("%s(%d)", varp.name(), i); + break; + } + } + addVarLog(VarType.VARP, name, old, neew); + } + } + + System.arraycopy(client.getVarps(), 0, oldVarps, 0, oldVarps.length); + System.arraycopy(client.getVarps(), 0, oldVarps2, 0, oldVarps2.length); + } + + @Subscribe + public void onVarClientIntChanged(VarClientIntChanged e) + { + int idx = e.getIndex(); + int neew = client.getIntVarcs()[idx]; + int old = oldIntVarcs[idx]; + oldIntVarcs[idx] = neew; + + if (old != neew) + { + String name = String.format("%d", idx); + for (VarClientInt varc : VarClientInt.values()) + { + if (varc.getIndex() == idx) + { + name = String.format("%s(%d)", varc.name(), idx); + break; + } + } + addVarLog(VarType.VARCINT, name, old, neew); + } + } + + @Subscribe + public void onVarClientStrChanged(VarClientStrChanged e) + { + int idx = e.getIndex(); + String neew = client.getStrVarcs()[idx]; + String old = oldStrVarcs[idx]; + oldStrVarcs[idx] = neew; + + if (!Objects.equals(old, neew)) + { + String name = String.format("%d", idx); + for (VarClientStr varc : VarClientStr.values()) + { + if (varc.getIndex() == idx) + { + name = String.format("%s(%d)", varc.name(), idx); + break; + } + } + if (old != null) + { + old = "\"" + old + "\""; + } + else + { + old = "null"; + } + if (neew != null) + { + neew = "\"" + neew + "\""; + } + else + { + neew = "null"; + } + addVarLog(VarType.VARCSTR, name, old, neew); + } + } + + public void open() + { + System.arraycopy(client.getVarps(), 0, oldVarps, 0, oldVarps.length); + System.arraycopy(client.getVarps(), 0, oldVarps2, 0, oldVarps2.length); + System.arraycopy(client.getIntVarcs(), 0, oldIntVarcs, 0, oldIntVarcs.length); + System.arraycopy(client.getStrVarcs(), 0, oldStrVarcs, 0, oldStrVarcs.length); + + eventBus.register(this); + setVisible(true); + toFront(); + repaint(); + } + + public void close() + { + tracker.removeAll(); + eventBus.unregister(this); + setVisible(false); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/VarTracker.java b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/VarTracker.java deleted file mode 100644 index 1edc584058..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/VarTracker.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (c) 2017, 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.devtools; - -import java.awt.event.ActionEvent; -import java.util.Arrays; -import lombok.extern.slf4j.Slf4j; -import net.runelite.api.Client; - -@Slf4j -public class VarTracker -{ - private final Client client; - - private int[] varPs; - - public VarTracker(Client client) - { - this.client = client; - } - - public void snapshot(ActionEvent e) - { - if (varPs == null) - { - varPs = copy(client.getVarps()); - - log.info("Snapshotted VarPs"); - return; - } - - int[] newVarPs = client.getVarps(); - - for (int i = 0; i < Math.min(varPs.length, newVarPs.length); ++i) - { - int before = varPs[i]; - int after = newVarPs[i]; - - if (before == after) - { - continue; - } - - log.info("VarP index {} has changed from {} to {}: {} -> {}", - i, before, after, prettyPrintInt(before), prettyPrintInt(after)); - } - - varPs = copy(newVarPs); - } - - public void clear(ActionEvent e) - { - varPs = null; - } - - private static int[] copy(int[] array) - { - return Arrays.copyOf(array, array.length); - } - - private static String prettyPrintInt(int value) - { - String s = Integer.toBinaryString(value); - while (s.length() < 32) - { - s = "0" + s; - } - - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < 32; i += 8) - { - String substr = s.substring(i, i + 8); - if (i > 0) - { - sb.append(' '); - } - sb.append(substr); - } - return sb.toString(); - } -}