Merge pull request #3901 from deathbeam/more-method-hooks-public

Replace Hook with MethodHook where possible
This commit is contained in:
Adam
2018-06-22 20:08:37 -04:00
committed by GitHub
12 changed files with 262 additions and 165 deletions

View File

@@ -33,39 +33,16 @@ import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.event.FocusEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.image.BufferedImage;
import java.awt.image.VolatileImage;
import net.runelite.api.Actor;
import net.runelite.api.ChatMessageType;
import net.runelite.api.Client;
import net.runelite.api.GraphicsObject;
import net.runelite.api.Hitsplat;
import net.runelite.api.ItemComposition;
import net.runelite.api.KeyFocusListener;
import net.runelite.api.MainBufferProvider;
import net.runelite.api.MenuAction;
import net.runelite.api.MessageNode;
import net.runelite.api.PacketBuffer;
import net.runelite.api.Projectile;
import net.runelite.api.Region;
import net.runelite.api.RenderOverview;
import net.runelite.api.TextureProvider;
import net.runelite.api.WorldMapManager;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.events.ChatMessage;
import net.runelite.api.events.FocusChanged;
import net.runelite.api.events.GameTick;
import net.runelite.api.events.GraphicsObjectCreated;
import net.runelite.api.events.HitsplatApplied;
import net.runelite.api.events.MenuOpened;
import net.runelite.api.events.MenuOptionClicked;
import net.runelite.api.events.PostItemComposition;
import net.runelite.api.events.ProjectileMoved;
import net.runelite.api.events.SetMessage;
import net.runelite.api.widgets.Widget;
import static net.runelite.api.widgets.WidgetInfo.WORLD_MAP_VIEW;
import net.runelite.client.Notifier;
@@ -83,6 +60,11 @@ import net.runelite.client.util.DeferredEventBus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class contains field required for mixins and runelite hooks to work.
* All remaining method hooks in this class are performance-critical or contain client-specific logic and so they
* can't just be placed in mixins or sent through event bus.
*/
public class Hooks
{
// must be public as the mixins use it
@@ -114,7 +96,7 @@ public class Hooks
private static long lastCheck;
private static boolean shouldProcessGameTick;
public static void clientMainLoop(Client client, boolean arg1)
public static void clientMainLoop()
{
if (shouldProcessGameTick)
{
@@ -245,22 +227,6 @@ public class Hooks
keyManager.processKeyTyped(keyEvent);
}
public static void focusGained(KeyFocusListener l, FocusEvent focusEvent)
{
FocusChanged focusChanged = new FocusChanged();
focusChanged.setFocused(true);
eventBus.post(focusChanged);
}
public static void focusLost(KeyFocusListener l, FocusEvent focusEvent)
{
FocusChanged focusChanged = new FocusChanged();
focusChanged.setFocused(false);
eventBus.post(focusChanged);
}
public static void draw(MainBufferProvider mainBufferProvider, Graphics graphics, int x, int y)
{
if (graphics == null)
@@ -303,7 +269,7 @@ public class Hooks
stretchedGraphics = (Graphics2D) stretchedImage.getGraphics();
lastStretchedDimensions = stretchedDimensions;
/*
Fill Canvas before drawing stretched image to prevent artifacts.
*/
@@ -326,7 +292,7 @@ public class Hooks
renderHooks.processDrawComplete(image);
}
public static void drawRegion(Region region, int var1, int var2, int var3, int var4, int var5, int var6)
public static void drawRegion()
{
MainBufferProvider bufferProvider = (MainBufferProvider) client.getBufferProvider();
BufferedImage image = (BufferedImage) bufferProvider.getImage();
@@ -342,7 +308,7 @@ public class Hooks
}
}
public static void drawAboveOverheads(TextureProvider textureProvider, int var1)
public static void drawAboveOverheads()
{
MainBufferProvider bufferProvider = (MainBufferProvider) client.getBufferProvider();
BufferedImage image = (BufferedImage) bufferProvider.getImage();
@@ -374,127 +340,11 @@ public class Hooks
}
}
public static boolean menuActionHook(int actionParam, int widgetId, int menuAction, int id, String menuOption, String menuTarget, int var6, int var7)
{
/* Along the way, the RuneScape client may change a menuAction by incrementing it with 2000.
* I have no idea why, but it does. Their code contains the same conditional statement.
*/
if (menuAction >= 2000)
{
menuAction -= 2000;
}
MenuOptionClicked menuOptionClicked = new MenuOptionClicked();
menuOptionClicked.setActionParam(actionParam);
menuOptionClicked.setMenuOption(menuOption);
menuOptionClicked.setMenuTarget(menuTarget);
menuOptionClicked.setMenuAction(MenuAction.of(menuAction));
menuOptionClicked.setId(id);
menuOptionClicked.setWidgetId(widgetId);
log.debug("Menu action clicked: {}", menuOptionClicked);
eventBus.post(menuOptionClicked);
return menuOptionClicked.isConsumed();
}
public static void addChatMessage(int type, String name, String message, String sender)
{
if (log.isDebugEnabled())
{
log.debug("Chat message type {}: {}", ChatMessageType.of(type), message);
}
ChatMessageType chatMessageType = ChatMessageType.of(type);
ChatMessage chatMessage = new ChatMessage(chatMessageType, name, message, sender);
eventBus.post(chatMessage);
}
/**
* Called when a projectile is set to move towards a point. For
* projectiles that target the ground, like AoE projectiles from
* Lizardman Shamans, this is only called once
*
* @param projectile The projectile being moved
* @param targetX X position of where the projectile is being moved to
* @param targetY Y position of where the projectile is being moved to
* @param targetZ Z position of where the projectile is being moved to
* @param cycle
*/
public static void projectileMoved(Projectile projectile, int targetX, int targetY, int targetZ, int cycle)
{
LocalPoint position = new LocalPoint(targetX, targetY);
ProjectileMoved projectileMoved = new ProjectileMoved();
projectileMoved.setProjectile(projectile);
projectileMoved.setPosition(position);
projectileMoved.setZ(targetZ);
eventBus.post(projectileMoved);
}
public static void setMessage(MessageNode messageNode, int type, String name, String sender, String value)
{
SetMessage setMessage = new SetMessage();
setMessage.setMessageNode(messageNode);
setMessage.setType(ChatMessageType.of(type));
setMessage.setName(name);
setMessage.setSender(sender);
setMessage.setValue(value);
eventBus.post(setMessage);
}
public static void onNpcUpdate(boolean var0, PacketBuffer var1)
public static void updateNpcs()
{
// The NPC update event seem to run every server tick,
// but having the game tick event after all packets
// have been processed is typically more useful.
shouldProcessGameTick = true;
}
public static void postItemComposition(ItemComposition itemComposition)
{
PostItemComposition event = new PostItemComposition();
event.setItemComposition(itemComposition);
eventBus.post(event);
}
public static void menuOpened(Client client, int var1, int var2)
{
MenuOpened event = new MenuOpened();
event.setMenuEntries(client.getMenuEntries());
eventBus.post(event);
}
/**
* Called after a hitsplat has been processed on an actor.
* Note that this event runs even if the hitsplat didn't show up,
* i.e. the actor already had 4 visible hitsplats.
*
* @param actor The actor the hitsplat was applied to
* @param type The hitsplat type (i.e. color)
* @param value The value of the hitsplat (i.e. how high the hit was)
* @param var3
* @param var4
* @param gameCycle The gamecycle the hitsplat was applied on
* @param duration The amount of gamecycles the hitsplat will last for
*/
public static void onActorHitsplat(Actor actor, int type, int value, int var3, int var4,
int gameCycle, int duration)
{
Hitsplat hitsplat = new Hitsplat(Hitsplat.HitsplatType.fromInteger(type), value,
gameCycle + duration);
HitsplatApplied event = new HitsplatApplied();
event.setActor(actor);
event.setHitsplat(hitsplat);
eventBus.post(event);
}
public static void onGraphicsObjectCreated(GraphicsObject go, int var1, int var2, int var3, int var4, int var5, int var6, int var7)
{
GraphicsObjectCreated event = new GraphicsObjectCreated(go);
eventBus.post(event);
}
}

