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);
/**
* 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.
* <p>
@@ -692,7 +683,7 @@ public interface Client extends GameEngine
* @return the widget node component table
* @see WidgetNode
*/
HashTable getComponentTable();
HashTable<WidgetNode> getComponentTable();
/**
* 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
* 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.
@@ -38,12 +38,12 @@ public interface HashTable
* @param value the node value
* @return the associated node
*/
Node get(long value);
T get(long value);
/**
* Gets a collection of all nodes stored in this table.
*
* @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.GameState;
import net.runelite.api.HashTable;
import net.runelite.api.Node;
import net.runelite.api.Prayer;
import net.runelite.api.WidgetNode;
import net.runelite.api.events.ConfigChanged;
import net.runelite.api.events.DraggingWidgetChanged;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.WidgetMenuOptionClicked;
import net.runelite.api.events.WidgetLoaded;
import net.runelite.api.events.WidgetMenuOptionClicked;
import net.runelite.api.widgets.Widget;
import static net.runelite.api.widgets.WidgetConfig.DRAG;
import static net.runelite.api.widgets.WidgetConfig.DRAG_ON;
@@ -339,10 +338,9 @@ public class ReorderPrayersPlugin extends Plugin
private PrayerTabState getPrayerTabState()
{
HashTable componentTable = client.getComponentTable();
for (Node node : componentTable.getNodes())
HashTable<WidgetNode> componentTable = client.getComponentTable();
for (WidgetNode widgetNode : componentTable.getNodes())
{
WidgetNode widgetNode = (WidgetNode) node;
if (widgetNode.getId() == WidgetID.PRAYER_GROUP_ID)
{
return PrayerTabState.PRAYERS;

View File

@@ -35,6 +35,7 @@ import net.runelite.api.Friend;
import net.runelite.api.GameState;
import net.runelite.api.GrandExchangeOffer;
import net.runelite.api.GraphicsObject;
import net.runelite.api.HashTable;
import net.runelite.api.HintArrowType;
import net.runelite.api.IndexedSprite;
import net.runelite.api.InventoryID;
@@ -61,6 +62,7 @@ import net.runelite.api.SpritePixels;
import net.runelite.api.Tile;
import net.runelite.api.VarPlayer;
import net.runelite.api.Varbits;
import net.runelite.api.WidgetNode;
import net.runelite.api.WorldType;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.coords.WorldPoint;
@@ -321,9 +323,9 @@ public abstract class RSClientMixin implements RSClient
{
int topGroup = getWidgetRoot();
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);
}
@@ -343,7 +345,7 @@ public abstract class RSClientMixin implements RSClient
@Inject
@Override
public Widget[] getGroup(int groupId)
public RSWidget[] getGroup(int groupId)
{
RSWidget[][] widgets = getWidgets();
@@ -352,15 +354,7 @@ public abstract class RSClientMixin implements RSClient
return null;
}
List<Widget> w = new ArrayList<Widget>();
for (Widget widget : widgets[groupId])
{
if (widget != null)
{
w.add(widget);
}
}
return w.toArray(new Widget[w.size()]);
return widgets[groupId];
}
@Inject
@@ -564,7 +558,7 @@ public abstract class RSClientMixin implements RSClient
for (Node node = head.getNext(); node != head; node = node.getNext())
{
graphicsObjects.add((GraphicsObject)node);
graphicsObjects.add((GraphicsObject) node);
}
return graphicsObjects;
@@ -1099,4 +1093,45 @@ public abstract class RSClientMixin implements RSClient
{
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.Collection;
import java.util.List;
import net.runelite.api.Node;
import net.runelite.api.HashTable;
import net.runelite.api.Point;
import net.runelite.api.WidgetNode;
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 net.runelite.api.widgets.WidgetItem;
import net.runelite.rs.api.RSClient;
import net.runelite.rs.api.RSHashTable;
import net.runelite.rs.api.RSNode;
import net.runelite.rs.api.RSWidget;
@Mixin(RSWidget.class)
@@ -57,6 +55,44 @@ public abstract class RSWidgetMixin implements RSWidget
@Inject
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
@Override
public Widget getParent()
@@ -74,34 +110,27 @@ public abstract class RSWidgetMixin implements RSWidget
@Override
public int getParentId()
{
int parentId = getRSParentId();
if (parentId != -1)
{
return parentId;
}
int parentId = rl$parentId;
int groupId = TO_GROUP(getId());
RSHashTable componentTable = client.getComponentTable();
RSNode[] buckets = componentTable.getBuckets();
for (int i = 0; i < buckets.length; ++i)
if (parentId != -1 && getRSParentId() == -1)
{
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
// a sentinel
Node cur = node.getNext();
while (cur != node)
// check the parent in the component table
HashTable<WidgetNode> componentTable = client.getComponentTable();
WidgetNode widgetNode = componentTable.get(parentId);
if (widgetNode == null || widgetNode.getId() != TO_GROUP(getId()))
{
WidgetNode wn = (WidgetNode) cur;
if (groupId == wn.getId())
{
return (int) wn.getHash();
}
cur = cur.getNext();
// invalidate parent
rl$parentId = -1;
return -1;
}
}
return -1;
return parentId;
}
@Inject
@@ -153,42 +182,7 @@ public abstract class RSWidgetMixin implements RSWidget
@Override
public Point getCanvasLocation()
{
int x = 0;
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);
return new Point(rl$x, rl$y);
}
@Inject
@@ -285,9 +279,9 @@ public abstract class RSWidgetMixin implements RSWidget
}
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);
}
@@ -299,51 +293,52 @@ public abstract class RSWidgetMixin implements RSWidget
@Override
public Widget[] getStaticChildren()
{
List<Widget> widgets = new ArrayList<Widget>();
for (Widget widget : client.getGroup(TO_GROUP(getId())))
if (getRSParentId() == 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);
}
}
return widgets.toArray(new Widget[widgets.size()]);
return widgets.toArray(new RSWidget[widgets.size()]);
}
@Inject
@Override
public Widget[] getNestedChildren()
{
RSHashTable componentTable = client.getComponentTable();
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)
if (getRSParentId() == getId())
{
// This is a dynamic widget, so it can't have nested children
return new Widget[0];
}
List<Widget> widgets = new ArrayList<Widget>();
for (Widget widget : client.getGroup(group))
HashTable<WidgetNode> componentTable = client.getComponentTable();
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);
}
}
return widgets.toArray(new Widget[widgets.size()]);
return widgets.toArray(new RSWidget[widgets.size()]);
}
@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 (child == null || child.isSelfHidden())
{
continue;
}
child.broadcastHidden(hidden);
}
@@ -386,7 +383,9 @@ public abstract class RSWidgetMixin implements RSWidget
for (Widget nestedChild : nestedChildren)
{
if (nestedChild == null || nestedChild.isSelfHidden())
{
continue;
}
((RSWidget) nestedChild).broadcastHidden(hidden);
}

View File

@@ -156,6 +156,15 @@ public interface RSClient extends RSGameEngine, Client
@Import("widgets")
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")
@Override
RSRegion getRegion();

View File

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

View File

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