devtools: Replace Var tracker with Var Inspector

This commit is contained in:
Max Weber
2018-05-13 22:41:45 -06:00
parent 572d92378a
commit 106e781cc7
3 changed files with 362 additions and 117 deletions

View File

@@ -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 ->
{

View File

@@ -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);
}
}

View File

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