View File

@@ -44,11 +44,26 @@ public class Hooks
public static EventBus eventBus;
public static EventBus deferredEventBus;
public static void clientMainLoop()
{
throw new IllegalStateException();
}
public static void draw(MainBufferProvider mainBufferProvider, Graphics graphics, int x, int y)
{
throw new IllegalStateException();
}
public static void drawAboveOverheads()
{
throw new IllegalStateException();
}
public static void drawRegion()
{
throw new IllegalStateException();
}
public static MouseEvent mousePressed(MouseEvent mouseEvent)
{
throw new RuntimeException();
@@ -104,7 +119,7 @@ public class Hooks
throw new RuntimeException();
}
public static boolean menuActionHook(int actionParam, int widgetId, int menuAction, int id, String menuOption, String menuTarget, int var6, int var7)
public static void updateNpcs()
{
throw new RuntimeException();
}

View File

@@ -25,13 +25,22 @@
package net.runelite.mixins;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.events.GraphicsObjectCreated;
import net.runelite.api.mixins.Inject;
import net.runelite.api.mixins.Mixin;
import static net.runelite.client.callback.Hooks.eventBus;
import net.runelite.rs.api.RSGraphicsObject;
@Mixin(RSGraphicsObject.class)
public abstract class GraphicsObjectMixin implements RSGraphicsObject
{
@Inject
GraphicsObjectMixin()
{
final GraphicsObjectCreated event = new GraphicsObjectCreated(this);
eventBus.post(event);
}
@Override
@Inject
public LocalPoint getLocation()

View File

@@ -28,6 +28,7 @@ import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.image.BufferedImage;
import net.runelite.api.Actor;
import net.runelite.api.Hitsplat;
import net.runelite.api.NPC;
import net.runelite.api.NPCComposition;
import net.runelite.api.Perspective;
@@ -39,6 +40,7 @@ import net.runelite.api.coords.WorldArea;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.events.AnimationChanged;
import net.runelite.api.events.GraphicChanged;
import net.runelite.api.events.HitsplatApplied;
import net.runelite.api.events.LocalPlayerDeath;
import net.runelite.api.mixins.FieldHook;
import net.runelite.api.mixins.Inject;
@@ -251,4 +253,27 @@ public abstract class RSActorMixin implements RSActor
}
}
}
/**
* Called after a hitsplat has been processed on an actor.
* Note that this event runs even if the hitsplat didn't show up,
* i.e. the actor already had 4 visible hitsplats.
*
* @param type The hitsplat type (i.e. color)
* @param value The value of the hitsplat (i.e. how high the hit was)
* @param var3 unknown
* @param var4 unknown
* @param gameCycle The gamecycle the hitsplat was applied on
* @param duration The amount of gamecycles the hitsplat will last for
*/
@Inject
@MethodHook(value = "applyActorHitsplat", end = true)
public void applyActorHitsplat(int type, int value, int var3, int var4, int gameCycle, int duration)
{
final Hitsplat hitsplat = new Hitsplat(Hitsplat.HitsplatType.fromInteger(type), value, gameCycle + duration);
final HitsplatApplied event = new HitsplatApplied();
event.setActor(this);
event.setHitsplat(hitsplat);
eventBus.post(event);
}
}

