Merge pull request #1106 from Abextm/client-threading

Ensure createItemSprite is called on the game thread
This commit is contained in:
Adam
2018-03-25 15:04:39 -04:00
committed by GitHub
9 changed files with 160 additions and 6 deletions

View File

@@ -29,4 +29,8 @@ import java.awt.Canvas;
public interface GameEngine public interface GameEngine
{ {
Canvas getCanvas(); Canvas getCanvas();
Thread getClientThread();
boolean isClientThread();
} }

View File

@@ -0,0 +1,75 @@
/*
* 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.callback;
import com.google.inject.Inject;
import java.util.Iterator;
import java.util.concurrent.ConcurrentLinkedQueue;
import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
@Singleton
@Slf4j
public class ClientThread
{
private ConcurrentLinkedQueue<Runnable> invokes = new ConcurrentLinkedQueue<>();
@Inject
private Client client;
public void invokeLater(Runnable r)
{
if (client.isClientThread())
{
r.run();
return;
}
invokes.add(r);
}
void invoke()
{
assert client.isClientThread();
Iterator<Runnable> ir = invokes.iterator();
for (; ir.hasNext(); )
{
Runnable r = ir.next();
ir.remove();
try
{
r.run();
}
catch (ThreadDeath d)
{
throw d;
}
catch (Throwable e)
{
log.warn("Exception in invokeLater", e);
}
}
}
}

View File

@@ -85,6 +85,7 @@ public class Hooks
private static final OverlayRenderer renderer = injector.getInstance(OverlayRenderer.class); private static final OverlayRenderer renderer = injector.getInstance(OverlayRenderer.class);
private static final MouseManager mouseManager = injector.getInstance(MouseManager.class); private static final MouseManager mouseManager = injector.getInstance(MouseManager.class);
private static final KeyManager keyManager = injector.getInstance(KeyManager.class); private static final KeyManager keyManager = injector.getInstance(KeyManager.class);
private static final ClientThread clientThread = injector.getInstance(ClientThread.class);
private static final GameTick tick = new GameTick(); private static final GameTick tick = new GameTick();
private static Dimension lastStretchedDimensions; private static Dimension lastStretchedDimensions;
@@ -95,6 +96,8 @@ public class Hooks
public static void clientMainLoop(Client client, boolean arg1) public static void clientMainLoop(Client client, boolean arg1)
{ {
clientThread.invoke();
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
if (now - lastCheck < CHECK) if (now - lastCheck < CHECK)

View File

@@ -52,6 +52,7 @@ import net.runelite.api.events.ConfigChanged;
import net.runelite.api.events.GameObjectSpawned; import net.runelite.api.events.GameObjectSpawned;
import net.runelite.api.events.ProjectileMoved; import net.runelite.api.events.ProjectileMoved;
import net.runelite.client.Notifier; import net.runelite.client.Notifier;
import net.runelite.client.callback.ClientThread;
import net.runelite.client.config.ConfigManager; import net.runelite.client.config.ConfigManager;
import net.runelite.client.game.ItemManager; import net.runelite.client.game.ItemManager;
import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.Plugin;
@@ -106,6 +107,9 @@ public class CannonPlugin extends Plugin
@Inject @Inject
private Client client; private Client client;
@Inject
private ClientThread clientThread;
@Provides @Provides
CannonConfig provideConfig(ConfigManager configManager) CannonConfig provideConfig(ConfigManager configManager)
{ {
@@ -140,7 +144,7 @@ public class CannonPlugin extends Plugin
{ {
if (cannonPlaced) if (cannonPlaced)
{ {
addCounter(); clientThread.invokeLater(this::addCounter);
} }
} }
} }

View File

@@ -148,7 +148,7 @@ public class RunepouchOverlay extends Overlay
{ {
OverlayUtil.renderImageLocation(graphics, OverlayUtil.renderImageLocation(graphics,
new Point(location.getX(), location.getY() + graphics.getFontMetrics().getHeight() * i), new Point(location.getX(), location.getY() + graphics.getFontMetrics().getHeight() * i),
getRuneImage(rune)); image);
} }
} }

View File

@@ -49,6 +49,7 @@ import net.runelite.api.events.GameStateChanged;
import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetInfo; import net.runelite.api.widgets.WidgetInfo;
import net.runelite.client.Notifier; import net.runelite.client.Notifier;
import net.runelite.client.callback.ClientThread;
import net.runelite.client.config.ConfigManager; import net.runelite.client.config.ConfigManager;
import net.runelite.client.game.ItemManager; import net.runelite.client.game.ItemManager;
import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.Plugin;
@@ -96,6 +97,9 @@ public class SlayerPlugin extends Plugin
@Inject @Inject
private Notifier notifier; private Notifier notifier;
@Inject
private ClientThread clientThread;
private String taskName; private String taskName;
private int amount; private int amount;
private TaskCounter counter; private TaskCounter counter;
@@ -112,7 +116,7 @@ public class SlayerPlugin extends Plugin
&& config.amount() != -1 && config.amount() != -1
&& !config.taskName().isEmpty()) && !config.taskName().isEmpty())
{ {
setTask(config.taskName(), config.amount()); clientThread.invokeLater(() -> setTask(config.taskName(), config.amount()));
} }
} }
@@ -307,7 +311,7 @@ public class SlayerPlugin extends Plugin
if (config.showInfobox()) if (config.showInfobox())
{ {
addCounter(); clientThread.invokeLater(this::addCounter);
} }
else else
{ {

View File

@@ -55,8 +55,8 @@ import net.runelite.api.SpritePixels;
import net.runelite.api.Varbits; import net.runelite.api.Varbits;
import net.runelite.api.WidgetNode; import net.runelite.api.WidgetNode;
import net.runelite.api.coords.LocalPoint; import net.runelite.api.coords.LocalPoint;
import net.runelite.api.events.DraggingWidgetChanged;
import net.runelite.api.events.BoostedLevelChanged; import net.runelite.api.events.BoostedLevelChanged;
import net.runelite.api.events.DraggingWidgetChanged;
import net.runelite.api.events.ExperienceChanged; import net.runelite.api.events.ExperienceChanged;
import net.runelite.api.events.GameStateChanged; import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.GrandExchangeOfferChanged; import net.runelite.api.events.GrandExchangeOfferChanged;
@@ -480,6 +480,7 @@ public abstract class RSClientMixin implements RSClient
@Override @Override
public SpritePixels createItemSprite(int itemId, int quantity, int border, int shadowColor, int stackable, boolean noted, int scale) public SpritePixels createItemSprite(int itemId, int quantity, int border, int shadowColor, int stackable, boolean noted, int scale)
{ {
assert isClientThread();
int zoom = get3dZoom(); int zoom = get3dZoom();
set3dZoom(scale); set3dZoom(scale);
try try

View File

@@ -0,0 +1,62 @@
/*
* 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.mixins;
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.rs.api.RSGameEngine;
@Mixin(RSGameEngine.class)
public abstract class RSGameEngineMixin implements RSGameEngine
{
@Inject
private Thread thread;
@Inject
@Override
public Thread getClientThread()
{
return thread;
}
@Inject
@Override
public boolean isClientThread()
{
return thread == Thread.currentThread();
}
@Copy("run")
public abstract void rs$run();
@Replace("run")
public void rl$run()
{
thread = Thread.currentThread();
rs$run();
}
}

View File

@@ -25,10 +25,11 @@
package net.runelite.rs.api; package net.runelite.rs.api;
import java.awt.Canvas; import java.awt.Canvas;
import net.runelite.api.GameEngine;
import net.runelite.api.KeyFocusListener; import net.runelite.api.KeyFocusListener;
import net.runelite.mapping.Import; import net.runelite.mapping.Import;
public interface RSGameEngine extends KeyFocusListener public interface RSGameEngine extends GameEngine, KeyFocusListener
{ {
@Import("canvas") @Import("canvas")
Canvas getCanvas(); Canvas getCanvas();