Merge pull request #4025 from Adam-/widget-optimizations

Widget optimizations
This commit is contained in:
Adam
2018-06-28 16:58:30 -04:00
committed by GitHub
8 changed files with 158 additions and 119 deletions

View File

@@ -412,15 +412,6 @@ public interface Client extends GameEngine
*/ */
Widget getWidget(WidgetInfo widget); Widget getWidget(WidgetInfo widget);
/**
* Gets an array of widgets that correspond to the passed group ID.
*
* @param groupId the group ID
* @return the widget group
* @see net.runelite.api.widgets.WidgetID
*/
Widget[] getGroup(int groupId);
/** /**
* Gets a widget by its raw group ID and child ID. * Gets a widget by its raw group ID and child ID.
* <p> * <p>
@@ -692,7 +683,7 @@ public interface Client extends GameEngine
* @return the widget node component table * @return the widget node component table
* @see WidgetNode * @see WidgetNode
*/ */
HashTable getComponentTable(); HashTable<WidgetNode> getComponentTable();
/** /**
* Gets an array of current grand exchange offers. * Gets an array of current grand exchange offers.

View File

@@ -30,7 +30,7 @@ import java.util.Collection;
* A data structure that uses a hash function to compute an index into an * A data structure that uses a hash function to compute an index into an
* array of buckets from which node objects can be quickly obtained. * array of buckets from which node objects can be quickly obtained.
*/ */
public interface HashTable public interface HashTable<T extends Node>
{ {
/** /**
* Gets a node by its hash value. * Gets a node by its hash value.
@@ -38,12 +38,12 @@ public interface HashTable
* @param value the node value * @param value the node value
* @return the associated node * @return the associated node
*/ */
Node get(long value); T get(long value);
/** /**
* Gets a collection of all nodes stored in this table. * Gets a collection of all nodes stored in this table.
* *
* @return the nodes stored * @return the nodes stored
*/ */
Collection<Node> getNodes(); Collection<T> getNodes();
} }

View File