View File

@@ -49,6 +49,7 @@ import static net.runelite.api.MenuAction.PLAYER_THIRD_OPTION;
import net.runelite.api.MenuEntry;
import net.runelite.api.NPC;
import net.runelite.api.Node;
import net.runelite.api.PacketBuffer;
import static net.runelite.api.Perspective.LOCAL_TILE_SIZE;
import net.runelite.api.Player;
import net.runelite.api.Point;
@@ -63,12 +64,15 @@ import net.runelite.api.WorldType;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.events.BoostedLevelChanged;
import net.runelite.api.events.ChatMessage;
import net.runelite.api.events.ClanChanged;
import net.runelite.api.events.DraggingWidgetChanged;
import net.runelite.api.events.ExperienceChanged;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.GrandExchangeOfferChanged;
import net.runelite.api.events.MenuEntryAdded;
import net.runelite.api.events.MenuOpened;
import net.runelite.api.events.MenuOptionClicked;
import net.runelite.api.events.NpcSpawned;
import net.runelite.api.events.PlayerDespawned;
import net.runelite.api.events.PlayerMenuOptionsChanged;
@@ -80,6 +84,7 @@ import net.runelite.api.events.WidgetLoaded;
import net.runelite.api.mixins.Copy;
import net.runelite.api.mixins.FieldHook;
import net.runelite.api.mixins.Inject;
import net.runelite.api.mixins.MethodHook;
import net.runelite.api.mixins.Mixin;
import net.runelite.api.mixins.Replace;
import net.runelite.api.mixins.Shadow;
@@ -89,6 +94,7 @@ import net.runelite.api.widgets.WidgetInfo;
import net.runelite.client.callback.Hooks;
import static net.runelite.client.callback.Hooks.deferredEventBus;
import static net.runelite.client.callback.Hooks.eventBus;
import static net.runelite.client.callback.Hooks.log;
import net.runelite.rs.api.RSClanMemberManager;
import net.runelite.rs.api.RSClient;
import net.runelite.rs.api.RSDeque;
@@ -978,13 +984,31 @@ public abstract class RSClientMixin implements RSClient
}
@Replace("menuAction")
static void rl$menuAction(int var0, int var1, int var2, int var3, String var4, String var5, int var6, int var7)
static void rl$menuAction(int actionParam, int widgetId, int menuAction, int id, String menuOption, String menuTarget, int var6, int var7)
{
if (Hooks.menuActionHook(var0, var1, var2, var3, var4, var5, var6, var7))
/* Along the way, the RuneScape client may change a menuAction by incrementing it with 2000.
* I have no idea why, but it does. Their code contains the same conditional statement.
*/
if (menuAction >= 2000)
{
menuAction -= 2000;
}
final MenuOptionClicked menuOptionClicked = new MenuOptionClicked();
menuOptionClicked.setActionParam(actionParam);
menuOptionClicked.setMenuOption(menuOption);
menuOptionClicked.setMenuTarget(menuTarget);
menuOptionClicked.setMenuAction(MenuAction.of(menuAction));
menuOptionClicked.setId(id);
menuOptionClicked.setWidgetId(widgetId);
eventBus.post(menuOptionClicked);
if (menuOptionClicked.isConsumed())
{
return;
}
rs$menuAction(var0, var1, var2, var3, var4, var5, var6, var7);
rs$menuAction(actionParam, widgetId, menuAction, id, menuOption, menuTarget, var6, var7);
}
@FieldHook("username")
@@ -1015,4 +1039,41 @@ public abstract class RSClientMixin implements RSClient
int flags = getFlags();
return WorldType.fromMask(flags);
}
}
@Inject
@MethodHook("openMenu")
public void menuOpened(int var1, int var2)
{
final MenuOpened event = new MenuOpened();
event.setMenuEntries(getMenuEntries());
eventBus.post(event);
}
@Inject
@MethodHook("updateNpcs")
public static void updateNpcs(boolean var0, PacketBuffer var1)
{
Hooks.updateNpcs();
}
@Inject
@MethodHook("addChatMessage")
public static void onAddChatMessage(int type, String name, String message, String sender)
{
if (log.isDebugEnabled())
{
log.debug("Chat message type {}: {}", ChatMessageType.of(type), message);
}
final ChatMessageType chatMessageType = ChatMessageType.of(type);
final ChatMessage chatMessage = new ChatMessage(chatMessageType, name, message, sender);
eventBus.post(chatMessage);
}
@Inject
@MethodHook("methodDraw")
public void methodDraw(boolean var1)
{
Hooks.clientMainLoop();
}
}

