Calculate and store widget parent id and position when the interfaces are rendered

This removes the need to calculate widget bounds and parent on demand by
traversing up the widget tree.
This commit is contained in:
Adam
2018-06-26 15:14:53 -04:00
parent 8ad6f466da
commit 799f8b1266
6 changed files with 137 additions and 110 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>

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;

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);
} }
@@ -300,50 +294,39 @@ public abstract class RSWidgetMixin implements RSWidget
public Widget[] getStaticChildren() public Widget[] getStaticChildren()
{ {
List<Widget> widgets = new ArrayList<Widget>(); List<Widget> widgets = new ArrayList<Widget>();
for (Widget widget : client.getGroup(TO_GROUP(getId()))) for (RSWidget widget : client.getGroup(TO_GROUP(getId())))
{ {
if (widget != null && widget.getParentId() == 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(); HashTable<WidgetNode> componentTable = client.getComponentTable();
int group = -1;
// XXX can actually use hashtable lookup instead of table WidgetNode wn = componentTable.get(getId());
// iteration here... if (wn == null)
for (Node node : componentTable.getNodes())
{ {
WidgetNode wn = (WidgetNode) node; return new RSWidget[0];
if (wn.getHash() == getId())
{
group = wn.getId();
break;
}
} }
if (group == -1) int group = wn.getId();
{
return new Widget[0];
}
List<Widget> widgets = new ArrayList<Widget>(); List<RSWidget> widgets = new ArrayList<RSWidget>();
for (Widget widget : client.getGroup(group)) for (RSWidget widget : client.getGroup(group))
{ {
if (widget != null && widget.getParentId() == getId()) 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 +356,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 +371,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

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