@@ -36,14 +36,13 @@ import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client; import net.runelite.api.Client;
import net.runelite.api.GameState; import net.runelite.api.GameState;
import net.runelite.api.HashTable; import net.runelite.api.HashTable;
import net.runelite.api.Node;
import net.runelite.api.Prayer; import net.runelite.api.Prayer;
import net.runelite.api.WidgetNode; import net.runelite.api.WidgetNode;
import net.runelite.api.events.ConfigChanged; import net.runelite.api.events.ConfigChanged;
import net.runelite.api.events.DraggingWidgetChanged; import net.runelite.api.events.DraggingWidgetChanged;
import net.runelite.api.events.GameStateChanged; import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.WidgetMenuOptionClicked;
import net.runelite.api.events.WidgetLoaded; import net.runelite.api.events.WidgetLoaded;
import net.runelite.api.events.WidgetMenuOptionClicked;
import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.Widget;
import static net.runelite.api.widgets.WidgetConfig.DRAG; import static net.runelite.api.widgets.WidgetConfig.DRAG;
import static net.runelite.api.widgets.WidgetConfig.DRAG_ON; import static net.runelite.api.widgets.WidgetConfig.DRAG_ON;
@@ -339,10 +338,9 @@ public class ReorderPrayersPlugin extends Plugin
private PrayerTabState getPrayerTabState() private PrayerTabState getPrayerTabState()
{ {
HashTable componentTable = client.getComponentTable(); HashTable<WidgetNode> componentTable = client.getComponentTable();
for (Node node : componentTable.getNodes()) for (WidgetNode widgetNode : componentTable.getNodes())
{ {
WidgetNode widgetNode = (WidgetNode) node;
if (widgetNode.getId() == WidgetID.PRAYER_GROUP_ID) if (widgetNode.getId() == WidgetID.PRAYER_GROUP_ID)
{ {
return PrayerTabState.PRAYERS; return PrayerTabState.PRAYERS;

View File

@@ -35,6 +35,7 @@ import net.runelite.api.Friend;
import net.runelite.api.GameState; import net.runelite.api.GameState;
import net.runelite.api.GrandExchangeOffer; import net.runelite.api.GrandExchangeOffer;
import net.runelite.api.GraphicsObject; import net.runelite.api.GraphicsObject;
import net.runelite.api.HashTable;
import net.runelite.api.HintArrowType; import net.runelite.api.HintArrowType;
import net.runelite.api.IndexedSprite; import net.runelite.api.IndexedSprite;
import net.runelite.api.InventoryID; import net.runelite.api.InventoryID;
@@ -61,6 +62,7 @@ import net.runelite.api.SpritePixels;
import net.runelite.api.Tile; import net.runelite.api.Tile;
import net.runelite.api.VarPlayer; import net.runelite.api.VarPlayer;
import net.runelite.api.Varbits; import net.runelite.api.Varbits;
import net.runelite.api.WidgetNode;
import net.runelite.api.WorldType; import net.runelite.api.WorldType;
import net.runelite.api.coords.LocalPoint; import net.runelite.api.coords.LocalPoint;
import net.runelite.api.coords.WorldPoint; import net.runelite.api.coords.WorldPoint;
@@ -321,9 +323,9 @@ public abstract class RSClientMixin implements RSClient
{ {
int topGroup = getWidgetRoot(); int topGroup = getWidgetRoot();
List<Widget> widgets = new ArrayList<Widget>(); List<Widget> widgets = new ArrayList<Widget>();
for (Widget widget : getWidgets()[topGroup]) for (RSWidget widget : getWidgets()[topGroup])
{ {
if (widget != null && widget.getParentId() == -1) if (widget != null && widget.getRSParentId() == -1)
{ {
widgets.add(widget); widgets.add(widget);
} }
@@ -343,7 +345,7 @@ public abstract class RSClientMixin implements RSClient
@Inject @Inject
@Override @Override
public Widget[] getGroup(int groupId) public RSWidget[] getGroup(int groupId)
{ {
RSWidget[][] widgets = getWidgets(); RSWidget[][] widgets = getWidgets();
@@ -352,15 +354,7 @@ public abstract class RSClientMixin implements RSClient
return null; return null;
} }
List<Widget> w = new ArrayList<Widget>(); return widgets[groupId];
for (Widget widget : widgets[groupId])
{
if (widget != null)
{
w.add(widget);
}
}
return w.toArray(new Widget[w.size()]);
} }
@Inject @Inject
@@ -564,7 +558,7 @@ public abstract class RSClientMixin implements RSClient
for (Node node = head.getNext(); node != head; node = node.getNext()) for (Node node = head.getNext(); node != head; node = node.getNext())
{ {
graphicsObjects.add((GraphicsObject)node); graphicsObjects.add((GraphicsObject) node);
} }
return graphicsObjects; return graphicsObjects;
@@ -1099,4 +1093,45 @@ public abstract class RSClientMixin implements RSClient
{ {
callbacks.clientMainLoop(); callbacks.clientMainLoop();
} }
@MethodHook("gameDraw")
@Inject
public static void gameDraw(Widget[] widgets, int parentId, int var2, int var3, int var4, int var5, int x, int y, int var8)
{
for (Widget rlWidget : widgets)
{
RSWidget widget = (RSWidget) rlWidget;
if (widget == null)
{
continue;
}
if (widget.getRSParentId() == parentId)
{
if (parentId != -1)
{
widget.setRenderParentId(parentId);
}
widget.setRenderX(x + widget.getRelativeX());
widget.setRenderY(y + widget.getRelativeY());
}
HashTable<WidgetNode> componentTable = client.getComponentTable();
WidgetNode childNode = componentTable.get(widget.getId());
if (childNode != null)
{
int widgetId = widget.getId();
int groupId = childNode.getId();
RSWidget[] children = client.getWidgets()[groupId];
for (RSWidget child : children)
{
if (child.getRSParentId() == -1)
{
child.setRenderParentId(widgetId);
}
}
}
}
}
} }

View File

@@ -28,7 +28,7 @@ import java.awt.Rectangle;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import net.runelite.api.Node; import net.runelite.api.HashTable;
import net.runelite.api.Point; import net.runelite.api.Point;
import net.runelite.api.WidgetNode; import net.runelite.api.WidgetNode;
import net.runelite.api.events.WidgetHiddenChanged; import net.runelite.api.events.WidgetHiddenChanged;
@@ -42,8 +42,6 @@ import static net.runelite.api.widgets.WidgetInfo.TO_CHILD;
import static net.runelite.api.widgets.WidgetInfo.TO_GROUP; import static net.runelite.api.widgets.WidgetInfo.TO_GROUP;
import net.runelite.api.widgets.WidgetItem; import net.runelite.api.widgets.WidgetItem;
import net.runelite.rs.api.RSClient; import net.runelite.rs.api.RSClient;
import net.runelite.rs.api.RSHashTable;
import net.runelite.rs.api.RSNode;
import net.runelite.rs.api.RSWidget; import net.runelite.rs.api.RSWidget;
@Mixin(RSWidget.class) @Mixin(RSWidget.class)
@@ -57,6 +55,44 @@ public abstract class RSWidgetMixin implements RSWidget
@Inject @Inject
private static int rl$widgetLastPosChanged; private static int rl$widgetLastPosChanged;
@Inject
private int rl$parentId;
@Inject
private int rl$x;
@Inject
private int rl$y;
@Inject
RSWidgetMixin()
{
rl$parentId = -1;
rl$x = -1;
rl$y = -1;
}
@Inject
@Override
public void setRenderParentId(int parentId)
{
rl$parentId = parentId;
}
@Inject
@Override
public void setRenderX(int x)
{
rl$x = x;
}
@Inject
@Override
public void setRenderY(int y)
{
rl$y = y;
}
@Inject @Inject
@Override @Override
public Widget getParent() public Widget getParent()
@@ -74,34 +110,27 @@ public abstract class RSWidgetMixin implements RSWidget
@Override @Override
public int getParentId() public int getParentId()
{ {
int parentId = getRSParentId(); int parentId = rl$parentId;
if (parentId != -1)
{
return parentId;
}
int groupId = TO_GROUP(getId()); if (parentId != -1 && getRSParentId() == -1)
RSHashTable componentTable = client.getComponentTable();
RSNode[] buckets = componentTable.getBuckets();
for (int i = 0; i < buckets.length; ++i)
{ {
Node node = buckets[i]; // if this happens, the widget is or was nested.
// rl$parentId is updated when drawing, but will not be updated when
// the widget is no longer reachable in the tree, leaving
// parent id potentially incorrect
// It looks like the first node in the bucket is always // check the parent in the component table
// a sentinel HashTable<WidgetNode> componentTable = client.getComponentTable();
Node cur = node.getNext(); WidgetNode widgetNode = componentTable.get(parentId);
while (cur != node) if (widgetNode == null || widgetNode.getId() != TO_GROUP(getId()))
{ {
WidgetNode wn = (WidgetNode) cur; // invalidate parent
rl$parentId = -1;
if (groupId == wn.getId()) return -1;
{
return (int) wn.getHash();
}
cur = cur.getNext();
} }
} }
return -1;
return parentId;
} }
@Inject @Inject
@@ -153,42 +182,7 @@ public abstract class RSWidgetMixin implements RSWidget
@Override @Override
public Point getCanvasLocation() public Point getCanvasLocation()
{ {
int x = 0; return new Point(rl$x, rl$y);
int y = 0;
RSWidget cur;
for (cur = this; cur.getParent() != null; cur = (RSWidget) cur.getParent())
{
x += cur.getRelativeX();
y += cur.getRelativeY();
x -= cur.getScrollX();
y -= cur.getScrollY();
}
// cur is now the root
int[] widgetBoundsWidth = client.getWidgetPositionsX();
int[] widgetBoundsHeight = client.getWidgetPositionsY();
int boundsIndex = cur.getBoundsIndex();
if (boundsIndex != -1)
{
x += widgetBoundsWidth[boundsIndex];
y += widgetBoundsHeight[boundsIndex];
if (cur.getType() > 0)
{
x += cur.getRelativeX();
y += cur.getRelativeY();
}
}
else
{
x += cur.getRelativeX();
y += cur.getRelativeY();
}
return new Point(x, y);
} }
@Inject @Inject
@@ -285,9 +279,9 @@ public abstract class RSWidgetMixin implements RSWidget
} }
List<Widget> widgets = new ArrayList<Widget>(); List<Widget> widgets = new ArrayList<Widget>();
for (Widget widget : children) for (RSWidget widget : children)
{ {
if (widget != null && widget.getParentId() == getId()) if (widget != null && widget.getRSParentId() == getId())
{ {
widgets.add(widget); widgets.add(widget);
} }
@@ -299,51 +293,52 @@ public abstract class RSWidgetMixin implements RSWidget
@Override @Override
public Widget[] getStaticChildren() public Widget[] getStaticChildren()
{ {
List<Widget> widgets = new ArrayList<Widget>(); if (getRSParentId() == getId())
for (Widget widget : client.getGroup(TO_GROUP(getId())))
{ {
if (widget != null && widget.getParentId() == getId()) // This is a dynamic widget, so it can't have static children
return new Widget[0];
}
List<Widget> widgets = new ArrayList<Widget>();
for (RSWidget widget : client.getGroup(TO_GROUP(getId())))
{
if (widget != null && widget.getRSParentId() == getId())
{ {
widgets.add(widget); widgets.add(widget);
} }
} }
return widgets.toArray(new Widget[widgets.size()]); return widgets.toArray(new RSWidget[widgets.size()]);
} }
@Inject @Inject
@Override @Override
public Widget[] getNestedChildren() public Widget[] getNestedChildren()
{ {
RSHashTable componentTable = client.getComponentTable(); if (getRSParentId() == getId())
int group = -1;
// XXX can actually use hashtable lookup instead of table
// iteration here...
for (Node node : componentTable.getNodes())
{
WidgetNode wn = (WidgetNode) node;
if (wn.getHash() == getId())
{
group = wn.getId();
break;
}
}
if (group == -1)
{ {
// This is a dynamic widget, so it can't have nested children
return new Widget[0]; return new Widget[0];
} }
List<Widget> widgets = new ArrayList<Widget>(); HashTable<WidgetNode> componentTable = client.getComponentTable();
for (Widget widget : client.getGroup(group))
WidgetNode wn = componentTable.get(getId());
if (wn == null)
{ {
if (widget != null && widget.getParentId() == getId()) return new RSWidget[0];
}
int group = wn.getId();
List<RSWidget> widgets = new ArrayList<RSWidget>();
for (RSWidget widget : client.getGroup(group))
{
if (widget != null && widget.getRSParentId() == -1)
{ {
widgets.add(widget); widgets.add(widget);
} }
} }
return widgets.toArray(new Widget[widgets.size()]); return widgets.toArray(new RSWidget[widgets.size()]);
} }
@Inject @Inject
@@ -373,7 +368,9 @@ public abstract class RSWidgetMixin implements RSWidget
{ {
// if the widget is hidden it will not magically unhide from its parent changing // if the widget is hidden it will not magically unhide from its parent changing
if (child == null || child.isSelfHidden()) if (child == null || child.isSelfHidden())
{
continue; continue;
}
child.broadcastHidden(hidden); child.broadcastHidden(hidden);
} }
@@ -386,7 +383,9 @@ public abstract class RSWidgetMixin implements RSWidget
for (Widget nestedChild : nestedChildren) for (Widget nestedChild : nestedChildren)
{ {
if (nestedChild == null || nestedChild.isSelfHidden()) if (nestedChild == null || nestedChild.isSelfHidden())
{
continue; continue;
}
((RSWidget) nestedChild).broadcastHidden(hidden); ((RSWidget) nestedChild).broadcastHidden(hidden);
} }

View File

@@ -156,6 +156,15 @@ public interface RSClient extends RSGameEngine, Client
@Import("widgets") @Import("widgets")
RSWidget[][] getWidgets(); RSWidget[][] getWidgets();
/**
* Gets an array of widgets that correspond to the passed group ID.
*
* @param groupId the group ID
* @return the widget group
* @see net.runelite.api.widgets.WidgetID
*/
RSWidget[] getGroup(int groupId);
@Import("region") @Import("region")
@Override @Override
RSRegion getRegion(); RSRegion getRegion();

View File

@@ -30,6 +30,7 @@ import net.runelite.mapping.Import;
public interface RSHashTable extends HashTable public interface RSHashTable extends HashTable
{ {
@Import("get") @Import("get")
@Override
RSNode get(long value); RSNode get(long value);
@Import("size") @Import("size")

View File

@@ -40,6 +40,12 @@ public interface RSWidget extends Widget
@Override @Override
int getId(); int getId();
void setRenderParentId(int parentId);
void setRenderX(int x);
void setRenderY(int y);
@Import("parentId") @Import("parentId")
int getRSParentId(); int getRSParentId();