View File

@@ -24,10 +24,14 @@
*/
package net.runelite.mixins;
import java.awt.event.FocusEvent;
import net.runelite.api.events.FocusChanged;
import net.runelite.api.mixins.Copy;
import net.runelite.api.mixins.Inject;
import net.runelite.api.mixins.MethodHook;
import net.runelite.api.mixins.Mixin;
import net.runelite.api.mixins.Replace;
import static net.runelite.client.callback.Hooks.eventBus;
import net.runelite.rs.api.RSGameEngine;
@Mixin(RSGameEngine.class)
@@ -59,4 +63,13 @@ public abstract class RSGameEngineMixin implements RSGameEngine
thread = Thread.currentThread();
rs$run();
}
@Inject
@MethodHook("focusGained")
public void onFocusGained(FocusEvent focusEvent)
{
final FocusChanged focusChanged = new FocusChanged();
focusChanged.setFocused(true);
eventBus.post(focusChanged);
}
}

View File

@@ -25,10 +25,13 @@
package net.runelite.mixins;
import net.runelite.api.events.PostItemComposition;
import net.runelite.api.mixins.Copy;
import net.runelite.api.mixins.Inject;
import net.runelite.api.mixins.MethodHook;
import net.runelite.api.mixins.Mixin;
import net.runelite.api.mixins.Replace;
import static net.runelite.client.callback.Hooks.eventBus;
import net.runelite.rs.api.RSItemComposition;
@Mixin(RSItemComposition.class)
@@ -73,4 +76,13 @@ public abstract class RSItemCompositionMixin implements RSItemComposition
{
shiftClickActionIndex = DEFAULT_CUSTOM_SHIFT_CLICK_INDEX;
}
@Inject
@MethodHook(value = "post", end = true)
public void post()
{
final PostItemComposition event = new PostItemComposition();
event.setItemComposition(this);
eventBus.post(event);
}
}

View File

@@ -24,11 +24,16 @@
*/
package net.runelite.mixins;
import java.awt.event.FocusEvent;
import java.awt.event.KeyEvent;
import net.runelite.api.events.FocusChanged;
import net.runelite.api.mixins.Copy;
import net.runelite.api.mixins.Inject;
import net.runelite.api.mixins.MethodHook;
import net.runelite.api.mixins.Mixin;
import net.runelite.api.mixins.Replace;
import net.runelite.client.callback.Hooks;
import static net.runelite.client.callback.Hooks.eventBus;
import net.runelite.rs.api.RSKeyFocusListener;
@Mixin(RSKeyFocusListener.class)
@@ -75,4 +80,13 @@ public abstract class RSKeyFocusListenerMixin implements RSKeyFocusListener
rs$keyTyped(keyEvent);
}
}
@Inject
@MethodHook("focusLost")
public void onFocusLost(FocusEvent focusEvent)
{
final FocusChanged focusChanged = new FocusChanged();
focusChanged.setFocused(false);
eventBus.post(focusChanged);
}
}

View File

@@ -25,8 +25,11 @@
package net.runelite.mixins;
import net.runelite.api.ChatMessageType;
import net.runelite.api.events.SetMessage;
import net.runelite.api.mixins.Inject;
import net.runelite.api.mixins.MethodHook;
import net.runelite.api.mixins.Mixin;
import static net.runelite.client.callback.Hooks.eventBus;
import net.runelite.rs.api.RSMessageNode;
@Mixin(RSMessageNode.class)
@@ -35,6 +38,18 @@ public abstract class RSMessageNodeMixin implements RSMessageNode
@Inject
private String runeLiteFormatMessage;
@Inject
RSMessageNodeMixin()
{
final SetMessage setMessage = new SetMessage();
setMessage.setMessageNode(this);
setMessage.setType(getType());
setMessage.setName(getName());
setMessage.setSender(getSender());
setMessage.setValue(getValue());
eventBus.post(setMessage);
}
@Inject
@Override
public ChatMessageType getType()
@@ -55,4 +70,17 @@ public abstract class RSMessageNodeMixin implements RSMessageNode
{
this.runeLiteFormatMessage = runeLiteFormatMessage;
}
@Inject
@MethodHook(value = "setMessage", end = true)
public void setMessage(int type, String name, String sender, String value)
{
final SetMessage setMessage = new SetMessage();
setMessage.setMessageNode(this);
setMessage.setType(ChatMessageType.of(type));
setMessage.setName(name);
setMessage.setSender(sender);
setMessage.setValue(value);
eventBus.post(setMessage);
}
}

View File

@@ -25,9 +25,13 @@
package net.runelite.mixins;
import net.runelite.api.Actor;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.events.ProjectileMoved;
import net.runelite.api.mixins.Inject;
import net.runelite.api.mixins.MethodHook;
import net.runelite.api.mixins.Mixin;
import net.runelite.api.mixins.Shadow;
import static net.runelite.client.callback.Hooks.eventBus;
import net.runelite.rs.api.RSClient;
import net.runelite.rs.api.RSNPC;
import net.runelite.rs.api.RSPlayer;
@@ -77,4 +81,26 @@ public abstract class RSProjectileMixin implements RSProjectile
return players[idx];
}
}
/**
* Called when a projectile is set to move towards a point. For
* projectiles that target the ground, like AoE projectiles from
* Lizardman Shamans, this is only called once
*
* @param targetX X position of where the projectile is being moved to
* @param targetY Y position of where the projectile is being moved to
* @param targetZ Z position of where the projectile is being moved to
* @param cycle
*/
@Inject
@MethodHook("moveProjectile")
public void projectileMoved(int targetX, int targetY, int targetZ, int cycle)
{
final LocalPoint position = new LocalPoint(targetX, targetY);
final ProjectileMoved projectileMoved = new ProjectileMoved();
projectileMoved.setProjectile(this);
projectileMoved.setPosition(position);
projectileMoved.setZ(targetZ);
eventBus.post(projectileMoved);
}
}

View File

@@ -31,6 +31,7 @@ import net.runelite.api.mixins.Copy;
import net.runelite.api.mixins.Inject;
import net.runelite.api.mixins.Mixin;
import net.runelite.api.mixins.Replace;
import net.runelite.client.callback.Hooks;
import static net.runelite.client.callback.Hooks.log;
import net.runelite.rs.api.RSDecorativeObject;
import net.runelite.rs.api.RSGroundObject;
@@ -54,6 +55,7 @@ public abstract class RSRegionMixin implements RSRegion
{
isDrawingRegion = true;
rs$drawRegion(cameraX, cameraY, cameraZ, cameraPitch, cameraYaw, plane);
Hooks.drawRegion();
}
finally
{

View File

@@ -0,0 +1,42 @@
/*
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
* 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.mixins;
import net.runelite.api.mixins.Inject;
import net.runelite.api.mixins.MethodHook;
import net.runelite.api.mixins.Mixin;
import net.runelite.client.callback.Hooks;
import net.runelite.rs.api.RSTextureProvider;
@Mixin(RSTextureProvider.class)
public abstract class RSTextureProviderMixin implements RSTextureProvider
{
@Inject
@MethodHook(value = "checkTextures", end = true)
public void checkTextures(int var1)
{
Hooks.drawAboveOverheads();
}
}