Merge remote-tracking branch 'upstream/master' into runelite

# Conflicts:
#	cache-client/pom.xml
#	cache-updater/pom.xml
#	cache/pom.xml
#	http-api/pom.xml
#	http-service/pom.xml
#	pom.xml
#	runelite-api/pom.xml
#	runelite-api/src/main/java/net/runelite/api/Client.java
#	runelite-api/src/main/java/net/runelite/api/FarmingTrackerTest.java
#	runelite-client/pom.xml
#	runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperConfig.java
#	runelite-client/src/test/java/net/runelite/client/chat/ChatMessageManagerTest.java
#	runelite-client/src/test/java/net/runelite/client/plugins/cluescrolls/ClueScrollPluginTest.java
#	runelite-client/src/test/java/net/runelite/client/plugins/emojis/EmojiPluginTest.java
#	runelite-client/src/test/java/net/runelite/client/plugins/grounditems/GroundItemsPluginTest.java
#	runelite-client/src/test/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPluginTest.java
#	runelite-script-assembler-plugin/pom.xml
This commit is contained in:
ThatGamerBlue
2021-02-14 00:31:56 +00:00
81 changed files with 3354 additions and 657 deletions

View File

@@ -27,13 +27,26 @@ package net.runelite.http.api.gson;
import com.google.gson.ExclusionStrategy; import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes; import com.google.gson.FieldAttributes;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.HashSet;
import java.util.Set;
public class IllegalReflectionExclusion implements ExclusionStrategy public class IllegalReflectionExclusion implements ExclusionStrategy
{ {
private static final Set<ClassLoader> PRIVATE_CLASSLOADERS = new HashSet<>();
static
{
for (ClassLoader cl = ClassLoader.getSystemClassLoader(); cl != null; )
{
cl = cl.getParent();
PRIVATE_CLASSLOADERS.add(cl);
}
}
@Override @Override
public boolean shouldSkipField(FieldAttributes f) public boolean shouldSkipField(FieldAttributes f)
{ {
if (f.getDeclaringClass().getName().startsWith("net.runelite")) if (!PRIVATE_CLASSLOADERS.contains(f.getDeclaringClass().getClassLoader()))
{ {
return false; return false;
} }

View File

@@ -97,6 +97,7 @@ public final class AnimationID
public static final int CRAFTING_SPINNING = 894; public static final int CRAFTING_SPINNING = 894;
public static final int CRAFTING_POTTERS_WHEEL = 883; public static final int CRAFTING_POTTERS_WHEEL = 883;
public static final int CRAFTING_POTTERY_OVEN = 24975; public static final int CRAFTING_POTTERY_OVEN = 24975;
public static final int CRAFTING_LOOM = 2270;
public static final int SMITHING_SMELTING = 899; public static final int SMITHING_SMELTING = 899;
public static final int SMITHING_CANNONBALL = 827; //cball smithing uses this and SMITHING_SMELTING public static final int SMITHING_CANNONBALL = 827; //cball smithing uses this and SMITHING_SMELTING
public static final int SMITHING_ANVIL = 898; public static final int SMITHING_ANVIL = 898;
@@ -209,6 +210,12 @@ public final class AnimationID
public static final int HOME_MAKE_TABLET = 4067; public static final int HOME_MAKE_TABLET = 4067;
public static final int DRAGONFIRE_SHIELD_SPECIAL = 6696; public static final int DRAGONFIRE_SHIELD_SPECIAL = 6696;
// Ectofuntus animations
public static final int ECTOFUNTUS_FILL_SLIME_BUCKET = 4471;
public static final int ECTOFUNTUS_GRIND_BONES = 1648;
public static final int ECTOFUNTUS_INSERT_BONES = 1649;
public static final int ECTOFUNTUS_EMPTY_BIN = 1650;
// NPC animations // NPC animations
public static final int TZTOK_JAD_MAGIC_ATTACK = 2656; public static final int TZTOK_JAD_MAGIC_ATTACK = 2656;
public static final int TZTOK_JAD_RANGE_ATTACK = 2652; public static final int TZTOK_JAD_RANGE_ATTACK = 2652;

View File

@@ -124,12 +124,25 @@ public interface Client extends GameEngine
/** /**
* Adds a new chat message to the chatbox. * Adds a new chat message to the chatbox.
* *
* @param type the type of message * @param type the type of message
* @param name the name of the player that sent the message * @param name the name of the player that sent the message
* @param message the message contents * @param message the message contents
* @param sender the sender/channel name * @param sender the sender/channel name
* @return the message node for the message
*/ */
void addChatMessage(ChatMessageType type, String name, String message, String sender); MessageNode addChatMessage(ChatMessageType type, String name, String message, String sender);
/**
* Adds a new chat message to the chatbox.
*
* @param type the type of message
* @param name the name of the player that sent the message
* @param message the message contents
* @param sender the sender/channel name
* @param postEvent whether to post the chat message event
* @return the message node for the message
*/
MessageNode addChatMessage(ChatMessageType type, String name, String message, String sender, boolean postEvent);
/** /**
* Gets the current game state. * Gets the current game state.
@@ -1542,7 +1555,7 @@ public interface Client extends GameEngine
* *
* @param state the new player hidden state * @param state the new player hidden state
*/ */
void setPlayersHidden(boolean state); void setOthersHidden(boolean state);
/** /**
* Sets whether 2D sprites related to the other players are hidden. * Sets whether 2D sprites related to the other players are hidden.
@@ -1550,7 +1563,7 @@ public interface Client extends GameEngine
* *
* @param state the new player 2D hidden state * @param state the new player 2D hidden state
*/ */
void setPlayersHidden2D(boolean state); void setOthersHidden2D(boolean state);
/** /**
* Sets whether or not friends are hidden. * Sets whether or not friends are hidden.
@@ -1566,6 +1579,13 @@ public interface Client extends GameEngine
*/ */
void setFriendsChatMembersHidden(boolean state); void setFriendsChatMembersHidden(boolean state);
/**
* Sets whether or not ignored players are hidden.
*
* @param state the new ignored player hidden state
*/
void setIgnoresHidden(boolean state);
/** /**
* Sets whether the local player is hidden. * Sets whether the local player is hidden.
* *

View File

@@ -376,7 +376,7 @@ public class Perspective
*/ */
public static Polygon getCanvasTilePoly(@Nonnull Client client, @Nonnull LocalPoint localLocation, int zOffset) public static Polygon getCanvasTilePoly(@Nonnull Client client, @Nonnull LocalPoint localLocation, int zOffset)
{ {
return getCanvasTileAreaPoly(client, localLocation, 1, zOffset); return getCanvasTileAreaPoly(client, localLocation, 1, 1, zOffset);
} }
/** /**
@@ -389,7 +389,7 @@ public class Perspective
*/ */
public static Polygon getCanvasTileAreaPoly(@Nonnull Client client, @Nonnull LocalPoint localLocation, int size) public static Polygon getCanvasTileAreaPoly(@Nonnull Client client, @Nonnull LocalPoint localLocation, int size)
{ {
return getCanvasTileAreaPoly(client, localLocation, size, 0); return getCanvasTileAreaPoly(client, localLocation, size, size, 0);
} }
/** /**
@@ -397,23 +397,25 @@ public class Perspective
* *
* @param client the game client * @param client the game client
* @param localLocation the center location of the AoE * @param localLocation the center location of the AoE
* @param size the size of the area (ie. 3x3 AoE evaluates to size 3) * @param sizeX the size of the area in tiles on the x axis
* @param sizeY the size of the area in tiles on the y axis
* @param zOffset offset from ground plane * @param zOffset offset from ground plane
* @return a polygon representing the tiles in the area * @return a polygon representing the tiles in the area
*/ */
public static Polygon getCanvasTileAreaPoly( public static Polygon getCanvasTileAreaPoly(
@Nonnull Client client, @Nonnull Client client,
@Nonnull LocalPoint localLocation, @Nonnull LocalPoint localLocation,
int size, int sizeX,
int sizeY,
int zOffset) int zOffset)
{ {
final int plane = client.getPlane(); final int plane = client.getPlane();
final int swX = localLocation.getX() - (size * LOCAL_TILE_SIZE / 2); final int swX = localLocation.getX() - (sizeX * LOCAL_TILE_SIZE / 2);
final int swY = localLocation.getY() - (size * LOCAL_TILE_SIZE / 2); final int swY = localLocation.getY() - (sizeY * LOCAL_TILE_SIZE / 2);
final int neX = localLocation.getX() + (size * LOCAL_TILE_SIZE / 2); final int neX = localLocation.getX() + (sizeX * LOCAL_TILE_SIZE / 2);
final int neY = localLocation.getY() + (size * LOCAL_TILE_SIZE / 2); final int neY = localLocation.getY() + (sizeY * LOCAL_TILE_SIZE / 2);
final byte[][][] tileSettings = client.getTileSettings(); final byte[][][] tileSettings = client.getTileSettings();

View File

@@ -59,6 +59,7 @@ import net.runelite.api.ChatMessageType;
import net.runelite.api.Client; import net.runelite.api.Client;
import net.runelite.api.Constants; import net.runelite.api.Constants;
import net.runelite.api.GameState; import net.runelite.api.GameState;
import net.runelite.api.Player;
import net.runelite.client.chat.ChatColorType; import net.runelite.client.chat.ChatColorType;
import net.runelite.client.chat.ChatMessageBuilder; import net.runelite.client.chat.ChatMessageBuilder;
import net.runelite.client.chat.ChatMessageManager; import net.runelite.client.chat.ChatMessageManager;
@@ -175,7 +176,7 @@ public class Notifier
if (runeLiteConfig.enableTrayNotifications()) if (runeLiteConfig.enableTrayNotifications())
{ {
sendNotification(appName, message, type); sendNotification(buildTitle(), message, type);
} }
switch (runeLiteConfig.notificationSound()) switch (runeLiteConfig.notificationSound())
@@ -210,6 +211,23 @@ public class Notifier
log.debug(message); log.debug(message);
} }
private String buildTitle()
{
Player player = client.getLocalPlayer();
if (player == null)
{
return appName;
}
String name = player.getName();
if (Strings.isNullOrEmpty(name))
{
return appName;
}
return appName + " - " + name;
}
public void processFlash(final Graphics2D graphics) public void processFlash(final Graphics2D graphics)
{ {
FlashNotification flashNotification = runeLiteConfig.flashNotification(); FlashNotification flashNotification = runeLiteConfig.flashNotification();

View File

@@ -106,11 +106,6 @@ public class ChatCommandManager implements ChatboxInputListener
String message = chatMessage.getMessage(); String message = chatMessage.getMessage();
String command = extractCommand(message); String command = extractCommand(message);
if (command == null)
{
return;
}
ChatCommand chatCommand = commands.get(command.toLowerCase()); ChatCommand chatCommand = commands.get(command.toLowerCase());
if (chatCommand == null) if (chatCommand == null)
{ {
@@ -137,11 +132,6 @@ public class ChatCommandManager implements ChatboxInputListener
} }
String command = extractCommand(message); String command = extractCommand(message);
if (command == null)
{
return false;
}
ChatCommand chatCommand = commands.get(command.toLowerCase()); ChatCommand chatCommand = commands.get(command.toLowerCase());
if (chatCommand == null) if (chatCommand == null)
{ {
@@ -163,11 +153,6 @@ public class ChatCommandManager implements ChatboxInputListener
final String message = privateMessageInput.getMessage(); final String message = privateMessageInput.getMessage();
String command = extractCommand(message); String command = extractCommand(message);
if (command == null)
{
return false;
}
ChatCommand chatCommand = commands.get(command.toLowerCase()); ChatCommand chatCommand = commands.get(command.toLowerCase());
if (chatCommand == null) if (chatCommand == null)
{ {

View File

@@ -39,22 +39,22 @@ import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import net.runelite.api.ChatLineBuffer;
import net.runelite.api.ChatMessageType; import net.runelite.api.ChatMessageType;
import net.runelite.api.Client; import net.runelite.api.Client;
import net.runelite.api.MessageNode; import net.runelite.api.MessageNode;
import net.runelite.api.Player; import net.runelite.api.Player;
import net.runelite.api.Varbits; import net.runelite.api.Varbits;
import net.runelite.api.events.ChatMessage; import net.runelite.api.events.ChatMessage;
import net.runelite.client.events.ConfigChanged;
import net.runelite.api.events.ResizeableChanged; import net.runelite.api.events.ResizeableChanged;
import net.runelite.api.events.ScriptCallbackEvent; import net.runelite.api.events.ScriptCallbackEvent;
import net.runelite.api.events.VarbitChanged; import net.runelite.api.events.VarbitChanged;
import net.runelite.client.callback.ClientThread; import net.runelite.client.callback.ClientThread;
import net.runelite.client.config.ChatColorConfig; import net.runelite.client.config.ChatColorConfig;
import net.runelite.client.eventbus.Subscribe; import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.events.ConfigChanged;
import net.runelite.client.ui.JagexColors; import net.runelite.client.ui.JagexColors;
import net.runelite.client.util.ColorUtil; import net.runelite.client.util.ColorUtil;
import net.runelite.client.util.Text;
@Singleton @Singleton
public class ChatMessageManager public class ChatMessageManager
@@ -126,7 +126,8 @@ public class ChatMessageManager
case PUBLICCHAT: case PUBLICCHAT:
case MODCHAT: case MODCHAT:
{ {
boolean isFriend = client.isFriended(chatMessage.getName(), true) && !client.getLocalPlayer().getName().equals(chatMessage.getName()); String sanitizedUsername = Text.removeTags(chatMessage.getName());
boolean isFriend = client.isFriended(sanitizedUsername, true) && !client.getLocalPlayer().getName().equals(sanitizedUsername);
if (isFriend) if (isFriend)
{ {
@@ -571,18 +572,15 @@ public class ChatMessageManager
return; return;
} }
final String formattedMessage = formatRuneLiteMessage(message.getRuneLiteFormattedMessage(), message.getType());
// this updates chat cycle // this updates chat cycle
client.addChatMessage( final MessageNode line = client.addChatMessage(
message.getType(), message.getType(),
MoreObjects.firstNonNull(message.getName(), ""), MoreObjects.firstNonNull(message.getName(), ""),
MoreObjects.firstNonNull(message.getValue(), message.getRuneLiteFormattedMessage()), MoreObjects.firstNonNull(formattedMessage, message.getValue()),
message.getSender()); message.getSender());
// Get last message from line buffer (the one we just added)
final ChatLineBuffer chatLineBuffer = client.getChatLineMap().get(message.getType().getType());
final MessageNode[] lines = chatLineBuffer.getLines();
final MessageNode line = lines[0];
// Update the message with RuneLite additions // Update the message with RuneLite additions
line.setRuneLiteFormatMessage(message.getRuneLiteFormattedMessage()); line.setRuneLiteFormatMessage(message.getRuneLiteFormattedMessage());
@@ -590,34 +588,38 @@ public class ChatMessageManager
{ {
line.setTimestamp(message.getTimestamp()); line.setTimestamp(message.getTimestamp());
} }
update(line);
} }
public void update(final MessageNode target) /**
* Rebuild the message node message from the RuneLite format message
*
* @param messageNode message node
*/
public void update(final MessageNode messageNode)
{ {
if (Strings.isNullOrEmpty(target.getRuneLiteFormatMessage())) String message = formatRuneLiteMessage(messageNode.getRuneLiteFormatMessage(), messageNode.getType());
if (message != null)
{ {
return; messageNode.setValue(message);
}
}
private String formatRuneLiteMessage(String runeLiteFormatMessage, ChatMessageType type)
{
if (Strings.isNullOrEmpty(runeLiteFormatMessage))
{
return null;
} }
final boolean transparent = client.isResized() && transparencyVarbit != 0; final boolean transparent = client.isResized() && transparencyVarbit != 0;
final Collection<ChatColor> chatColors = colorCache.get(target.getType()); final Collection<ChatColor> chatColors = colorCache.get(type);
// If we do not have any colors cached, simply set clean message
if (chatColors == null || chatColors.isEmpty()) if (chatColors == null || chatColors.isEmpty())
{ {
target.setValue(target.getRuneLiteFormatMessage()); return runeLiteFormatMessage;
return;
} }
target.setValue(recolorMessage(transparent, target.getRuneLiteFormatMessage(), target.getType())); final AtomicReference<String> resultMessage = new AtomicReference<>(runeLiteFormatMessage);
}
private String recolorMessage(boolean transparent, String message, ChatMessageType messageType)
{
final Collection<ChatColor> chatColors = colorCache.get(messageType);
final AtomicReference<String> resultMessage = new AtomicReference<>(message);
// Replace custom formatting with actual colors // Replace custom formatting with actual colors
chatColors.stream() chatColors.stream()

View File

@@ -152,6 +152,11 @@ public class ConfigManager
scheduledExecutorService.scheduleWithFixedDelay(this::sendConfig, 30, 30, TimeUnit.SECONDS); scheduledExecutorService.scheduleWithFixedDelay(this::sendConfig, 30, 30, TimeUnit.SECONDS);
} }
public String getRSProfileKey()
{
return rsProfileKey;
}
public final void switchSession(AccountSession session) public final void switchSession(AccountSession session)
{ {
// Ensure existing config is saved // Ensure existing config is saved
@@ -507,6 +512,11 @@ public class ConfigManager
public void setConfiguration(String groupName, String profile, String key, String value) public void setConfiguration(String groupName, String profile, String key, String value)
{ {
if (Strings.isNullOrEmpty(groupName) || Strings.isNullOrEmpty(key))
{
throw new IllegalArgumentException();
}
assert !key.startsWith(RSPROFILE_GROUP + "."); assert !key.startsWith(RSPROFILE_GROUP + ".");
String wholeKey = getWholeKey(groupName, profile, key); String wholeKey = getWholeKey(groupName, profile, key);
String oldValue = (String) properties.setProperty(wholeKey, value); String oldValue = (String) properties.setProperty(wholeKey, value);

View File

@@ -29,6 +29,9 @@ import java.util.Map;
import lombok.Getter; import lombok.Getter;
import net.runelite.api.ItemID; import net.runelite.api.ItemID;
import static net.runelite.api.NpcID.FISHING_SPOT; import static net.runelite.api.NpcID.FISHING_SPOT;
import static net.runelite.api.NpcID.FISHING_SPOT_10513;
import static net.runelite.api.NpcID.FISHING_SPOT_10514;
import static net.runelite.api.NpcID.FISHING_SPOT_10515;
import static net.runelite.api.NpcID.FISHING_SPOT_1497; import static net.runelite.api.NpcID.FISHING_SPOT_1497;
import static net.runelite.api.NpcID.FISHING_SPOT_1498; import static net.runelite.api.NpcID.FISHING_SPOT_1498;
import static net.runelite.api.NpcID.FISHING_SPOT_1499; import static net.runelite.api.NpcID.FISHING_SPOT_1499;
@@ -132,13 +135,14 @@ public enum FishingSpot
FISHING_SPOT_1525, FISHING_SPOT_1528, FISHING_SPOT_1530, FISHING_SPOT_1525, FISHING_SPOT_1528, FISHING_SPOT_1530,
FISHING_SPOT_1544, FISHING_SPOT_3913, FISHING_SPOT_7155, FISHING_SPOT_1544, FISHING_SPOT_3913, FISHING_SPOT_7155,
FISHING_SPOT_7459, FISHING_SPOT_7462, FISHING_SPOT_7467, FISHING_SPOT_7459, FISHING_SPOT_7462, FISHING_SPOT_7467,
FISHING_SPOT_7469, FISHING_SPOT_7947 FISHING_SPOT_7469, FISHING_SPOT_7947, FISHING_SPOT_10513
), ),
LOBSTER("Lobster, Swordfish, Tuna", "Lobster", ItemID.RAW_LOBSTER, LOBSTER("Lobster, Swordfish, Tuna", "Lobster", ItemID.RAW_LOBSTER,
FISHING_SPOT_1510, FISHING_SPOT_1519, FISHING_SPOT_1522, FISHING_SPOT_1510, FISHING_SPOT_1519, FISHING_SPOT_1522,
FISHING_SPOT_3914, FISHING_SPOT_5820, FISHING_SPOT_7199, FISHING_SPOT_3914, FISHING_SPOT_5820, FISHING_SPOT_7199,
FISHING_SPOT_7460, FISHING_SPOT_7465, FISHING_SPOT_7470, FISHING_SPOT_7460, FISHING_SPOT_7465, FISHING_SPOT_7470,
FISHING_SPOT_7946, FISHING_SPOT_9173, FISHING_SPOT_9174 FISHING_SPOT_7946, FISHING_SPOT_9173, FISHING_SPOT_9174,
FISHING_SPOT_10515
), ),
SHARK("Shark, Bass", "Shark", ItemID.RAW_SHARK, SHARK("Shark, Bass", "Shark", ItemID.RAW_SHARK,
FISHING_SPOT_1511, FISHING_SPOT_1520, FISHING_SPOT_3419, FISHING_SPOT_1511, FISHING_SPOT_1520, FISHING_SPOT_3419,
@@ -146,7 +150,7 @@ public enum FishingSpot
FISHING_SPOT_5233, FISHING_SPOT_5234, FISHING_SPOT_5821, FISHING_SPOT_5233, FISHING_SPOT_5234, FISHING_SPOT_5821,
FISHING_SPOT_7200, FISHING_SPOT_7461, FISHING_SPOT_7466, FISHING_SPOT_7200, FISHING_SPOT_7461, FISHING_SPOT_7466,
FISHING_SPOT_8525, FISHING_SPOT_8526, FISHING_SPOT_8527, FISHING_SPOT_8525, FISHING_SPOT_8526, FISHING_SPOT_8527,
FISHING_SPOT_9171, FISHING_SPOT_9172 FISHING_SPOT_9171, FISHING_SPOT_9172, FISHING_SPOT_10514
), ),
MONKFISH("Monkfish", ItemID.RAW_MONKFISH, MONKFISH("Monkfish", ItemID.RAW_MONKFISH,
FISHING_SPOT_4316 FISHING_SPOT_4316

View File

@@ -0,0 +1,31 @@
/*
* Copyright (c) 2021, Adam <Adam@sigterm.info>
* 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 HOLDER 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.plugins.banktags;
public interface BankTag
{
boolean contains(int itemId);
}

View File

@@ -75,13 +75,11 @@ import net.runelite.client.game.chatbox.ChatboxPanelManager;
import net.runelite.client.input.MouseManager; import net.runelite.client.input.MouseManager;
import net.runelite.client.input.MouseWheelListener; import net.runelite.client.input.MouseWheelListener;
import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDependency;
import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.plugins.banktags.tabs.TabInterface; import net.runelite.client.plugins.banktags.tabs.TabInterface;
import static net.runelite.client.plugins.banktags.tabs.TabInterface.FILTERED_CHARS; import static net.runelite.client.plugins.banktags.tabs.TabInterface.FILTERED_CHARS;
import net.runelite.client.plugins.banktags.tabs.TabSprites; import net.runelite.client.plugins.banktags.tabs.TabSprites;
import net.runelite.client.plugins.banktags.tabs.TagTab; import net.runelite.client.plugins.banktags.tabs.TagTab;
import net.runelite.client.plugins.cluescrolls.ClueScrollPlugin;
import net.runelite.client.util.Text; import net.runelite.client.util.Text;
@PluginDescriptor( @PluginDescriptor(
@@ -89,7 +87,6 @@ import net.runelite.client.util.Text;
description = "Enable tagging of bank items and searching of bank tags", description = "Enable tagging of bank items and searching of bank tags",
tags = {"searching", "tagging"} tags = {"searching", "tagging"}
) )
@PluginDependency(ClueScrollPlugin.class)
public class BankTagsPlugin extends Plugin implements MouseWheelListener public class BankTagsPlugin extends Plugin implements MouseWheelListener
{ {
public static final String CONFIG_GROUP = "banktags"; public static final String CONFIG_GROUP = "banktags";

View File

@@ -27,24 +27,17 @@ package net.runelite.client.plugins.banktags;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import net.runelite.api.ItemID;
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.game.ItemVariationMapping; import net.runelite.client.game.ItemVariationMapping;
import static net.runelite.client.plugins.banktags.BankTagsPlugin.CONFIG_GROUP; import static net.runelite.client.plugins.banktags.BankTagsPlugin.CONFIG_GROUP;
import net.runelite.client.plugins.cluescrolls.ClueScrollService;
import net.runelite.client.plugins.cluescrolls.clues.ClueScroll;
import net.runelite.client.plugins.cluescrolls.clues.CoordinateClue;
import net.runelite.client.plugins.cluescrolls.clues.EmoteClue;
import net.runelite.client.plugins.cluescrolls.clues.FairyRingClue;
import net.runelite.client.plugins.cluescrolls.clues.HotColdClue;
import net.runelite.client.plugins.cluescrolls.clues.MapClue;
import net.runelite.client.plugins.cluescrolls.clues.item.ItemRequirement;
import net.runelite.client.util.Text; import net.runelite.client.util.Text;
@Singleton @Singleton
@@ -53,17 +46,15 @@ public class TagManager
static final String ITEM_KEY_PREFIX = "item_"; static final String ITEM_KEY_PREFIX = "item_";
private final ConfigManager configManager; private final ConfigManager configManager;
private final ItemManager itemManager; private final ItemManager itemManager;
private final ClueScrollService clueScrollService; private final Map<String, BankTag> customTags = new HashMap<>();
@Inject @Inject
private TagManager( private TagManager(
final ItemManager itemManager, final ItemManager itemManager,
final ConfigManager configManager, final ConfigManager configManager)
final ClueScrollService clueScrollService)
{ {
this.itemManager = itemManager; this.itemManager = itemManager;
this.configManager = configManager; this.configManager = configManager;
this.clueScrollService = clueScrollService;
} }
String getTagString(int itemId, boolean variation) String getTagString(int itemId, boolean variation)
@@ -123,7 +114,8 @@ public class TagManager
boolean findTag(int itemId, String search) boolean findTag(int itemId, String search)
{ {
if (search.equals("clue") && testClue(itemId)) BankTag bankTag = customTags.get(search);
if (bankTag != null && bankTag.contains(itemId))
{ {
return true; return true;
} }
@@ -194,38 +186,13 @@ public class TagManager
return itemId; return itemId;
} }
private boolean testClue(int itemId) public void registerTag(String name, BankTag tag)
{ {
ClueScroll c = clueScrollService.getClue(); customTags.put(name, tag);
}
if (c == null) public void unregisterTag(String name)
{ {
return false; customTags.remove(name);
}
if (c instanceof EmoteClue)
{
EmoteClue emote = (EmoteClue) c;
for (ItemRequirement ir : emote.getItemRequirements())
{
if (ir.fulfilledBy(itemId))
{
return true;
}
}
}
else if (c instanceof CoordinateClue || c instanceof HotColdClue || c instanceof FairyRingClue)
{
return itemId == ItemID.SPADE;
}
else if (c instanceof MapClue)
{
MapClue mapClue = (MapClue) c;
return mapClue.getObjectId() == -1 && itemId == ItemID.SPADE;
}
return false;
} }
} }

View File

@@ -304,7 +304,8 @@ public class CannonPlugin extends Plugin
} }
if (event.getMessage().contains("You pick up the cannon") if (event.getMessage().contains("You pick up the cannon")
|| event.getMessage().contains("Your cannon has decayed. Speak to Nulodion to get a new one!")) || event.getMessage().contains("Your cannon has decayed. Speak to Nulodion to get a new one!")
|| event.getMessage().contains("Your cannon has been destroyed!"))
{ {
cannonPlaced = false; cannonPlaced = false;
cballsLeft = 0; cballsLeft = 0;

View File

@@ -49,7 +49,7 @@ enum CannonSpots
DUST_DEVIL(new WorldPoint(3218, 9366, 0)), DUST_DEVIL(new WorldPoint(3218, 9366, 0)),
EARTH_WARRIOR(new WorldPoint(3120, 9987, 0)), EARTH_WARRIOR(new WorldPoint(3120, 9987, 0)),
ELDER_CHAOS_DRUID(new WorldPoint(3237, 3622, 0)), ELDER_CHAOS_DRUID(new WorldPoint(3237, 3622, 0)),
ELVES(new WorldPoint(2044, 4635, 0), new WorldPoint(3278, 6098, 0)), ELVES(new WorldPoint(3278, 6098, 0)),
FIRE_GIANTS(new WorldPoint(2393, 9782, 0), new WorldPoint(2412, 9776, 0), new WorldPoint(2401, 9780, 0), new WorldPoint(3047, 10340, 0)), FIRE_GIANTS(new WorldPoint(2393, 9782, 0), new WorldPoint(2412, 9776, 0), new WorldPoint(2401, 9780, 0), new WorldPoint(3047, 10340, 0)),
GREATER_DEMONS(new WorldPoint(1435, 10086, 2), new WorldPoint(3224, 10132, 0)), GREATER_DEMONS(new WorldPoint(1435, 10086, 2), new WorldPoint(3224, 10132, 0)),
GREEN_DRAGON(new WorldPoint(3225, 10068, 0)), GREEN_DRAGON(new WorldPoint(3225, 10068, 0)),

View File

@@ -666,7 +666,7 @@ public class ChatCommandsPlugin extends Plugin
.append(ChatColorType.NORMAL) .append(ChatColorType.NORMAL)
.append(" kill count: ") .append(" kill count: ")
.append(ChatColorType.HIGHLIGHT) .append(ChatColorType.HIGHLIGHT)
.append(Integer.toString(kc)) .append(String.format("%,d", kc))
.build(); .build();
log.debug("Setting response {}", response); log.debug("Setting response {}", response);
@@ -748,15 +748,15 @@ public class ChatCommandsPlugin extends Plugin
.append(ChatColorType.NORMAL) .append(ChatColorType.NORMAL)
.append("Duel Arena wins: ") .append("Duel Arena wins: ")
.append(ChatColorType.HIGHLIGHT) .append(ChatColorType.HIGHLIGHT)
.append(Integer.toString(wins)) .append(String.format("%,d", wins))
.append(ChatColorType.NORMAL) .append(ChatColorType.NORMAL)
.append(" losses: ") .append(" losses: ")
.append(ChatColorType.HIGHLIGHT) .append(ChatColorType.HIGHLIGHT)
.append(Integer.toString(losses)) .append(String.format("%,d", losses))
.append(ChatColorType.NORMAL) .append(ChatColorType.NORMAL)
.append(" streak: ") .append(" streak: ")
.append(ChatColorType.HIGHLIGHT) .append(ChatColorType.HIGHLIGHT)
.append(Integer.toString((winningStreak != 0 ? winningStreak : -losingStreak))) .append(String.format("%,d", winningStreak != 0 ? winningStreak : -losingStreak))
.build(); .build();
log.debug("Setting response {}", response); log.debug("Setting response {}", response);
@@ -957,7 +957,7 @@ public class ChatCommandsPlugin extends Plugin
.append(ChatColorType.NORMAL) .append(ChatColorType.NORMAL)
.append("Barbarian Assault High-level gambles: ") .append("Barbarian Assault High-level gambles: ")
.append(ChatColorType.HIGHLIGHT) .append(ChatColorType.HIGHLIGHT)
.append(Integer.toString(gc)) .append(String.format("%,d", gc))
.build(); .build();
log.debug("Setting response {}", response); log.debug("Setting response {}", response);
@@ -1420,7 +1420,7 @@ public class ChatCommandsPlugin extends Plugin
.append(ChatColorType.NORMAL) .append(ChatColorType.NORMAL)
.append("Clue scroll (" + level + ")").append(": ") .append("Clue scroll (" + level + ")").append(": ")
.append(ChatColorType.HIGHLIGHT) .append(ChatColorType.HIGHLIGHT)
.append(Integer.toString(quantity)); .append(String.format("%,d", quantity));
if (rank != -1) if (rank != -1)
{ {

View File

@@ -56,8 +56,6 @@ import net.runelite.api.widgets.WidgetInfo;
import static net.runelite.api.widgets.WidgetInfo.TO_CHILD; 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.client.callback.ClientThread; import net.runelite.client.callback.ClientThread;
import net.runelite.client.chat.ChatMessageManager;
import net.runelite.client.chat.QueuedMessage;
import net.runelite.client.config.ConfigManager; import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.Subscribe; import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.input.KeyListener; import net.runelite.client.input.KeyListener;
@@ -82,7 +80,7 @@ public class ChatHistoryPlugin extends Plugin implements KeyListener
private static final int CYCLE_HOTKEY = KeyEvent.VK_TAB; private static final int CYCLE_HOTKEY = KeyEvent.VK_TAB;
private static final int FRIENDS_MAX_SIZE = 5; private static final int FRIENDS_MAX_SIZE = 5;
private Queue<QueuedMessage> messageQueue; private Queue<MessageNode> messageQueue;
private Deque<String> friends; private Deque<String> friends;
private String currentMessage = null; private String currentMessage = null;
@@ -99,9 +97,6 @@ public class ChatHistoryPlugin extends Plugin implements KeyListener
@Inject @Inject
private KeyManager keyManager; private KeyManager keyManager;
@Inject
private ChatMessageManager chatMessageManager;
@Provides @Provides
ChatHistoryConfig getConfig(ConfigManager configManager) ChatHistoryConfig getConfig(ConfigManager configManager)
{ {
@@ -111,6 +106,9 @@ public class ChatHistoryPlugin extends Plugin implements KeyListener
@Override @Override
protected void startUp() protected void startUp()
{ {
// The client reuses MessageNodes after 100 chat messages of
// the same type, so this must be 100 (or maybe a map of
// size 100 evicting queues)
messageQueue = EvictingQueue.create(100); messageQueue = EvictingQueue.create(100);
friends = new ArrayDeque<>(FRIENDS_MAX_SIZE + 1); friends = new ArrayDeque<>(FRIENDS_MAX_SIZE + 1);
keyManager.registerKeyListener(this); keyManager.registerKeyListener(this);
@@ -140,11 +138,16 @@ public class ChatHistoryPlugin extends Plugin implements KeyListener
return; return;
} }
QueuedMessage queuedMessage; for (MessageNode queuedMessage : messageQueue)
while ((queuedMessage = messageQueue.poll()) != null)
{ {
chatMessageManager.queue(queuedMessage); final MessageNode node = client.addChatMessage(
queuedMessage.getType(),
queuedMessage.getName(),
queuedMessage.getValue(),
queuedMessage.getSender(),
false);
node.setRuneLiteFormatMessage(queuedMessage.getRuneLiteFormatMessage());
node.setTimestamp(queuedMessage.getTimestamp());
} }
return; return;
@@ -171,19 +174,7 @@ public class ChatHistoryPlugin extends Plugin implements KeyListener
case MODCHAT: case MODCHAT:
case FRIENDSCHAT: case FRIENDSCHAT:
case CONSOLE: case CONSOLE:
final QueuedMessage queuedMessage = QueuedMessage.builder() messageQueue.offer(chatMessage.getMessageNode());
.type(chatMessageType)
.name(chatMessage.getName())
.sender(chatMessage.getSender())
.value(nbsp(chatMessage.getMessage()))
.runeLiteFormattedMessage(nbsp(chatMessage.getMessageNode().getRuneLiteFormatMessage()))
.timestamp(chatMessage.getTimestamp())
.build();
if (!messageQueue.contains(queuedMessage))
{
messageQueue.offer(queuedMessage);
}
} }
} }
@@ -348,21 +339,6 @@ public class ChatHistoryPlugin extends Plugin implements KeyListener
clearMessageQueue(tab); clearMessageQueue(tab);
} }
/**
* Small hack to prevent plugins checking for specific messages to match
* @param message message
* @return message with nbsp
*/
private static String nbsp(final String message)
{
if (message != null)
{
return message.replace(' ', '\u00A0');
}
return null;
}
@Override @Override
public void keyPressed(KeyEvent e) public void keyPressed(KeyEvent e)
{ {

View File

@@ -108,4 +108,15 @@ public interface ChatNotificationsConfig extends Config
{ {
return false; return false;
} }
@ConfigItem(
position = 7,
keyName = "notifyOnPM",
name = "Notify on private message",
description = "Notifies you whenever you receive a private message"
)
default boolean notifyOnPM()
{
return false;
}
} }

View File

@@ -165,6 +165,13 @@ public class ChatNotificationsPlugin extends Plugin
notifier.notify(Text.removeFormattingTags(broadcast)); notifier.notify(Text.removeFormattingTags(broadcast));
} }
break; break;
case PRIVATECHAT:
case MODPRIVATECHAT:
if (config.notifyOnPM())
{
notifier.notify(Text.removeTags(chatMessage.getName()) + ": " + chatMessage.getMessage());
}
break;
case CONSOLE: case CONSOLE:
// Don't notify for notification messages // Don't notify for notification messages
if (chatMessage.getName().equals(runeliteTitle)) if (chatMessage.getName().equals(runeliteTitle))

View File

@@ -96,7 +96,10 @@ import net.runelite.client.events.ConfigChanged;
import net.runelite.client.events.OverlayMenuClicked; import net.runelite.client.events.OverlayMenuClicked;
import net.runelite.client.game.ItemManager; import net.runelite.client.game.ItemManager;
import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDependency;
import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.plugins.banktags.BankTagsPlugin;
import net.runelite.client.plugins.banktags.TagManager;
import net.runelite.client.plugins.cluescrolls.clues.AnagramClue; import net.runelite.client.plugins.cluescrolls.clues.AnagramClue;
import net.runelite.client.plugins.cluescrolls.clues.BeginnerMapClue; import net.runelite.client.plugins.cluescrolls.clues.BeginnerMapClue;
import net.runelite.client.plugins.cluescrolls.clues.CipherClue; import net.runelite.client.plugins.cluescrolls.clues.CipherClue;
@@ -117,6 +120,7 @@ import net.runelite.client.plugins.cluescrolls.clues.ObjectClueScroll;
import net.runelite.client.plugins.cluescrolls.clues.SkillChallengeClue; import net.runelite.client.plugins.cluescrolls.clues.SkillChallengeClue;
import net.runelite.client.plugins.cluescrolls.clues.TextClueScroll; import net.runelite.client.plugins.cluescrolls.clues.TextClueScroll;
import net.runelite.client.plugins.cluescrolls.clues.ThreeStepCrypticClue; import net.runelite.client.plugins.cluescrolls.clues.ThreeStepCrypticClue;
import net.runelite.client.plugins.cluescrolls.clues.item.ItemRequirement;
import net.runelite.client.ui.overlay.OverlayManager; import net.runelite.client.ui.overlay.OverlayManager;
import net.runelite.client.ui.overlay.OverlayMenuEntry; import net.runelite.client.ui.overlay.OverlayMenuEntry;
import net.runelite.client.ui.overlay.OverlayUtil; import net.runelite.client.ui.overlay.OverlayUtil;
@@ -131,6 +135,7 @@ import org.apache.commons.lang3.ArrayUtils;
description = "Show answers to clue scroll riddles, anagrams, ciphers, and cryptic clues", description = "Show answers to clue scroll riddles, anagrams, ciphers, and cryptic clues",
tags = {"arrow", "hints", "world", "map", "coordinates", "emotes"} tags = {"arrow", "hints", "world", "map", "coordinates", "emotes"}
) )
@PluginDependency(BankTagsPlugin.class)
@Slf4j @Slf4j
public class ClueScrollPlugin extends Plugin public class ClueScrollPlugin extends Plugin
{ {
@@ -144,6 +149,7 @@ public class ClueScrollPlugin extends Plugin
13150, 9011, 13150, 9011,
13151, 9012 13151, 9012
}; };
private static final String CLUE_TAG_NAME = "clue";
@Getter @Getter
private ClueScroll clue; private ClueScroll clue;
@@ -191,6 +197,9 @@ public class ClueScrollPlugin extends Plugin
@Inject @Inject
private WorldMapPointManager worldMapPointManager; private WorldMapPointManager worldMapPointManager;
@Inject
private TagManager tagManager;
@Inject @Inject
@Named("developerMode") @Named("developerMode")
boolean developerMode; boolean developerMode;
@@ -227,11 +236,13 @@ public class ClueScrollPlugin extends Plugin
overlayManager.add(clueScrollEmoteOverlay); overlayManager.add(clueScrollEmoteOverlay);
overlayManager.add(clueScrollWorldOverlay); overlayManager.add(clueScrollWorldOverlay);
overlayManager.add(clueScrollMusicOverlay); overlayManager.add(clueScrollMusicOverlay);
tagManager.registerTag(CLUE_TAG_NAME, this::testClueTag);
} }
@Override @Override
protected void shutDown() throws Exception protected void shutDown() throws Exception
{ {
tagManager.unregisterTag(CLUE_TAG_NAME);
overlayManager.remove(clueScrollOverlay); overlayManager.remove(clueScrollOverlay);
overlayManager.remove(clueScrollEmoteOverlay); overlayManager.remove(clueScrollEmoteOverlay);
overlayManager.remove(clueScrollWorldOverlay); overlayManager.remove(clueScrollWorldOverlay);
@@ -484,6 +495,10 @@ public class ClueScrollPlugin extends Plugin
{ {
resetClue(true); resetClue(true);
} }
else if (state == GameState.HOPPING)
{
namedObjectCheckThisTick = true;
}
} }
@Subscribe @Subscribe
@@ -550,6 +565,7 @@ public class ClueScrollPlugin extends Plugin
} }
// Load the current plane's tiles if a tick has elapsed since the player has changed planes // Load the current plane's tiles if a tick has elapsed since the player has changed planes
// or upon reaching a logged in state after hopping worlds
if (namedObjectCheckThisTick) if (namedObjectCheckThisTick)
{ {
namedObjectCheckThisTick = false; namedObjectCheckThisTick = false;
@@ -569,7 +585,7 @@ public class ClueScrollPlugin extends Plugin
if (chatDialogClueItem != null if (chatDialogClueItem != null
&& (chatDialogClueItem.getItemId() == ItemID.CLUE_SCROLL_BEGINNER || chatDialogClueItem.getItemId() == ItemID.CLUE_SCROLL_MASTER)) && (chatDialogClueItem.getItemId() == ItemID.CLUE_SCROLL_BEGINNER || chatDialogClueItem.getItemId() == ItemID.CLUE_SCROLL_MASTER))
{ {
resetClue(true); resetClue(false);
} }
final Widget clueScrollText = client.getWidget(WidgetInfo.CLUE_SCROLL_TEXT); final Widget clueScrollText = client.getWidget(WidgetInfo.CLUE_SCROLL_TEXT);
@@ -1109,4 +1125,38 @@ public class ClueScrollPlugin extends Plugin
} }
return worldPoint; return worldPoint;
} }
private boolean testClueTag(int itemId)
{
ClueScroll c = clue;
if (c == null)
{
return false;
}
if (c instanceof EmoteClue)
{
EmoteClue emote = (EmoteClue) c;
for (ItemRequirement ir : emote.getItemRequirements())
{
if (ir.fulfilledBy(itemId))
{
return true;
}
}
}
else if (c instanceof CoordinateClue || c instanceof HotColdClue || c instanceof FairyRingClue)
{
return itemId == ItemID.SPADE;
}
else if (c instanceof MapClue)
{
MapClue mapClue = (MapClue) c;
return mapClue.getObjectId() == -1 && itemId == ItemID.SPADE;
}
return false;
}
} }

View File

@@ -205,6 +205,8 @@ public class CoordinateClue extends ClueScroll implements TextClueScroll, Locati
.put(new WorldPoint(2484, 4016, 0), new CoordinateClueInfo("Northeast corner of the Island of Stone.", ARMADYLIAN_OR_BANDOSIAN_GUARD)) .put(new WorldPoint(2484, 4016, 0), new CoordinateClueInfo("Northeast corner of the Island of Stone.", ARMADYLIAN_OR_BANDOSIAN_GUARD))
.put(new WorldPoint(2222, 3331, 0), new CoordinateClueInfo("Prifddinas, west of the Tower of Voices", ARMADYLIAN_OR_BANDOSIAN_GUARD)) .put(new WorldPoint(2222, 3331, 0), new CoordinateClueInfo("Prifddinas, west of the Tower of Voices", ARMADYLIAN_OR_BANDOSIAN_GUARD))
.put(new WorldPoint(3560, 3987, 0), new CoordinateClueInfo("Lithkren. Digsite pendant teleport if unlocked, otherwise take rowboat from west of Mushroom Meadow Mushtree.", ARMADYLIAN_OR_BANDOSIAN_GUARD)) .put(new WorldPoint(3560, 3987, 0), new CoordinateClueInfo("Lithkren. Digsite pendant teleport if unlocked, otherwise take rowboat from west of Mushroom Meadow Mushtree.", ARMADYLIAN_OR_BANDOSIAN_GUARD))
.put(new WorldPoint(2318, 2954, 0), new CoordinateClueInfo("North-east corner of the Isle of Souls.", ARMADYLIAN_OR_BANDOSIAN_GUARD))
.put(new WorldPoint(2094, 2889, 0), new CoordinateClueInfo("West side of the Isle of Souls.", ARMADYLIAN_OR_BANDOSIAN_GUARD))
// Master // Master
.put(new WorldPoint(2178, 3209, 0), new CoordinateClueInfo("South of Iorwerth Camp.", BRASSICAN_MAGE)) .put(new WorldPoint(2178, 3209, 0), new CoordinateClueInfo("South of Iorwerth Camp.", BRASSICAN_MAGE))
.put(new WorldPoint(2155, 3100, 0), new CoordinateClueInfo("South of Port Tyras (BJS if 76 Agility).", BRASSICAN_MAGE)) .put(new WorldPoint(2155, 3100, 0), new CoordinateClueInfo("South of Port Tyras (BJS if 76 Agility).", BRASSICAN_MAGE))
@@ -227,7 +229,7 @@ public class CoordinateClue extends ClueScroll implements TextClueScroll, Locati
.put(new WorldPoint(2202, 3825, 0), new CoordinateClueInfo("Pirates' Cove, between Lunar Isle and Rellekka.", ANCIENT_WIZARDS)) .put(new WorldPoint(2202, 3825, 0), new CoordinateClueInfo("Pirates' Cove, between Lunar Isle and Rellekka.", ANCIENT_WIZARDS))
.put(new WorldPoint(1761, 3853, 0), new CoordinateClueInfo("Arceuus essence mine (CIS).", BRASSICAN_MAGE)) .put(new WorldPoint(1761, 3853, 0), new CoordinateClueInfo("Arceuus essence mine (CIS).", BRASSICAN_MAGE))
.put(new WorldPoint(2090, 3863, 0), new CoordinateClueInfo("South of Lunar Isle, west of Astral altar.", ANCIENT_WIZARDS)) .put(new WorldPoint(2090, 3863, 0), new CoordinateClueInfo("South of Lunar Isle, west of Astral altar.", ANCIENT_WIZARDS))
.put(new WorldPoint(1442, 3878, 0), new CoordinateClueInfo("Sulphur Mine.", BRASSICAN_MAGE)) .put(new WorldPoint(1442, 3878, 0), new CoordinateClueInfo("Northern area of the Lovakengj Sulphur Mine. Facemask or Slayer Helmet recommended.", BRASSICAN_MAGE))
.put(new WorldPoint(3380, 3929, 0), new CoordinateClueInfo("Wilderness. Near Volcano.", ANCIENT_WIZARDS)) .put(new WorldPoint(3380, 3929, 0), new CoordinateClueInfo("Wilderness. Near Volcano.", ANCIENT_WIZARDS))
.put(new WorldPoint(3188, 3939, 0), new CoordinateClueInfo("Wilderness. Resource Area.", BRASSICAN_MAGE)) .put(new WorldPoint(3188, 3939, 0), new CoordinateClueInfo("Wilderness. Resource Area.", BRASSICAN_MAGE))
.put(new WorldPoint(3304, 3941, 0), new CoordinateClueInfo("Wilderness. East of Rogues' Castle.", ANCIENT_WIZARDS)) .put(new WorldPoint(3304, 3941, 0), new CoordinateClueInfo("Wilderness. East of Rogues' Castle.", ANCIENT_WIZARDS))

View File

@@ -96,6 +96,7 @@ public enum HotColdLocation
FREMENNIK_PROVINCE_LUNAR_VILLAGE(new WorldPoint(2084, 3916, 0), FREMENNIK_PROVINCE, "Lunar Isle, inside the village.", ANCIENT_WIZARDS), FREMENNIK_PROVINCE_LUNAR_VILLAGE(new WorldPoint(2084, 3916, 0), FREMENNIK_PROVINCE, "Lunar Isle, inside the village.", ANCIENT_WIZARDS),
FREMENNIK_PROVINCE_LUNAR_NORTH(new WorldPoint(2106, 3949, 0), FREMENNIK_PROVINCE, "Lunar Isle, north of the village.", ANCIENT_WIZARDS), FREMENNIK_PROVINCE_LUNAR_NORTH(new WorldPoint(2106, 3949, 0), FREMENNIK_PROVINCE, "Lunar Isle, north of the village.", ANCIENT_WIZARDS),
ICE_MOUNTAIN(new WorldPoint(3007, 3475, 0), MISTHALIN, "Atop Ice Mountain"), ICE_MOUNTAIN(new WorldPoint(3007, 3475, 0), MISTHALIN, "Atop Ice Mountain"),
ISLE_OF_SOULS_MINE(new WorldPoint(2189, 2794, 0), KANDARIN, "Isle of Souls Mine, south of the Soul Wars lobby"),
KANDARIN_SINCLAR_MANSION(new WorldPoint(2730, 3588, 0), KANDARIN, "North-west of the Sinclair Mansion, near the log balance shortcut.", BRASSICAN_MAGE), KANDARIN_SINCLAR_MANSION(new WorldPoint(2730, 3588, 0), KANDARIN, "North-west of the Sinclair Mansion, near the log balance shortcut.", BRASSICAN_MAGE),
KANDARIN_CATHERBY(new WorldPoint(2774, 3436, 0), KANDARIN, "Catherby, between the bank and the beehives, near small rock formation.", BRASSICAN_MAGE), KANDARIN_CATHERBY(new WorldPoint(2774, 3436, 0), KANDARIN, "Catherby, between the bank and the beehives, near small rock formation.", BRASSICAN_MAGE),
KANDARIN_GRAND_TREE(new WorldPoint(2448, 3503, 0), KANDARIN, "Grand Tree, just east of the terrorchick gnome enclosure.", BRASSICAN_MAGE), KANDARIN_GRAND_TREE(new WorldPoint(2448, 3503, 0), KANDARIN, "Grand Tree, just east of the terrorchick gnome enclosure.", BRASSICAN_MAGE),
@@ -164,7 +165,7 @@ public enum HotColdLocation
ZEAH_BLASTMINE_NORTH(new WorldPoint(1488, 3881, 0), ZEAH, "Northern part of the Lovakengj blast mine.", BRASSICAN_MAGE), ZEAH_BLASTMINE_NORTH(new WorldPoint(1488, 3881, 0), ZEAH, "Northern part of the Lovakengj blast mine.", BRASSICAN_MAGE),
ZEAH_LOVAKITE_FURNACE(new WorldPoint(1507, 3819, 0), ZEAH, "Next to the lovakite furnace in Lovakengj.", ANCIENT_WIZARDS), ZEAH_LOVAKITE_FURNACE(new WorldPoint(1507, 3819, 0), ZEAH, "Next to the lovakite furnace in Lovakengj.", ANCIENT_WIZARDS),
ZEAH_LOVAKENGJ_MINE(new WorldPoint(1477, 3778, 0), ZEAH, "Next to mithril rock in the Lovakengj mine.", ANCIENT_WIZARDS), ZEAH_LOVAKENGJ_MINE(new WorldPoint(1477, 3778, 0), ZEAH, "Next to mithril rock in the Lovakengj mine.", ANCIENT_WIZARDS),
ZEAH_SULPHR_MINE(new WorldPoint(1428, 3869, 0), ZEAH, "Western entrance in the Lovakengj sulphur mine.", BRASSICAN_MAGE), ZEAH_SULPHR_MINE(new WorldPoint(1428, 3869, 0), ZEAH, "Western entrance in the Lovakengj sulphur mine. Facemask or Slayer Helmet recommended.", BRASSICAN_MAGE),
ZEAH_SHAYZIEN_BANK(new WorldPoint(1517, 3603, 0), ZEAH, "South-east of the bank in Shayzien.", ANCIENT_WIZARDS), ZEAH_SHAYZIEN_BANK(new WorldPoint(1517, 3603, 0), ZEAH, "South-east of the bank in Shayzien.", ANCIENT_WIZARDS),
ZEAH_OVERPASS(new WorldPoint(1467, 3714, 0), ZEAH, "Overpass between Lovakengj and Shayzien.", BRASSICAN_MAGE), ZEAH_OVERPASS(new WorldPoint(1467, 3714, 0), ZEAH, "Overpass between Lovakengj and Shayzien.", BRASSICAN_MAGE),
ZEAH_LIZARDMAN(new WorldPoint(1490, 3698, 0), ZEAH, "Within Lizardman Canyon, east of the ladder. Requires 5% favour with Shayzien.", ANCIENT_WIZARDS), ZEAH_LIZARDMAN(new WorldPoint(1490, 3698, 0), ZEAH, "Within Lizardman Canyon, east of the ladder. Requires 5% favour with Shayzien.", ANCIENT_WIZARDS),

View File

@@ -43,8 +43,6 @@ import net.runelite.api.Constants;
import net.runelite.api.DecorativeObject; import net.runelite.api.DecorativeObject;
import net.runelite.api.GameObject; import net.runelite.api.GameObject;
import net.runelite.api.GraphicsObject; import net.runelite.api.GraphicsObject;
import net.runelite.api.TileItem;
import net.runelite.api.GroundObject;
import net.runelite.api.ItemLayer; import net.runelite.api.ItemLayer;
import net.runelite.api.NPC; import net.runelite.api.NPC;
import net.runelite.api.NPCComposition; import net.runelite.api.NPCComposition;
@@ -55,7 +53,8 @@ import net.runelite.api.Point;
import net.runelite.api.Projectile; import net.runelite.api.Projectile;
import net.runelite.api.Scene; import net.runelite.api.Scene;
import net.runelite.api.Tile; import net.runelite.api.Tile;
import net.runelite.api.WallObject; import net.runelite.api.TileItem;
import net.runelite.api.TileObject;
import net.runelite.api.coords.LocalPoint; import net.runelite.api.coords.LocalPoint;
import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetInfo; import net.runelite.api.widgets.WidgetInfo;
@@ -154,7 +153,6 @@ class DevToolsOverlay extends Overlay
String text = local.getName() + " (A: " + local.getAnimation() + ") (P: " + local.getPoseAnimation() + ") (G: " + local.getGraphic() + ")"; String text = local.getName() + " (A: " + local.getAnimation() + ") (P: " + local.getPoseAnimation() + ") (G: " + local.getGraphic() + ")";
OverlayUtil.renderActorOverlay(graphics, local, text, CYAN); OverlayUtil.renderActorOverlay(graphics, local, text, CYAN);
renderPlayerWireframe(graphics, local, CYAN);
} }
private void renderNpcs(Graphics2D graphics) private void renderNpcs(Graphics2D graphics)
@@ -214,7 +212,7 @@ class DevToolsOverlay extends Overlay
if (plugin.getGroundObjects().isActive()) if (plugin.getGroundObjects().isActive())
{ {
renderGroundObject(graphics, tile, player); renderTileObject(graphics, tile.getGroundObject(), player, PURPLE);
} }
if (plugin.getGameObjects().isActive()) if (plugin.getGameObjects().isActive())
@@ -224,7 +222,7 @@ class DevToolsOverlay extends Overlay
if (plugin.getWalls().isActive()) if (plugin.getWalls().isActive())
{ {
renderWallObject(graphics, tile, player); renderTileObject(graphics, tile.getWallObject(), player, GRAY);
} }
if (plugin.getDecorations().isActive()) if (plugin.getDecorations().isActive())
@@ -309,45 +307,21 @@ class DevToolsOverlay extends Overlay
{ {
for (GameObject gameObject : gameObjects) for (GameObject gameObject : gameObjects)
{ {
if (gameObject != null) if (gameObject != null && gameObject.getSceneMinLocation().equals(tile.getSceneLocation()))
{ {
if (player.getLocalLocation().distanceTo(gameObject.getLocalLocation()) <= MAX_DISTANCE) renderTileObject(graphics, gameObject, player, GREEN);
{
OverlayUtil.renderTileOverlay(graphics, gameObject, "ID: " + gameObject.getId(), GREEN);
}
// Draw a polygon around the convex hull
// of the model vertices
Shape p = gameObject.getConvexHull();
if (p != null)
{
graphics.draw(p);
}
} }
} }
} }
} }
private void renderGroundObject(Graphics2D graphics, Tile tile, Player player) private void renderTileObject(Graphics2D graphics, TileObject tileObject, Player player, Color color)
{ {
GroundObject groundObject = tile.getGroundObject(); if (tileObject != null)
if (groundObject != null)
{ {
if (player.getLocalLocation().distanceTo(groundObject.getLocalLocation()) <= MAX_DISTANCE) if (player.getLocalLocation().distanceTo(tileObject.getLocalLocation()) <= MAX_DISTANCE)
{ {
OverlayUtil.renderTileOverlay(graphics, groundObject, "ID: " + groundObject.getId(), PURPLE); OverlayUtil.renderTileOverlay(graphics, tileObject, "ID: " + tileObject.getId(), color);
}
}
}
private void renderWallObject(Graphics2D graphics, Tile tile, Player player)
{
WallObject wallObject = tile.getWallObject();
if (wallObject != null)
{
if (player.getLocalLocation().distanceTo(wallObject.getLocalLocation()) <= MAX_DISTANCE)
{
OverlayUtil.renderTileOverlay(graphics, wallObject, "ID: " + wallObject.getId(), GRAY);
} }
} }
} }
@@ -447,22 +421,4 @@ class DevToolsOverlay extends Overlay
} }
} }
} }
private void renderPlayerWireframe(Graphics2D graphics, Player player, Color color)
{
Polygon[] polys = player.getPolygons();
if (polys == null)
{
return;
}
graphics.setColor(color);
for (Polygon p : polys)
{
graphics.drawPolygon(p);
}
}
} }

View File

@@ -26,16 +26,12 @@
*/ */
package net.runelite.client.plugins.discord; package net.runelite.client.plugins.discord;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import net.runelite.api.Client;
import net.runelite.api.Skill; import net.runelite.api.Skill;
import net.runelite.api.Varbits;
@AllArgsConstructor @AllArgsConstructor
@Getter @Getter
@@ -187,6 +183,7 @@ enum DiscordGameEventType
DUNGEON_CRABCLAW_CAVES("Crabclaw Caves", DiscordAreaType.DUNGEONS, 6553, 6809), DUNGEON_CRABCLAW_CAVES("Crabclaw Caves", DiscordAreaType.DUNGEONS, 6553, 6809),
DUNGEON_CRANDOR("Crandor Dungeon", DiscordAreaType.DUNGEONS, 11414), DUNGEON_CRANDOR("Crandor Dungeon", DiscordAreaType.DUNGEONS, 11414),
DUNGEON_CRASH_SITE_CAVERN("Crash Site Cavern", DiscordAreaType.DUNGEONS, 8280, 8536), DUNGEON_CRASH_SITE_CAVERN("Crash Site Cavern", DiscordAreaType.DUNGEONS, 8280, 8536),
DUNGEON_CRUMBLING_TOWER("Crumbling Tower", DiscordAreaType.DUNGEONS, 7827),
DUNGEON_DAEYALT_ESSENCE_MINE("Daeyalt Essence Mine", DiscordAreaType.DUNGEONS, 14744), DUNGEON_DAEYALT_ESSENCE_MINE("Daeyalt Essence Mine", DiscordAreaType.DUNGEONS, 14744),
DUNGEON_DIGSITE("Digsite Dungeon", DiscordAreaType.DUNGEONS, 13464, 13465), DUNGEON_DIGSITE("Digsite Dungeon", DiscordAreaType.DUNGEONS, 13464, 13465),
DUNGEON_DORGESHKAAN("Dorgesh-Kaan South Dungeon", DiscordAreaType.DUNGEONS, 10833), DUNGEON_DORGESHKAAN("Dorgesh-Kaan South Dungeon", DiscordAreaType.DUNGEONS, 10833),
@@ -210,6 +207,7 @@ enum DiscordGameEventType
DUNGEON_HAM_STORE_ROOM("H.A.M. Store room", DiscordAreaType.DUNGEONS, 10321), DUNGEON_HAM_STORE_ROOM("H.A.M. Store room", DiscordAreaType.DUNGEONS, 10321),
DUNGEON_HEROES_GUILD("Heroes' Guild Mine", DiscordAreaType.DUNGEONS, 11674), DUNGEON_HEROES_GUILD("Heroes' Guild Mine", DiscordAreaType.DUNGEONS, 11674),
DUNGEON_IORWERTH("Iorwerth Dungeon", DiscordAreaType.DUNGEONS, 12737, 12738, 12993, 12994), DUNGEON_IORWERTH("Iorwerth Dungeon", DiscordAreaType.DUNGEONS, 12737, 12738, 12993, 12994),
DUNGEON_ISLE_OF_SOULS("Isle of Souls Dungeon", DiscordAreaType.DUNGEONS, 8593),
DUNGEON_JATIZSO_MINES("Jatizso Mines", DiscordAreaType.DUNGEONS, 9631), DUNGEON_JATIZSO_MINES("Jatizso Mines", DiscordAreaType.DUNGEONS, 9631),
DUNGEON_JIGGIG_BURIAL_TOMB("Jiggig Burial Tomb", DiscordAreaType.DUNGEONS, 9875, 9874), DUNGEON_JIGGIG_BURIAL_TOMB("Jiggig Burial Tomb", DiscordAreaType.DUNGEONS, 9875, 9874),
DUNGEON_JOGRE("Jogre Dungeon", DiscordAreaType.DUNGEONS, 11412), DUNGEON_JOGRE("Jogre Dungeon", DiscordAreaType.DUNGEONS, 11412),
@@ -307,8 +305,8 @@ enum DiscordGameEventType
MG_VOLCANIC_MINE("Volcanic Mine", DiscordAreaType.MINIGAMES, 15263, 15262), MG_VOLCANIC_MINE("Volcanic Mine", DiscordAreaType.MINIGAMES, 15263, 15262),
// Raids // Raids
RAIDS_CHAMBERS_OF_XERIC("Chambers of Xeric", DiscordAreaType.RAIDS, Varbits.IN_RAID), RAIDS_CHAMBERS_OF_XERIC("Chambers of Xeric", DiscordAreaType.RAIDS, 12889, 13136, 13137, 13138, 13139, 13140, 13141, 13145, 13393, 13394, 13395, 13396, 13397, 13401),
RAIDS_THEATRE_OF_BLOOD("Theatre of Blood", DiscordAreaType.RAIDS, Varbits.THEATRE_OF_BLOOD), RAIDS_THEATRE_OF_BLOOD("Theatre of Blood", DiscordAreaType.RAIDS, 12611, 12612, 12613, 12867, 12869, 13122, 13123, 13125, 13379),
// Other // Other
REGION_ABYSSAL_AREA("Abyssal Area", DiscordAreaType.REGIONS, 12108), REGION_ABYSSAL_AREA("Abyssal Area", DiscordAreaType.REGIONS, 12108),
@@ -367,6 +365,7 @@ enum DiscordGameEventType
REGION_ICYENE_GRAVEYARD("Icyene Graveyard", DiscordAreaType.REGIONS, 14641, 14897, 14898), REGION_ICYENE_GRAVEYARD("Icyene Graveyard", DiscordAreaType.REGIONS, 14641, 14897, 14898),
REGION_ISAFDAR("Isafdar", DiscordAreaType.REGIONS, 8497, 8753, 8754, 9009, 9010), REGION_ISAFDAR("Isafdar", DiscordAreaType.REGIONS, 8497, 8753, 8754, 9009, 9010),
REGION_ISLAND_OF_STONE("Island of Stone", DiscordAreaType.REGIONS, 9790), REGION_ISLAND_OF_STONE("Island of Stone", DiscordAreaType.REGIONS, 9790),
REGION_ISLE_OF_SOULS("Isle of Souls", DiscordAreaType.REGIONS, 8236, 8237, 8238, 8491, 8492, 8494, 8747, 8750, 9003, 9004, 9006, 9260, 9261, 9262),
REGION_JIGGIG("Jiggig" , DiscordAreaType.REGIONS, 9775), REGION_JIGGIG("Jiggig" , DiscordAreaType.REGIONS, 9775),
REGION_KANDARIN("Kandarin", DiscordAreaType.REGIONS, 9014, 9263, 9264, 9519, 9524, 9527, 9776, 9783, 10037, 10290, 10294, 10546, 10551, 10805), REGION_KANDARIN("Kandarin", DiscordAreaType.REGIONS, 9014, 9263, 9264, 9519, 9524, 9527, 9776, 9783, 10037, 10290, 10294, 10546, 10551, 10805),
REGION_KARAMJA("Karamja" , DiscordAreaType.REGIONS, 10801, 10802, 11054, 11311, 11312, 11313, 11566, 11567, 11568, 11569, 11822), REGION_KARAMJA("Karamja" , DiscordAreaType.REGIONS, 10801, 10802, 11054, 11311, 11312, 11313, 11566, 11567, 11568, 11569, 11822),
@@ -430,20 +429,12 @@ enum DiscordGameEventType
REGION_WRATH_ALTAR("Wrath Altar", DiscordAreaType.REGIONS, 9291); REGION_WRATH_ALTAR("Wrath Altar", DiscordAreaType.REGIONS, 9291);
private static final Map<Integer, DiscordGameEventType> FROM_REGION; private static final Map<Integer, DiscordGameEventType> FROM_REGION;
private static final List<DiscordGameEventType> FROM_VARBITS;
static static
{ {
ImmutableMap.Builder<Integer, DiscordGameEventType> regionMapBuilder = new ImmutableMap.Builder<>(); ImmutableMap.Builder<Integer, DiscordGameEventType> regionMapBuilder = new ImmutableMap.Builder<>();
ImmutableList.Builder<DiscordGameEventType> fromVarbitsBuilder = ImmutableList.builder();
for (DiscordGameEventType discordGameEventType : DiscordGameEventType.values()) for (DiscordGameEventType discordGameEventType : DiscordGameEventType.values())
{ {
if (discordGameEventType.getVarbits() != null)
{
fromVarbitsBuilder.add(discordGameEventType);
continue;
}
if (discordGameEventType.getRegionIds() == null) if (discordGameEventType.getRegionIds() == null)
{ {
continue; continue;
@@ -455,7 +446,6 @@ enum DiscordGameEventType
} }
} }
FROM_REGION = regionMapBuilder.build(); FROM_REGION = regionMapBuilder.build();
FROM_VARBITS = fromVarbitsBuilder.build();
} }
@Nullable @Nullable
@@ -497,9 +487,6 @@ enum DiscordGameEventType
@Nullable @Nullable
private DiscordAreaType discordAreaType; private DiscordAreaType discordAreaType;
@Nullable
private Varbits varbits;
@Nullable @Nullable
private int[] regionIds; private int[] regionIds;
@@ -541,15 +528,6 @@ enum DiscordGameEventType
this(state, priority, true, false, false, true, false); this(state, priority, true, false, false, true, false);
} }
DiscordGameEventType(String areaName, DiscordAreaType areaType, Varbits varbits)
{
this.state = exploring(areaType, areaName);
this.priority = -2;
this.discordAreaType = areaType;
this.varbits = varbits;
this.shouldClear = true;
}
private static String training(final Skill skill) private static String training(final Skill skill)
{ {
return training(skill.getName()); return training(skill.getName());
@@ -609,17 +587,4 @@ enum DiscordGameEventType
{ {
return FROM_REGION.get(regionId); return FROM_REGION.get(regionId);
} }
public static DiscordGameEventType fromVarbit(final Client client)
{
for (DiscordGameEventType fromVarbit : FROM_VARBITS)
{
if (client.getVar(fromVarbit.getVarbits()) != 0)
{
return fromVarbit;
}
}
return null;
}
} }

View File

@@ -46,7 +46,6 @@ import net.runelite.api.WorldType;
import net.runelite.api.coords.WorldPoint; import net.runelite.api.coords.WorldPoint;
import net.runelite.api.events.GameStateChanged; import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.StatChanged; import net.runelite.api.events.StatChanged;
import net.runelite.api.events.VarbitChanged;
import net.runelite.client.config.ConfigManager; import net.runelite.client.config.ConfigManager;
import net.runelite.client.discord.DiscordService; import net.runelite.client.discord.DiscordService;
import net.runelite.client.discord.events.DiscordJoinGame; import net.runelite.client.discord.events.DiscordJoinGame;
@@ -210,22 +209,6 @@ public class DiscordPlugin extends Plugin
} }
} }
@Subscribe
public void onVarbitChanged(VarbitChanged event)
{
if (!config.showRaidingActivity())
{
return;
}
final DiscordGameEventType discordGameEventType = DiscordGameEventType.fromVarbit(client);
if (discordGameEventType != null)
{
discordState.triggerEvent(discordGameEventType);
}
}
@Subscribe @Subscribe
public void onDiscordReady(DiscordReady event) public void onDiscordReady(DiscordReady event)
{ {
@@ -447,6 +430,7 @@ public class DiscordPlugin extends Plugin
case DUNGEONS: return config.showDungeonActivity(); case DUNGEONS: return config.showDungeonActivity();
case MINIGAMES: return config.showMinigameActivity(); case MINIGAMES: return config.showMinigameActivity();
case REGIONS: return config.showRegionsActivity(); case REGIONS: return config.showRegionsActivity();
case RAIDS: return config.showRaidingActivity();
} }
return false; return false;

View File

@@ -91,6 +91,8 @@ enum Emoji
GORILLA(":G"), GORILLA(":G"),
PLEADING("(n_n)"), PLEADING("(n_n)"),
XD("Xd"), XD("Xd"),
SPOON("--o"),
WEARY_FACE("Dx"),
; ;
private static final Map<String, Emoji> emojiMap; private static final Map<String, Emoji> emojiMap;

View File

@@ -40,7 +40,6 @@ import net.runelite.api.events.ChatMessage;
import net.runelite.api.events.GameStateChanged; import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.OverheadTextChanged; import net.runelite.api.events.OverheadTextChanged;
import net.runelite.client.callback.ClientThread; import net.runelite.client.callback.ClientThread;
import net.runelite.client.chat.ChatMessageManager;
import net.runelite.client.eventbus.Subscribe; import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.plugins.PluginDescriptor;
@@ -63,9 +62,6 @@ public class EmojiPlugin extends Plugin
@Inject @Inject
private ClientThread clientThread; private ClientThread clientThread;
@Inject
private ChatMessageManager chatMessageManager;
private int modIconsStart = -1; private int modIconsStart = -1;
@Override @Override
@@ -145,9 +141,7 @@ public class EmojiPlugin extends Plugin
return; return;
} }
messageNode.setRuneLiteFormatMessage(updatedMessage); messageNode.setValue(updatedMessage);
chatMessageManager.update(messageNode);
client.refreshChat();
} }
@Subscribe @Subscribe

View File

@@ -29,16 +29,18 @@ import net.runelite.client.config.Config;
import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigGroup;
import net.runelite.client.config.ConfigItem; import net.runelite.client.config.ConfigItem;
@ConfigGroup("entityhider") @ConfigGroup(EntityHiderConfig.GROUP)
public interface EntityHiderConfig extends Config public interface EntityHiderConfig extends Config
{ {
String GROUP = "entityhider";
@ConfigItem( @ConfigItem(
position = 1, position = 1,
keyName = "hidePlayers", keyName = "hidePlayers",
name = "Hide Players", name = "Hide Others",
description = "Configures whether or not players are hidden" description = "Configures whether or not other players are hidden"
) )
default boolean hidePlayers() default boolean hideOthers()
{ {
return true; return true;
} }
@@ -46,10 +48,10 @@ public interface EntityHiderConfig extends Config
@ConfigItem( @ConfigItem(
position = 2, position = 2,
keyName = "hidePlayers2D", keyName = "hidePlayers2D",
name = "Hide Players 2D", name = "Hide Others 2D",
description = "Configures whether or not players 2D elements are hidden" description = "Configures whether or not other players 2D elements are hidden"
) )
default boolean hidePlayers2D() default boolean hideOthers2D()
{ {
return true; return true;
} }
@@ -78,6 +80,17 @@ public interface EntityHiderConfig extends Config
@ConfigItem( @ConfigItem(
position = 5, position = 5,
keyName = "hideIgnores",
name = "Hide Ignores",
description = "Configures whether or not ignored players are hidden"
)
default boolean hideIgnores()
{
return false;
}
@ConfigItem(
position = 6,
keyName = "hideLocalPlayer", keyName = "hideLocalPlayer",
name = "Hide Local Player", name = "Hide Local Player",
description = "Configures whether or not the local player is hidden" description = "Configures whether or not the local player is hidden"
@@ -88,7 +101,7 @@ public interface EntityHiderConfig extends Config
} }
@ConfigItem( @ConfigItem(
position = 6, position = 7,
keyName = "hideLocalPlayer2D", keyName = "hideLocalPlayer2D",
name = "Hide Local Player 2D", name = "Hide Local Player 2D",
description = "Configures whether or not the local player's 2D elements are hidden" description = "Configures whether or not the local player's 2D elements are hidden"
@@ -99,7 +112,7 @@ public interface EntityHiderConfig extends Config
} }
@ConfigItem( @ConfigItem(
position = 7, position = 8,
keyName = "hideNPCs", keyName = "hideNPCs",
name = "Hide NPCs", name = "Hide NPCs",
description = "Configures whether or not NPCs are hidden" description = "Configures whether or not NPCs are hidden"
@@ -110,7 +123,7 @@ public interface EntityHiderConfig extends Config
} }
@ConfigItem( @ConfigItem(
position = 8, position = 9,
keyName = "hideNPCs2D", keyName = "hideNPCs2D",
name = "Hide NPCs 2D", name = "Hide NPCs 2D",
description = "Configures whether or not NPCs 2D elements are hidden" description = "Configures whether or not NPCs 2D elements are hidden"
@@ -121,7 +134,7 @@ public interface EntityHiderConfig extends Config
} }
@ConfigItem( @ConfigItem(
position = 9, position = 10,
keyName = "hidePets", keyName = "hidePets",
name = "Hide Pets", name = "Hide Pets",
description = "Configures whether or not other player pets are hidden" description = "Configures whether or not other player pets are hidden"
@@ -132,7 +145,7 @@ public interface EntityHiderConfig extends Config
} }
@ConfigItem( @ConfigItem(
position = 10, position = 11,
keyName = "hideAttackers", keyName = "hideAttackers",
name = "Hide Attackers", name = "Hide Attackers",
description = "Configures whether or not NPCs/players attacking you are hidden" description = "Configures whether or not NPCs/players attacking you are hidden"
@@ -143,7 +156,7 @@ public interface EntityHiderConfig extends Config
} }
@ConfigItem( @ConfigItem(
position = 11, position = 12,
keyName = "hideProjectiles", keyName = "hideProjectiles",
name = "Hide Projectiles", name = "Hide Projectiles",
description = "Configures whether or not projectiles are hidden" description = "Configures whether or not projectiles are hidden"

View File

@@ -28,13 +28,9 @@ package net.runelite.client.plugins.entityhider;
import com.google.inject.Provides; import com.google.inject.Provides;
import javax.inject.Inject; import javax.inject.Inject;
import net.runelite.api.Client; import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.Player;
import net.runelite.api.coords.WorldPoint;
import net.runelite.client.events.ConfigChanged;
import net.runelite.api.events.GameStateChanged;
import net.runelite.client.config.ConfigManager; import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.Subscribe; import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.events.ConfigChanged;
import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.plugins.PluginDescriptor;
@@ -67,27 +63,22 @@ public class EntityHiderPlugin extends Plugin
@Subscribe @Subscribe
public void onConfigChanged(ConfigChanged e) public void onConfigChanged(ConfigChanged e)
{ {
updateConfig(); if (e.getGroup().equals(EntityHiderConfig.GROUP))
}
@Subscribe
public void onGameStateChanged(GameStateChanged event)
{
if (event.getGameState() == GameState.LOGGED_IN)
{ {
client.setIsHidingEntities(isPlayerRegionAllowed()); updateConfig();
} }
} }
private void updateConfig() private void updateConfig()
{ {
client.setIsHidingEntities(isPlayerRegionAllowed()); client.setIsHidingEntities(true);
client.setPlayersHidden(config.hidePlayers()); client.setOthersHidden(config.hideOthers());
client.setPlayersHidden2D(config.hidePlayers2D()); client.setOthersHidden2D(config.hideOthers2D());
client.setFriendsHidden(config.hideFriends()); client.setFriendsHidden(config.hideFriends());
client.setFriendsChatMembersHidden(config.hideFriendsChatMembers()); client.setFriendsChatMembersHidden(config.hideFriendsChatMembers());
client.setIgnoresHidden(config.hideIgnores());
client.setLocalPlayerHidden(config.hideLocalPlayer()); client.setLocalPlayerHidden(config.hideLocalPlayer());
client.setLocalPlayerHidden2D(config.hideLocalPlayer2D()); client.setLocalPlayerHidden2D(config.hideLocalPlayer2D());
@@ -107,11 +98,12 @@ public class EntityHiderPlugin extends Plugin
{ {
client.setIsHidingEntities(false); client.setIsHidingEntities(false);
client.setPlayersHidden(false); client.setOthersHidden(false);
client.setPlayersHidden2D(false); client.setOthersHidden2D(false);
client.setFriendsHidden(false); client.setFriendsHidden(false);
client.setFriendsChatMembersHidden(false); client.setFriendsChatMembersHidden(false);
client.setIgnoresHidden(false);
client.setLocalPlayerHidden(false); client.setLocalPlayerHidden(false);
client.setLocalPlayerHidden2D(false); client.setLocalPlayerHidden2D(false);
@@ -125,19 +117,4 @@ public class EntityHiderPlugin extends Plugin
client.setProjectilesHidden(false); client.setProjectilesHidden(false);
} }
private boolean isPlayerRegionAllowed()
{
final Player localPlayer = client.getLocalPlayer();
if (localPlayer == null)
{
return true;
}
final int playerRegionID = WorldPoint.fromLocalInstance(client, localPlayer.getLocalLocation()).getRegionID();
// 9520 = Castle Wars
return playerRegionID != 9520;
}
} }

View File

@@ -150,6 +150,17 @@ public interface FishingConfig extends Config
@ConfigItem( @ConfigItem(
position = 10, position = 10,
keyName = "flyingFishNotification",
name = "Flying fish notification",
description = "Send a notification when a flying fish spawns on your fishing spot."
)
default boolean flyingFishNotification()
{
return true;
}
@ConfigItem(
position = 11,
keyName = "trawlerNotification", keyName = "trawlerNotification",
name = "Trawler activity notification", name = "Trawler activity notification",
description = "Send a notification when fishing trawler activity drops below 15%." description = "Send a notification when fishing trawler activity drops below 15%."
@@ -160,7 +171,7 @@ public interface FishingConfig extends Config
} }
@ConfigItem( @ConfigItem(
position = 11, position = 12,
keyName = "trawlerTimer", keyName = "trawlerTimer",
name = "Trawler timer in MM:SS", name = "Trawler timer in MM:SS",
description = "Trawler Timer will display a more accurate timer in MM:SS format." description = "Trawler Timer will display a more accurate timer in MM:SS format."

View File

@@ -215,6 +215,11 @@ public class FishingPlugin extends Plugin
spotOverlay.setHidden(false); spotOverlay.setHidden(false);
fishingSpotMinimapOverlay.setHidden(false); fishingSpotMinimapOverlay.setHidden(false);
} }
if (event.getMessage().equals("A flying fish jumps up and eats some of your minnows!") && config.flyingFishNotification())
{
notifier.notify("A flying fish is eating your minnows!");
}
} }
@Subscribe @Subscribe
@@ -372,7 +377,7 @@ public class FishingPlugin extends Plugin
{ {
if (!trawlerNotificationSent) if (!trawlerNotificationSent)
{ {
notifier.notify("[" + client.getLocalPlayer().getName() + "] has low Fishing Trawler activity!"); notifier.notify("You have low Fishing Trawler activity!");
trawlerNotificationSent = true; trawlerNotificationSent = true;
} }
} }

View File

@@ -411,11 +411,7 @@ public class FriendsChatPlugin extends Plugin
.append(textColor, member.getName() + activityMessage); .append(textColor, member.getName() + activityMessage);
final String messageString = message.build(); final String messageString = message.build();
client.addChatMessage(ChatMessageType.FRIENDSCHATNOTIFICATION, "", messageString, ""); final MessageNode line = client.addChatMessage(ChatMessageType.FRIENDSCHATNOTIFICATION, "", messageString, "");
final ChatLineBuffer chatLineBuffer = client.getChatLineMap().get(ChatMessageType.FRIENDSCHATNOTIFICATION.getType());
final MessageNode[] lines = chatLineBuffer.getLines();
final MessageNode line = lines[0];
MemberJoinMessage joinMessage = new MemberJoinMessage(line, line.getId(), client.getTickCount()); MemberJoinMessage joinMessage = new MemberJoinMessage(line, line.getId(), client.getTickCount());
joinMessages.addLast(joinMessage); joinMessages.addLast(joinMessage);

View File

@@ -0,0 +1,40 @@
/*
* Copyright (c) 2021, Adam <Adam@sigterm.info>
* 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.plugins.gpu;
import org.jocl.Pointer;
import org.jocl.cl_mem;
class GLBuffer
{
int glBufferId = -1;
int size = -1;
cl_mem cl_mem;
Pointer ptr()
{
return cl_mem != null ? Pointer.to(cl_mem) : null;
}
}

View File

@@ -29,6 +29,11 @@ import com.google.inject.Provides;
import com.jogamp.nativewindow.awt.AWTGraphicsConfiguration; import com.jogamp.nativewindow.awt.AWTGraphicsConfiguration;
import com.jogamp.nativewindow.awt.JAWTWindow; import com.jogamp.nativewindow.awt.JAWTWindow;
import com.jogamp.opengl.GL; import com.jogamp.opengl.GL;
import static com.jogamp.opengl.GL.GL_ARRAY_BUFFER;
import static com.jogamp.opengl.GL.GL_DYNAMIC_DRAW;
import static com.jogamp.opengl.GL2ES2.GL_STREAM_DRAW;
import static com.jogamp.opengl.GL2ES3.GL_STATIC_COPY;
import static com.jogamp.opengl.GL2ES3.GL_UNIFORM_BUFFER;
import com.jogamp.opengl.GL4; import com.jogamp.opengl.GL4;
import com.jogamp.opengl.GLCapabilities; import com.jogamp.opengl.GLCapabilities;
import com.jogamp.opengl.GLContext; import com.jogamp.opengl.GLContext;
@@ -45,6 +50,7 @@ import java.awt.Image;
import java.awt.geom.AffineTransform; import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt; import java.awt.image.DataBufferInt;
import java.nio.Buffer;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.nio.FloatBuffer; import java.nio.FloatBuffer;
@@ -93,6 +99,10 @@ import net.runelite.client.plugins.gpu.config.UIScalingMode;
import net.runelite.client.plugins.gpu.template.Template; import net.runelite.client.plugins.gpu.template.Template;
import net.runelite.client.ui.DrawManager; import net.runelite.client.ui.DrawManager;
import net.runelite.client.util.OSType; import net.runelite.client.util.OSType;
import org.jocl.CL;
import static org.jocl.CL.CL_MEM_READ_ONLY;
import static org.jocl.CL.CL_MEM_WRITE_ONLY;
import static org.jocl.CL.clCreateFromGLBuffer;
@PluginDescriptor( @PluginDescriptor(
name = "GPU", name = "GPU",
@@ -105,8 +115,8 @@ import net.runelite.client.util.OSType;
public class GpuPlugin extends Plugin implements DrawCallbacks public class GpuPlugin extends Plugin implements DrawCallbacks
{ {
// This is the maximum number of triangles the compute shaders support // This is the maximum number of triangles the compute shaders support
private static final int MAX_TRIANGLE = 4096; static final int MAX_TRIANGLE = 4096;
private static final int SMALL_TRIANGLE_COUNT = 512; static final int SMALL_TRIANGLE_COUNT = 512;
private static final int FLAG_SCENE_BUFFER = Integer.MIN_VALUE; private static final int FLAG_SCENE_BUFFER = Integer.MIN_VALUE;
private static final int DEFAULT_DISTANCE = 25; private static final int DEFAULT_DISTANCE = 25;
static final int MAX_DISTANCE = 90; static final int MAX_DISTANCE = 90;
@@ -115,6 +125,9 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
@Inject @Inject
private Client client; private Client client;
@Inject
private OpenCLManager openCLManager;
@Inject @Inject
private ClientThread clientThread; private ClientThread clientThread;
@@ -133,7 +146,14 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
@Inject @Inject
private PluginManager pluginManager; private PluginManager pluginManager;
private boolean useComputeShaders; enum ComputeMode
{
NONE,
OPENGL,
OPENCL
}
private ComputeMode computeMode = ComputeMode.NONE;
private Canvas canvas; private Canvas canvas;
private JAWTWindow jawtWindow; private JAWTWindow jawtWindow;
@@ -182,23 +202,22 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
private int texSceneHandle; private int texSceneHandle;
private int rboSceneHandle; private int rboSceneHandle;
// scene vertex buffer id // scene vertex buffer
private int bufferId; private final GLBuffer sceneVertexBuffer = new GLBuffer();
// scene uv buffer id // scene uv buffer
private int uvBufferId; private final GLBuffer sceneUvBuffer = new GLBuffer();
private int tmpBufferId; // temporary scene vertex buffer private final GLBuffer tmpVertexBuffer = new GLBuffer(); // temporary scene vertex buffer
private int tmpUvBufferId; // temporary scene uv buffer private final GLBuffer tmpUvBuffer = new GLBuffer(); // temporary scene uv buffer
private int tmpModelBufferId; // scene model buffer, large private final GLBuffer tmpModelBufferLarge = new GLBuffer(); // scene model buffer, large
private int tmpModelBufferSmallId; // scene model buffer, small private final GLBuffer tmpModelBufferSmall = new GLBuffer(); // scene model buffer, small
private int tmpModelBufferUnorderedId; private final GLBuffer tmpModelBufferUnordered = new GLBuffer(); // scene model buffer, unordered
private int tmpOutBufferId; // target vertex buffer for compute shaders private final GLBuffer tmpOutBuffer = new GLBuffer(); // target vertex buffer for compute shaders
private int tmpOutUvBufferId; // target uv buffer for compute shaders private final GLBuffer tmpOutUvBuffer = new GLBuffer(); // target uv buffer for compute shaders
private int textureArrayId; private int textureArrayId;
private int uniformBufferId; private final GLBuffer uniformBuffer = new GLBuffer();
private final IntBuffer uniformBuffer = GpuIntBuffer.allocateDirect(5 + 3 + 2048 * 4);
private final float[] textureOffsets = new float[128]; private final float[] textureOffsets = new float[128];
private GpuIntBuffer vertexBuffer; private GpuIntBuffer vertexBuffer;
@@ -278,7 +297,6 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
{ {
try try
{ {
bufferId = uvBufferId = uniformBufferId = tmpBufferId = tmpUvBufferId = tmpModelBufferId = tmpModelBufferSmallId = tmpModelBufferUnorderedId = tmpOutBufferId = tmpOutUvBufferId = -1;
texSceneHandle = fboSceneHandle = rboSceneHandle = -1; // AA FBO texSceneHandle = fboSceneHandle = rboSceneHandle = -1; // AA FBO
unorderedModels = smallModels = largeModels = 0; unorderedModels = smallModels = largeModels = 0;
drawingModel = false; drawingModel = false;
@@ -290,8 +308,9 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
return false; return false;
} }
// OSX supports up to OpenGL 4.1, however 4.3 is required for compute shaders computeMode = config.useComputeShaders()
useComputeShaders = config.useComputeShaders() && OSType.getOSType() != OSType.MacOS; ? (OSType.getOSType() == OSType.MacOS ? ComputeMode.OPENCL : ComputeMode.OPENGL)
: ComputeMode.NONE;
canvas.setIgnoreRepaint(true); canvas.setIgnoreRepaint(true);
@@ -397,7 +416,7 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
if (client.getGameState() == GameState.LOGGED_IN) if (client.getGameState() == GameState.LOGGED_IN)
{ {
uploadScene(); invokeOnMainThread(this::uploadScene);
} }
} }
catch (Throwable e) catch (Throwable e)
@@ -433,6 +452,8 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
invokeOnMainThread(() -> invokeOnMainThread(() ->
{ {
openCLManager.cleanup();
if (gl != null) if (gl != null)
{ {
if (textureArrayId != -1) if (textureArrayId != -1)
@@ -441,11 +462,7 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
textureArrayId = -1; textureArrayId = -1;
} }
if (uniformBufferId != -1) destroyGlBuffer(uniformBuffer);
{
glDeleteBuffer(gl, uniformBufferId);
uniformBufferId = -1;
}
shutdownBuffers(); shutdownBuffers();
shutdownInterfaceTexture(); shutdownInterfaceTexture();
@@ -519,12 +536,16 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
glProgram = PROGRAM.compile(gl, template); glProgram = PROGRAM.compile(gl, template);
glUiProgram = UI_PROGRAM.compile(gl, template); glUiProgram = UI_PROGRAM.compile(gl, template);
if (useComputeShaders) if (computeMode == ComputeMode.OPENGL)
{ {
glComputeProgram = COMPUTE_PROGRAM.compile(gl, template); glComputeProgram = COMPUTE_PROGRAM.compile(gl, template);
glSmallComputeProgram = SMALL_COMPUTE_PROGRAM.compile(gl, template); glSmallComputeProgram = SMALL_COMPUTE_PROGRAM.compile(gl, template);
glUnorderedComputeProgram = UNORDERED_COMPUTE_PROGRAM.compile(gl, template); glUnorderedComputeProgram = UNORDERED_COMPUTE_PROGRAM.compile(gl, template);
} }
else if (computeMode == ComputeMode.OPENCL)
{
openCLManager.init(gl);
}
initUniforms(); initUniforms();
} }
@@ -593,8 +614,8 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
-1f, 1f, 0.0f, 0.0f, 0f // top left -1f, 1f, 0.0f, 0.0f, 0f // top left
}); });
vboUiBuf.rewind(); vboUiBuf.rewind();
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, vboUiHandle); gl.glBindBuffer(GL_ARRAY_BUFFER, vboUiHandle);
gl.glBufferData(gl.GL_ARRAY_BUFFER, vboUiBuf.capacity() * Float.BYTES, vboUiBuf, gl.GL_STATIC_DRAW); gl.glBufferData(GL_ARRAY_BUFFER, vboUiBuf.capacity() * Float.BYTES, vboUiBuf, gl.GL_STATIC_DRAW);
// position attribute // position attribute
gl.glVertexAttribPointer(0, 3, gl.GL_FLOAT, false, 5 * Float.BYTES, 0); gl.glVertexAttribPointer(0, 3, gl.GL_FLOAT, false, 5 * Float.BYTES, 0);
@@ -605,7 +626,7 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
gl.glEnableVertexAttribArray(1); gl.glEnableVertexAttribArray(1);
// unbind VBO // unbind VBO
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, 0); gl.glBindBuffer(GL_ARRAY_BUFFER, 0);
} }
private void shutdownVao() private void shutdownVao()
@@ -622,71 +643,49 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
private void initBuffers() private void initBuffers()
{ {
bufferId = glGenBuffers(gl); initGlBuffer(sceneVertexBuffer);
uvBufferId = glGenBuffers(gl); initGlBuffer(sceneUvBuffer);
tmpBufferId = glGenBuffers(gl); initGlBuffer(tmpVertexBuffer);
tmpUvBufferId = glGenBuffers(gl); initGlBuffer(tmpUvBuffer);
tmpModelBufferId = glGenBuffers(gl); initGlBuffer(tmpModelBufferLarge);
tmpModelBufferSmallId = glGenBuffers(gl); initGlBuffer(tmpModelBufferSmall);
tmpModelBufferUnorderedId = glGenBuffers(gl); initGlBuffer(tmpModelBufferUnordered);
tmpOutBufferId = glGenBuffers(gl); initGlBuffer(tmpOutBuffer);
tmpOutUvBufferId = glGenBuffers(gl); initGlBuffer(tmpOutUvBuffer);
}
private void initGlBuffer(GLBuffer glBuffer)
{
glBuffer.glBufferId = glGenBuffers(gl);
} }
private void shutdownBuffers() private void shutdownBuffers()
{ {
if (bufferId != -1) destroyGlBuffer(sceneVertexBuffer);
{ destroyGlBuffer(sceneUvBuffer);
glDeleteBuffer(gl, bufferId);
bufferId = -1;
}
if (uvBufferId != -1) destroyGlBuffer(tmpVertexBuffer);
{ destroyGlBuffer(tmpUvBuffer);
glDeleteBuffer(gl, uvBufferId); destroyGlBuffer(tmpModelBufferLarge);
uvBufferId = -1; destroyGlBuffer(tmpModelBufferSmall);
} destroyGlBuffer(tmpModelBufferUnordered);
destroyGlBuffer(tmpOutBuffer);
destroyGlBuffer(tmpOutUvBuffer);
}
if (tmpBufferId != -1) private void destroyGlBuffer(GLBuffer glBuffer)
{
if (glBuffer.glBufferId != -1)
{ {
glDeleteBuffer(gl, tmpBufferId); glDeleteBuffer(gl, glBuffer.glBufferId);
tmpBufferId = -1; glBuffer.glBufferId = -1;
} }
glBuffer.size = -1;
if (tmpUvBufferId != -1) if (glBuffer.cl_mem != null)
{ {
glDeleteBuffer(gl, tmpUvBufferId); CL.clReleaseMemObject(glBuffer.cl_mem);
tmpUvBufferId = -1; glBuffer.cl_mem = null;
}
if (tmpModelBufferId != -1)
{
glDeleteBuffer(gl, tmpModelBufferId);
tmpModelBufferId = -1;
}
if (tmpModelBufferSmallId != -1)
{
glDeleteBuffer(gl, tmpModelBufferSmallId);
tmpModelBufferSmallId = -1;
}
if (tmpModelBufferUnorderedId != -1)
{
glDeleteBuffer(gl, tmpModelBufferUnorderedId);
tmpModelBufferUnorderedId = -1;
}
if (tmpOutBufferId != -1)
{
glDeleteBuffer(gl, tmpOutBufferId);
tmpOutBufferId = -1;
}
if (tmpOutUvBufferId != -1)
{
glDeleteBuffer(gl, tmpOutUvBufferId);
tmpOutUvBufferId = -1;
} }
} }
@@ -709,21 +708,21 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
private void initUniformBuffer() private void initUniformBuffer()
{ {
uniformBufferId = glGenBuffers(gl); initGlBuffer(uniformBuffer);
gl.glBindBuffer(gl.GL_UNIFORM_BUFFER, uniformBufferId);
uniformBuffer.clear(); IntBuffer uniformBuf = GpuIntBuffer.allocateDirect(8 + 2048 * 4);
uniformBuffer.put(new int[8]); uniformBuf.put(new int[8]); // uniform block
final int[] pad = new int[2]; final int[] pad = new int[2];
for (int i = 0; i < 2048; i++) for (int i = 0; i < 2048; i++)
{ {
uniformBuffer.put(Perspective.SINE[i]); uniformBuf.put(Perspective.SINE[i]);
uniformBuffer.put(Perspective.COSINE[i]); uniformBuf.put(Perspective.COSINE[i]);
uniformBuffer.put(pad); uniformBuf.put(pad); // ivec2 alignment in std140 is 16 bytes
} }
uniformBuffer.flip(); uniformBuf.flip();
gl.glBufferData(gl.GL_UNIFORM_BUFFER, uniformBuffer.limit() * Integer.BYTES, uniformBuffer, gl.GL_DYNAMIC_DRAW); updateBuffer(uniformBuffer, GL_UNIFORM_BUFFER, uniformBuf.limit() * Integer.BYTES, uniformBuf, GL_DYNAMIC_DRAW, CL_MEM_READ_ONLY);
gl.glBindBuffer(gl.GL_UNIFORM_BUFFER, 0); gl.glBindBuffer(GL_UNIFORM_BUFFER, 0);
} }
private void initAAFbo(int width, int height, int aaSamples) private void initAAFbo(int width, int height, int aaSamples)
@@ -785,9 +784,11 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
invokeOnMainThread(() -> invokeOnMainThread(() ->
{ {
// UBO. Only the first 32 bytes get modified here, the rest is the constant sin/cos table. // UBO. Only the first 32 bytes get modified here, the rest is the constant sin/cos table.
gl.glBindBuffer(gl.GL_UNIFORM_BUFFER, uniformBufferId); // We can reuse the vertex buffer since it isn't used yet.
uniformBuffer.clear(); vertexBuffer.clear();
uniformBuffer vertexBuffer.ensureCapacity(32);
IntBuffer uniformBuf = vertexBuffer.getBuffer();
uniformBuf
.put(yaw) .put(yaw)
.put(pitch) .put(pitch)
.put(client.getCenterX()) .put(client.getCenterX())
@@ -796,12 +797,14 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
.put(cameraX) .put(cameraX)
.put(cameraY) .put(cameraY)
.put(cameraZ); .put(cameraZ);
uniformBuffer.flip(); uniformBuf.flip();
gl.glBufferSubData(gl.GL_UNIFORM_BUFFER, 0, uniformBuffer.limit() * Integer.BYTES, uniformBuffer); gl.glBindBuffer(GL_UNIFORM_BUFFER, uniformBuffer.glBufferId);
gl.glBindBuffer(gl.GL_UNIFORM_BUFFER, 0); gl.glBufferSubData(GL_UNIFORM_BUFFER, 0, uniformBuf.limit() * Integer.BYTES, uniformBuf);
gl.glBindBuffer(GL_UNIFORM_BUFFER, 0);
gl.glBindBufferBase(gl.GL_UNIFORM_BUFFER, 0, uniformBufferId); gl.glBindBufferBase(GL_UNIFORM_BUFFER, 0, uniformBuffer.glBufferId);
uniformBuf.clear();
}); });
} }
@@ -813,7 +816,7 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
private void postDraw() private void postDraw()
{ {
if (!useComputeShaders) if (computeMode == ComputeMode.NONE)
{ {
// Upload buffers // Upload buffers
vertexBuffer.flip(); vertexBuffer.flip();
@@ -822,12 +825,8 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
IntBuffer vertexBuffer = this.vertexBuffer.getBuffer(); IntBuffer vertexBuffer = this.vertexBuffer.getBuffer();
FloatBuffer uvBuffer = this.uvBuffer.getBuffer(); FloatBuffer uvBuffer = this.uvBuffer.getBuffer();
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, tmpBufferId); updateBuffer(tmpVertexBuffer, GL_ARRAY_BUFFER, vertexBuffer.limit() * Integer.BYTES, vertexBuffer, GL_DYNAMIC_DRAW, 0L);
gl.glBufferData(gl.GL_ARRAY_BUFFER, vertexBuffer.limit() * Integer.BYTES, vertexBuffer, gl.GL_DYNAMIC_DRAW); updateBuffer(tmpUvBuffer, GL_ARRAY_BUFFER, uvBuffer.limit() * Float.BYTES, uvBuffer, GL_DYNAMIC_DRAW, 0L);
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, tmpUvBufferId);
gl.glBufferData(gl.GL_ARRAY_BUFFER, uvBuffer.limit() * Float.BYTES, uvBuffer, gl.GL_DYNAMIC_DRAW);
return; return;
} }
@@ -844,79 +843,91 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
IntBuffer modelBufferSmall = this.modelBufferSmall.getBuffer(); IntBuffer modelBufferSmall = this.modelBufferSmall.getBuffer();
IntBuffer modelBufferUnordered = this.modelBufferUnordered.getBuffer(); IntBuffer modelBufferUnordered = this.modelBufferUnordered.getBuffer();
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, tmpBufferId); // temp buffers
gl.glBufferData(gl.GL_ARRAY_BUFFER, vertexBuffer.limit() * Integer.BYTES, vertexBuffer, gl.GL_DYNAMIC_DRAW); updateBuffer(tmpVertexBuffer, GL_ARRAY_BUFFER, vertexBuffer.limit() * Integer.BYTES, vertexBuffer, GL_DYNAMIC_DRAW, CL_MEM_READ_ONLY);
updateBuffer(tmpUvBuffer, GL_ARRAY_BUFFER, uvBuffer.limit() * Float.BYTES, uvBuffer, GL_DYNAMIC_DRAW, CL_MEM_READ_ONLY);
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, tmpUvBufferId); // model buffers
gl.glBufferData(gl.GL_ARRAY_BUFFER, uvBuffer.limit() * Float.BYTES, uvBuffer, gl.GL_DYNAMIC_DRAW); updateBuffer(tmpModelBufferLarge, GL_ARRAY_BUFFER, modelBuffer.limit() * Integer.BYTES, modelBuffer, GL_DYNAMIC_DRAW, CL_MEM_READ_ONLY);
updateBuffer(tmpModelBufferSmall, GL_ARRAY_BUFFER, modelBufferSmall.limit() * Integer.BYTES, modelBufferSmall, GL_DYNAMIC_DRAW, CL_MEM_READ_ONLY);
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, tmpModelBufferId); updateBuffer(tmpModelBufferUnordered, GL_ARRAY_BUFFER, modelBufferUnordered.limit() * Integer.BYTES, modelBufferUnordered, GL_DYNAMIC_DRAW, CL_MEM_READ_ONLY);
gl.glBufferData(gl.GL_ARRAY_BUFFER, modelBuffer.limit() * Integer.BYTES, modelBuffer, gl.GL_DYNAMIC_DRAW);
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, tmpModelBufferSmallId);
gl.glBufferData(gl.GL_ARRAY_BUFFER, modelBufferSmall.limit() * Integer.BYTES, modelBufferSmall, gl.GL_DYNAMIC_DRAW);
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, tmpModelBufferUnorderedId);
gl.glBufferData(gl.GL_ARRAY_BUFFER, modelBufferUnordered.limit() * Integer.BYTES, modelBufferUnordered, gl.GL_DYNAMIC_DRAW);
// Output buffers // Output buffers
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, tmpOutBufferId); updateBuffer(tmpOutBuffer,
gl.glBufferData(gl.GL_ARRAY_BUFFER, GL_ARRAY_BUFFER,
targetBufferOffset * 16, // each vertex is an ivec4, which is 16 bytes targetBufferOffset * 16, // each vertex is an ivec4, which is 16 bytes
null, null,
gl.GL_STREAM_DRAW); GL_STREAM_DRAW,
CL_MEM_WRITE_ONLY);
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, tmpOutUvBufferId); updateBuffer(tmpOutUvBuffer,
gl.glBufferData(gl.GL_ARRAY_BUFFER, GL_ARRAY_BUFFER,
targetBufferOffset * 16, targetBufferOffset * 16, // each vertex is an ivec4, which is 16 bytes
null, null,
gl.GL_STREAM_DRAW); GL_STREAM_DRAW,
CL_MEM_WRITE_ONLY);
// Bind UBO to compute programs if (computeMode == ComputeMode.OPENCL)
gl.glUniformBlockBinding(glSmallComputeProgram, uniBlockSmall, 0); {
gl.glUniformBlockBinding(glComputeProgram, uniBlockLarge, 0); // The docs for clEnqueueAcquireGLObjects say all pending GL operations must be completed before calling
// clEnqueueAcquireGLObjects, and recommends calling glFinish() as the only portable way to do that.
// However no issues have been observed from not calling it, and so will leave disabled for now.
// gl.glFinish();
openCLManager.compute(
unorderedModels, smallModels, largeModels,
sceneVertexBuffer, sceneUvBuffer,
tmpVertexBuffer, tmpUvBuffer,
tmpModelBufferUnordered, tmpModelBufferSmall, tmpModelBufferLarge,
tmpOutBuffer, tmpOutUvBuffer,
uniformBuffer);
return;
}
/* /*
* Compute is split into three separate programs: 'unordered', 'small', and 'large' * Compute is split into three separate programs: 'unordered', 'small', and 'large'
* to save on GPU resources. Small will sort <= 512 faces, large will do <= 4096. * to save on GPU resources. Small will sort <= 512 faces, large will do <= 4096.
*/ */
// Bind UBO to compute programs
gl.glUniformBlockBinding(glSmallComputeProgram, uniBlockSmall, 0);
gl.glUniformBlockBinding(glComputeProgram, uniBlockLarge, 0);
// unordered // unordered
gl.glUseProgram(glUnorderedComputeProgram); gl.glUseProgram(glUnorderedComputeProgram);
gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 0, tmpModelBufferUnorderedId); gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 0, tmpModelBufferUnordered.glBufferId);
gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 1, this.bufferId); gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 1, sceneVertexBuffer.glBufferId);
gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 2, tmpBufferId); gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 2, tmpVertexBuffer.glBufferId);
gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 3, tmpOutBufferId); gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 3, tmpOutBuffer.glBufferId);
gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 4, tmpOutUvBufferId); gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 4, tmpOutUvBuffer.glBufferId);
gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 5, this.uvBufferId); gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 5, sceneUvBuffer.glBufferId);
gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 6, tmpUvBufferId); gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 6, tmpUvBuffer.glBufferId);
gl.glDispatchCompute(unorderedModels, 1, 1); gl.glDispatchCompute(unorderedModels, 1, 1);
// small // small
gl.glUseProgram(glSmallComputeProgram); gl.glUseProgram(glSmallComputeProgram);
gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 0, tmpModelBufferSmallId); gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 0, tmpModelBufferSmall.glBufferId);
gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 1, this.bufferId); gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 1, sceneVertexBuffer.glBufferId);
gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 2, tmpBufferId); gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 2, tmpVertexBuffer.glBufferId);
gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 3, tmpOutBufferId); gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 3, tmpOutBuffer.glBufferId);
gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 4, tmpOutUvBufferId); gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 4, tmpOutUvBuffer.glBufferId);
gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 5, this.uvBufferId); gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 5, sceneUvBuffer.glBufferId);
gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 6, tmpUvBufferId); gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 6, tmpUvBuffer.glBufferId);
gl.glDispatchCompute(smallModels, 1, 1); gl.glDispatchCompute(smallModels, 1, 1);
// large // large
gl.glUseProgram(glComputeProgram); gl.glUseProgram(glComputeProgram);
gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 0, tmpModelBufferId); gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 0, tmpModelBufferLarge.glBufferId);
gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 1, this.bufferId); gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 1, sceneVertexBuffer.glBufferId);
gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 2, tmpBufferId); gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 2, tmpVertexBuffer.glBufferId);
gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 3, tmpOutBufferId); gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 3, tmpOutBuffer.glBufferId);
gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 4, tmpOutUvBufferId); gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 4, tmpOutUvBuffer.glBufferId);
gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 5, this.uvBufferId); gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 5, sceneUvBuffer.glBufferId);
gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 6, tmpUvBufferId); gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 6, tmpUvBuffer.glBufferId);
gl.glDispatchCompute(largeModels, 1, 1); gl.glDispatchCompute(largeModels, 1, 1);
} }
@@ -926,7 +937,7 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
SceneTilePaint paint, int tileZ, int tileX, int tileY, SceneTilePaint paint, int tileZ, int tileX, int tileY,
int zoom, int centerX, int centerY) int zoom, int centerX, int centerY)
{ {
if (!useComputeShaders) if (computeMode == ComputeMode.NONE)
{ {
targetBufferOffset += sceneUploader.upload(paint, targetBufferOffset += sceneUploader.upload(paint,
tileZ, tileX, tileY, tileZ, tileX, tileY,
@@ -963,7 +974,7 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
SceneTileModel model, int tileZ, int tileX, int tileY, SceneTileModel model, int tileZ, int tileX, int tileY,
int zoom, int centerX, int centerY) int zoom, int centerX, int centerY)
{ {
if (!useComputeShaders) if (computeMode == ComputeMode.NONE)
{ {
targetBufferOffset += sceneUploader.upload(model, targetBufferOffset += sceneUploader.upload(model,
tileX, tileY, tileX, tileY,
@@ -1131,7 +1142,7 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
// Ceil the sizes because even if the size is 599.1 we want to treat it as size 600 (i.e. render to the x=599 pixel). // Ceil the sizes because even if the size is 599.1 we want to treat it as size 600 (i.e. render to the x=599 pixel).
renderViewportHeight = (int) Math.ceil(scaleFactorY * (renderViewportHeight)) + padding * 2; renderViewportHeight = (int) Math.ceil(scaleFactorY * (renderViewportHeight)) + padding * 2;
renderViewportWidth = (int) Math.ceil(scaleFactorX * (renderViewportWidth )) + padding * 2; renderViewportWidth = (int) Math.ceil(scaleFactorX * (renderViewportWidth )) + padding * 2;
// Floor the offsets because even if the offset is 4.9, we want to render to the x=4 pixel anyway. // Floor the offsets because even if the offset is 4.9, we want to render to the x=4 pixel anyway.
renderHeightOff = (int) Math.floor(scaleFactorY * (renderHeightOff)) - padding; renderHeightOff = (int) Math.floor(scaleFactorY * (renderHeightOff)) - padding;
@@ -1195,27 +1206,36 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
gl.glBindVertexArray(vaoHandle); gl.glBindVertexArray(vaoHandle);
int vertexBuffer, uvBuffer; int vertexBuffer, uvBuffer;
if (useComputeShaders) if (computeMode != ComputeMode.NONE)
{ {
// Before reading the SSBOs written to from postDrawScene() we must insert a barrier if (computeMode == ComputeMode.OPENGL)
gl.glMemoryBarrier(gl.GL_SHADER_STORAGE_BARRIER_BIT); {
// Before reading the SSBOs written to from postDrawScene() we must insert a barrier
gl.glMemoryBarrier(gl.GL_SHADER_STORAGE_BARRIER_BIT);
}
else
{
// Wait for the command queue to finish, so that we know the compute is done
openCLManager.finish();
}
// Draw using the output buffer of the compute // Draw using the output buffer of the compute
vertexBuffer = tmpOutBufferId; vertexBuffer = tmpOutBuffer.glBufferId;
uvBuffer = tmpOutUvBufferId; uvBuffer = tmpOutUvBuffer.glBufferId;
} }
else else
{ {
// Only use the temporary buffers, which will contain the full scene // Only use the temporary buffers, which will contain the full scene
vertexBuffer = tmpBufferId; vertexBuffer = tmpVertexBuffer.glBufferId;
uvBuffer = tmpUvBufferId; uvBuffer = tmpUvBuffer.glBufferId;
} }
gl.glEnableVertexAttribArray(0); gl.glEnableVertexAttribArray(0);
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, vertexBuffer); gl.glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
gl.glVertexAttribIPointer(0, 4, gl.GL_INT, 0, 0); gl.glVertexAttribIPointer(0, 4, gl.GL_INT, 0, 0);
gl.glEnableVertexAttribArray(1); gl.glEnableVertexAttribArray(1);
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, uvBuffer); gl.glBindBuffer(GL_ARRAY_BUFFER, uvBuffer);
gl.glVertexAttribPointer(1, 4, gl.GL_FLOAT, false, 0, 0); gl.glVertexAttribPointer(1, 4, gl.GL_FLOAT, false, 0, 0);
gl.glDrawArrays(gl.GL_TRIANGLES, 0, targetBufferOffset); gl.glDrawArrays(gl.GL_TRIANGLES, 0, targetBufferOffset);
@@ -1400,12 +1420,12 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
@Subscribe @Subscribe
public void onGameStateChanged(GameStateChanged gameStateChanged) public void onGameStateChanged(GameStateChanged gameStateChanged)
{ {
if (!useComputeShaders || gameStateChanged.getGameState() != GameState.LOGGED_IN) if (computeMode == ComputeMode.NONE || gameStateChanged.getGameState() != GameState.LOGGED_IN)
{ {
return; return;
} }
uploadScene(); invokeOnMainThread(this::uploadScene);
} }
private void uploadScene() private void uploadScene()
@@ -1421,13 +1441,10 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
IntBuffer vertexBuffer = this.vertexBuffer.getBuffer(); IntBuffer vertexBuffer = this.vertexBuffer.getBuffer();
FloatBuffer uvBuffer = this.uvBuffer.getBuffer(); FloatBuffer uvBuffer = this.uvBuffer.getBuffer();
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, bufferId); updateBuffer(sceneVertexBuffer, GL_ARRAY_BUFFER, vertexBuffer.limit() * Integer.BYTES, vertexBuffer, GL_STATIC_COPY, CL_MEM_READ_ONLY);
gl.glBufferData(gl.GL_ARRAY_BUFFER, vertexBuffer.limit() * Integer.BYTES, vertexBuffer, gl.GL_STATIC_COPY); updateBuffer(sceneUvBuffer, GL_ARRAY_BUFFER, uvBuffer.limit() * Float.BYTES, uvBuffer, GL_STATIC_COPY, CL_MEM_READ_ONLY);
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, uvBufferId); gl.glBindBuffer(GL_ARRAY_BUFFER, 0);
gl.glBufferData(gl.GL_ARRAY_BUFFER, uvBuffer.limit() * Float.BYTES, uvBuffer, gl.GL_STATIC_COPY);
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, 0);
vertexBuffer.clear(); vertexBuffer.clear();
uvBuffer.clear(); uvBuffer.clear();
@@ -1492,7 +1509,7 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
@Override @Override
public void draw(Renderable renderable, int orientation, int pitchSin, int pitchCos, int yawSin, int yawCos, int x, int y, int z, long hash) public void draw(Renderable renderable, int orientation, int pitchSin, int pitchCos, int yawSin, int yawCos, int x, int y, int z, long hash)
{ {
if (!useComputeShaders) if (computeMode == ComputeMode.NONE)
{ {
Model model = renderable instanceof Model ? (Model) renderable : renderable.getModel(); Model model = renderable instanceof Model ? (Model) renderable : renderable.getModel();
if (model != null) if (model != null)
@@ -1673,7 +1690,7 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
private int getDrawDistance() private int getDrawDistance()
{ {
final int limit = useComputeShaders ? MAX_DISTANCE : DEFAULT_DISTANCE; final int limit = computeMode != ComputeMode.NONE ? MAX_DISTANCE : DEFAULT_DISTANCE;
return Ints.constrainToRange(config.drawDistance(), 0, limit); return Ints.constrainToRange(config.drawDistance(), 0, limit);
} }
@@ -1688,4 +1705,36 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
runnable.run(); runnable.run();
} }
} }
private void updateBuffer(GLBuffer glBuffer, int target, int size, Buffer data, int usage, long clFlags)
{
gl.glBindBuffer(target, glBuffer.glBufferId);
if (size > glBuffer.size)
{
log.trace("Buffer resize: {} {} -> {}", glBuffer, glBuffer.size, size);
glBuffer.size = size;
gl.glBufferData(target, size, data, usage);
if (computeMode == ComputeMode.OPENCL)
{
if (glBuffer.cl_mem != null)
{
CL.clReleaseMemObject(glBuffer.cl_mem);
}
if (size == 0)
{
glBuffer.cl_mem = null;
}
else
{
glBuffer.cl_mem = clCreateFromGLBuffer(openCLManager.context, clFlags, glBuffer.glBufferId, null);
}
}
}
else if (data != null)
{
gl.glBufferSubData(target, 0, size, data);
}
}
} }

View File

@@ -0,0 +1,521 @@
/*
* Copyright (c) 2021, Adam <Adam@sigterm.info>
* 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.plugins.gpu;
import com.google.common.base.Charsets;
import com.jogamp.nativewindow.NativeSurface;
import com.jogamp.opengl.GL4;
import com.jogamp.opengl.GLContext;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Objects;
import javax.inject.Singleton;
import jogamp.opengl.GLContextImpl;
import jogamp.opengl.GLDrawableImpl;
import jogamp.opengl.egl.EGLContext;
import jogamp.opengl.macosx.cgl.CGL;
import jogamp.opengl.windows.wgl.WindowsWGLContext;
import jogamp.opengl.x11.glx.X11GLXContext;
import lombok.extern.slf4j.Slf4j;
import net.runelite.client.plugins.gpu.template.Template;
import net.runelite.client.util.OSType;
import org.jocl.CL;
import static org.jocl.CL.*;
import org.jocl.CLException;
import org.jocl.Pointer;
import org.jocl.Sizeof;
import org.jocl.cl_command_queue;
import org.jocl.cl_context;
import org.jocl.cl_context_properties;
import org.jocl.cl_device_id;
import org.jocl.cl_event;
import org.jocl.cl_kernel;
import org.jocl.cl_mem;
import org.jocl.cl_platform_id;
import org.jocl.cl_program;
@Singleton
@Slf4j
class OpenCLManager
{
private static final String GL_SHARING_PLATFORM_EXT = "cl_khr_gl_sharing";
private static final String KERNEL_NAME_UNORDERED = "computeUnordered";
private static final String KERNEL_NAME_LARGE = "computeLarge";
private static final int MIN_WORK_GROUP_SIZE = 256;
private static final int SMALL_SIZE = GpuPlugin.SMALL_TRIANGLE_COUNT;
private static final int LARGE_SIZE = GpuPlugin.MAX_TRIANGLE;
// struct shared_data {
// int totalNum[12];
// int totalDistance[12];
// int totalMappedNum[18];
// int min10;
// int dfs[0];
// };
private static final int SHARED_SIZE = 12 + 12 + 18 + 1; // in ints
// The number of faces each worker processes in the two kernels
private int largeFaceCount;
private int smallFaceCount;
private cl_platform_id platform;
private cl_device_id device;
cl_context context;
private cl_command_queue commandQueue;
private cl_program programUnordered;
private cl_program programSmall;
private cl_program programLarge;
private cl_kernel kernelUnordered;
private cl_kernel kernelSmall;
private cl_kernel kernelLarge;
void init(GL4 gl)
{
CL.setExceptionsEnabled(true);
switch (OSType.getOSType())
{
case Windows:
case Linux:
initPlatform();
initDevice();
initContext(gl);
break;
case MacOS:
initMacOS(gl);
break;
default:
throw new RuntimeException("Unsupported OS Type " + OSType.getOSType().name());
}
ensureMinWorkGroupSize();
initQueue();
compilePrograms();
}
void cleanup()
{
if (programUnordered != null)
{
CL.clReleaseProgram(programUnordered);
programUnordered = null;
}
if (programSmall != null)
{
CL.clReleaseProgram(programSmall);
programSmall = null;
}
if (programLarge != null)
{
CL.clReleaseProgram(programLarge);
programLarge = null;
}
if (kernelUnordered != null)
{
CL.clReleaseKernel(kernelUnordered);
kernelUnordered = null;
}
if (kernelSmall != null)
{
CL.clReleaseKernel(kernelSmall);
kernelSmall = null;
}
if (kernelLarge != null)
{
CL.clReleaseKernel(kernelLarge);
kernelLarge = null;
}
if (commandQueue != null)
{
CL.clReleaseCommandQueue(commandQueue);
commandQueue = null;
}
if (context != null)
{
CL.clReleaseContext(context);
context = null;
}
if (device != null)
{
CL.clReleaseDevice(device);
device = null;
}
}
private String logPlatformInfo(cl_platform_id platform, int param)
{
long[] size = new long[1];
clGetPlatformInfo(platform, param, 0, null, size);
byte[] buffer = new byte[(int) size[0]];
clGetPlatformInfo(platform, param, buffer.length, Pointer.to(buffer), null);
String platformInfo = new String(buffer, Charsets.UTF_8);
log.debug("Platform: {}, {}", stringFor_cl_platform_info(param), platformInfo);
return platformInfo;
}
private void logBuildInfo(cl_program program, int param)
{
long[] size = new long[1];
clGetProgramBuildInfo(program, device, param, 0, null, size);
ByteBuffer buffer = ByteBuffer.allocateDirect((int) size[0]);
clGetProgramBuildInfo(program, device, param, buffer.limit(), Pointer.toBuffer(buffer), null);
switch (param)
{
case CL_PROGRAM_BUILD_STATUS:
log.debug("Build status: {}, {}", stringFor_cl_program_build_info(param), stringFor_cl_build_status(buffer.getInt()));
break;
case CL_PROGRAM_BINARY_TYPE:
log.debug("Binary type: {}, {}", stringFor_cl_program_build_info(param), stringFor_cl_program_binary_type(buffer.getInt()));
break;
case CL_PROGRAM_BUILD_LOG:
String buildLog = StandardCharsets.US_ASCII.decode(buffer).toString();
log.trace("Build log: {}, {}", stringFor_cl_program_build_info(param), buildLog);
break;
case CL_PROGRAM_BUILD_OPTIONS:
String message = StandardCharsets.US_ASCII.decode(buffer).toString();
log.debug("Build options: {}, {}", stringFor_cl_program_build_info(param), message);
break;
default:
throw new IllegalArgumentException();
}
}
private void initPlatform()
{
int[] platformCount = new int[1];
clGetPlatformIDs(0, null, platformCount);
if (platformCount[0] == 0)
{
throw new RuntimeException("No compute platforms found");
}
cl_platform_id[] platforms = new cl_platform_id[platformCount[0]];
clGetPlatformIDs(platforms.length, platforms, null);
for (cl_platform_id platform : platforms)
{
log.debug("Found cl_platform_id {}", platform);
logPlatformInfo(platform, CL_PLATFORM_PROFILE);
logPlatformInfo(platform, CL_PLATFORM_VERSION);
logPlatformInfo(platform, CL_PLATFORM_NAME);
logPlatformInfo(platform, CL_PLATFORM_VENDOR);
String[] extensions = logPlatformInfo(platform, CL_PLATFORM_EXTENSIONS).split(" ");
if (Arrays.stream(extensions).noneMatch(s -> s.equals(GL_SHARING_PLATFORM_EXT)))
{
throw new RuntimeException("Platform does not support OpenGL buffer sharing");
}
}
platform = platforms[0];
log.debug("Selected cl_platform_id {}", platform);
}
private void initDevice()
{
int[] deviceCount = new int[1];
clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, 0, null, deviceCount);
if (deviceCount[0] == 0)
{
throw new RuntimeException("No compute devices found");
}
cl_device_id[] devices = new cl_device_id[(int) deviceCount[0]];
clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, devices.length, devices, null);
for (cl_device_id device : devices)
{
long[] size = new long[1];
clGetDeviceInfo(device, CL_DEVICE_EXTENSIONS, 0, null, size);
byte[] devInfoBuf = new byte[(int) size[0]];
clGetDeviceInfo(device, CL_DEVICE_EXTENSIONS, devInfoBuf.length, Pointer.to(devInfoBuf), null);
log.debug("Found cl_device_id: {}", device);
log.debug("Device extensions: {}", new String(devInfoBuf, Charsets.UTF_8));
}
device = devices[0];
log.debug("Selected cl_device_id {}", device);
}
private void initContext(GL4 gl)
{
// set computation platform
cl_context_properties contextProps = new cl_context_properties();
contextProps.addProperty(CL_CONTEXT_PLATFORM, platform);
// pull gl context
GLContext glContext = gl.getContext();
log.debug("Got GLContext of type {}", glContext.getClass().getSimpleName());
if (!glContext.isCurrent())
{
throw new RuntimeException("Can't create OpenCL context from inactive GL Context");
}
// get correct props based on os
long glContextHandle = glContext.getHandle();
GLContextImpl glContextImpl = (GLContextImpl) glContext;
GLDrawableImpl glDrawableImpl = glContextImpl.getDrawableImpl();
NativeSurface nativeSurface = glDrawableImpl.getNativeSurface();
if (glContext instanceof X11GLXContext)
{
long displayHandle = nativeSurface.getDisplayHandle();
contextProps.addProperty(CL_GL_CONTEXT_KHR, glContextHandle);
contextProps.addProperty(CL_GLX_DISPLAY_KHR, displayHandle);
}
else if (glContext instanceof WindowsWGLContext)
{
long surfaceHandle = nativeSurface.getSurfaceHandle();
contextProps.addProperty(CL_GL_CONTEXT_KHR, glContextHandle);
contextProps.addProperty(CL_WGL_HDC_KHR, surfaceHandle);
}
else if (glContext instanceof EGLContext)
{
long displayHandle = nativeSurface.getDisplayHandle();
contextProps.addProperty(CL_GL_CONTEXT_KHR, glContextHandle);
contextProps.addProperty(CL_EGL_DISPLAY_KHR, displayHandle);
}
log.debug("Creating context with props: {}", contextProps);
context = clCreateContext(contextProps, 1, new cl_device_id[]{device}, null, null, null);
log.debug("Created compute context {}", context);
}
private void initMacOS(GL4 gl)
{
// get sharegroup from gl context
GLContext glContext = gl.getContext();
if (!glContext.isCurrent())
{
throw new RuntimeException("Can't create context from inactive GL");
}
long cglContext = CGL.CGLGetCurrentContext();
long cglShareGroup = CGL.CGLGetShareGroup(cglContext);
// build context props
cl_context_properties contextProps = new cl_context_properties();
contextProps.addProperty(CL_CONTEXT_PROPERTY_USE_CGL_SHAREGROUP_APPLE, cglShareGroup);
// ask macos to make the context for us
log.debug("Creating context with props: {}", contextProps);
context = clCreateContext(contextProps, 0, null, null, null, null);
// pull the compute device out of the provided context
device = new cl_device_id();
clGetGLContextInfoAPPLE(context, cglContext, CL_CGL_DEVICE_FOR_CURRENT_VIRTUAL_SCREEN_APPLE, Sizeof.cl_device_id, Pointer.to(device), null);
log.debug("Got macOS CLGL compute device {}", device);
}
private void ensureMinWorkGroupSize()
{
long[] maxWorkGroupSize = new long[1];
clGetDeviceInfo(device, CL_DEVICE_MAX_WORK_GROUP_SIZE, Sizeof.size_t, Pointer.to(maxWorkGroupSize), null);
log.debug("Device CL_DEVICE_MAX_WORK_GROUP_SIZE: {}", maxWorkGroupSize[0]);
if (maxWorkGroupSize[0] < MIN_WORK_GROUP_SIZE)
{
throw new RuntimeException("Compute device does not support min work group size " + MIN_WORK_GROUP_SIZE);
}
// Largest power of 2 less than or equal to maxWorkGroupSize
int groupSize = 0x80000000 >>> Integer.numberOfLeadingZeros((int) maxWorkGroupSize[0]);
largeFaceCount = LARGE_SIZE / (Math.min(groupSize, LARGE_SIZE));
smallFaceCount = SMALL_SIZE / (Math.min(groupSize, SMALL_SIZE));
log.debug("Face counts: small: {}, large: {}", smallFaceCount, largeFaceCount);
}
private void initQueue()
{
long[] l = new long[1];
clGetDeviceInfo(device, CL_DEVICE_QUEUE_PROPERTIES, Sizeof.cl_long, Pointer.to(l), null);
commandQueue = clCreateCommandQueue(context, device, l[0] & CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE, null);
log.debug("Created command_queue {}, properties {}", commandQueue, l[0] & CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE);
}
private cl_program compileProgram(String programSource)
{
log.trace("Compiling program:\n {}", programSource);
cl_program program = clCreateProgramWithSource(context, 1, new String[]{programSource}, null, null);
try
{
clBuildProgram(program, 0, null, null, null, null);
}
catch (CLException e)
{
logBuildInfo(program, CL_PROGRAM_BUILD_LOG);
throw e;
}
logBuildInfo(program, CL_PROGRAM_BUILD_STATUS);
logBuildInfo(program, CL_PROGRAM_BINARY_TYPE);
logBuildInfo(program, CL_PROGRAM_BUILD_OPTIONS);
logBuildInfo(program, CL_PROGRAM_BUILD_LOG);
return program;
}
private cl_kernel getKernel(cl_program program, String kernelName)
{
cl_kernel kernel = clCreateKernel(program, kernelName, null);
log.debug("Loaded kernel {} for program {}", kernelName, program);
return kernel;
}
private void compilePrograms()
{
Template templateSmall = new Template()
.addInclude(OpenCLManager.class)
.add(key -> key.equals("FACE_COUNT") ? ("#define FACE_COUNT " + smallFaceCount) : null);
Template templateLarge = new Template()
.addInclude(OpenCLManager.class)
.add(key -> key.equals("FACE_COUNT") ? ("#define FACE_COUNT " + largeFaceCount) : null);
String unordered = new Template()
.addInclude(OpenCLManager.class)
.load("comp_unordered.cl");
String small = templateSmall.load("comp.cl");
String large = templateLarge.load("comp.cl");
programUnordered = compileProgram(unordered);
programSmall = compileProgram(small);
programLarge = compileProgram(large);
kernelUnordered = getKernel(programUnordered, KERNEL_NAME_UNORDERED);
kernelSmall = getKernel(programSmall, KERNEL_NAME_LARGE);
kernelLarge = getKernel(programLarge, KERNEL_NAME_LARGE);
}
void compute(int unorderedModels, int smallModels, int largeModels,
GLBuffer sceneVertexBuffer,
GLBuffer sceneUvBuffer,
GLBuffer vertexBuffer,
GLBuffer uvBuffer,
GLBuffer unorderedBuffer,
GLBuffer smallBuffer,
GLBuffer largeBuffer,
GLBuffer outVertexBuffer,
GLBuffer outUvBuffer,
GLBuffer uniformBuffer
)
{
cl_mem[] glBuffersAll = {
sceneVertexBuffer.cl_mem,
sceneUvBuffer.cl_mem,
unorderedBuffer.cl_mem,
smallBuffer.cl_mem,
largeBuffer.cl_mem,
vertexBuffer.cl_mem,
uvBuffer.cl_mem,
outVertexBuffer.cl_mem,
outUvBuffer.cl_mem,
uniformBuffer.cl_mem,
};
cl_mem[] glBuffers = Arrays.stream(glBuffersAll)
.filter(Objects::nonNull)
.toArray(cl_mem[]::new);
cl_event acquireGLBuffers = new cl_event();
clEnqueueAcquireGLObjects(commandQueue, glBuffers.length, glBuffers, 0, null, acquireGLBuffers);
cl_event[] computeEvents = {
new cl_event(),
new cl_event(),
new cl_event()
};
int numComputeEvents = 0;
if (unorderedModels > 0)
{
clSetKernelArg(kernelUnordered, 0, Sizeof.cl_mem, unorderedBuffer.ptr());
clSetKernelArg(kernelUnordered, 1, Sizeof.cl_mem, sceneVertexBuffer.ptr());
clSetKernelArg(kernelUnordered, 2, Sizeof.cl_mem, vertexBuffer.ptr());
clSetKernelArg(kernelUnordered, 3, Sizeof.cl_mem, sceneUvBuffer.ptr());
clSetKernelArg(kernelUnordered, 4, Sizeof.cl_mem, uvBuffer.ptr());
clSetKernelArg(kernelUnordered, 5, Sizeof.cl_mem, outVertexBuffer.ptr());
clSetKernelArg(kernelUnordered, 6, Sizeof.cl_mem, outUvBuffer.ptr());
// queue compute call after acquireGLBuffers
clEnqueueNDRangeKernel(commandQueue, kernelUnordered, 1, null,
new long[]{unorderedModels * 6L}, new long[]{6}, 1, new cl_event[]{acquireGLBuffers}, computeEvents[numComputeEvents++]);
}
if (smallModels > 0)
{
clSetKernelArg(kernelSmall, 0, (SHARED_SIZE + SMALL_SIZE) * Integer.BYTES, null);
clSetKernelArg(kernelSmall, 1, Sizeof.cl_mem, smallBuffer.ptr());
clSetKernelArg(kernelSmall, 2, Sizeof.cl_mem, sceneVertexBuffer.ptr());
clSetKernelArg(kernelSmall, 3, Sizeof.cl_mem, vertexBuffer.ptr());
clSetKernelArg(kernelSmall, 4, Sizeof.cl_mem, sceneUvBuffer.ptr());
clSetKernelArg(kernelSmall, 5, Sizeof.cl_mem, uvBuffer.ptr());
clSetKernelArg(kernelSmall, 6, Sizeof.cl_mem, outVertexBuffer.ptr());
clSetKernelArg(kernelSmall, 7, Sizeof.cl_mem, outUvBuffer.ptr());
clSetKernelArg(kernelSmall, 8, Sizeof.cl_mem, uniformBuffer.ptr());
clEnqueueNDRangeKernel(commandQueue, kernelSmall, 1, null,
new long[]{smallModels * (SMALL_SIZE / smallFaceCount)}, new long[]{SMALL_SIZE / smallFaceCount}, 1, new cl_event[]{acquireGLBuffers}, computeEvents[numComputeEvents++]);
}
if (largeModels > 0)
{
clSetKernelArg(kernelLarge, 0, (SHARED_SIZE + LARGE_SIZE) * Integer.BYTES, null);
clSetKernelArg(kernelLarge, 1, Sizeof.cl_mem, largeBuffer.ptr());
clSetKernelArg(kernelLarge, 2, Sizeof.cl_mem, sceneVertexBuffer.ptr());
clSetKernelArg(kernelLarge, 3, Sizeof.cl_mem, vertexBuffer.ptr());
clSetKernelArg(kernelLarge, 4, Sizeof.cl_mem, sceneUvBuffer.ptr());
clSetKernelArg(kernelLarge, 5, Sizeof.cl_mem, uvBuffer.ptr());
clSetKernelArg(kernelLarge, 6, Sizeof.cl_mem, outVertexBuffer.ptr());
clSetKernelArg(kernelLarge, 7, Sizeof.cl_mem, outUvBuffer.ptr());
clSetKernelArg(kernelLarge, 8, Sizeof.cl_mem, uniformBuffer.ptr());
clEnqueueNDRangeKernel(commandQueue, kernelLarge, 1, null,
new long[]{(long) largeModels * (LARGE_SIZE / largeFaceCount)}, new long[]{LARGE_SIZE / largeFaceCount}, 1, new cl_event[]{acquireGLBuffers}, computeEvents[numComputeEvents++]);
}
clEnqueueReleaseGLObjects(commandQueue, glBuffers.length, glBuffers, numComputeEvents, computeEvents, null);
}
void finish()
{
clFinish(commandQueue);
}
}

View File

@@ -75,12 +75,14 @@ public class GroundItemsOverlay extends Overlay
private static final Duration DESPAWN_TIME_INSTANCE = Duration.ofMinutes(30); private static final Duration DESPAWN_TIME_INSTANCE = Duration.ofMinutes(30);
private static final Duration DESPAWN_TIME_LOOT = Duration.ofMinutes(2); private static final Duration DESPAWN_TIME_LOOT = Duration.ofMinutes(2);
private static final Duration DESPAWN_TIME_DROP = Duration.ofMinutes(3); private static final Duration DESPAWN_TIME_DROP = Duration.ofMinutes(3);
private static final Duration DESPAWN_TIME_TABLE = Duration.ofMinutes(10);
private static final int KRAKEN_REGION = 9116; private static final int KRAKEN_REGION = 9116;
private static final int KBD_NMZ_REGION = 9033; private static final int KBD_NMZ_REGION = 9033;
private static final int ZILYANA_REGION = 11602; private static final int ZILYANA_REGION = 11602;
private static final int GRAARDOR_REGION = 11347; private static final int GRAARDOR_REGION = 11347;
private static final int KRIL_TSUTSAROTH_REGION = 11603; private static final int KRIL_TSUTSAROTH_REGION = 11603;
private static final int KREEARRA_REGION = 11346; private static final int KREEARRA_REGION = 11346;
private static final int NIGHTMARE_REGION = 15515;
private final Client client; private final Client client;
private final GroundItemsPlugin plugin; private final GroundItemsPlugin plugin;
@@ -397,8 +399,11 @@ public class GroundItemsOverlay extends Overlay
private Instant calculateDespawnTime(GroundItem groundItem) private Instant calculateDespawnTime(GroundItem groundItem)
{ {
// We can only accurately guess despawn times for our own pvm loot and dropped items // We can only accurately guess despawn times for our own pvm loot, dropped items,
if (groundItem.getLootType() != LootType.PVM && groundItem.getLootType() != LootType.DROPPED) // and items we placed on tables
if (groundItem.getLootType() != LootType.PVM
&& groundItem.getLootType() != LootType.DROPPED
&& groundItem.getLootType() != LootType.TABLE)
{ {
return null; return null;
} }
@@ -447,9 +452,9 @@ public class GroundItemsOverlay extends Overlay
} }
} }
else if (playerRegionID == ZILYANA_REGION || playerRegionID == GRAARDOR_REGION || else if (playerRegionID == ZILYANA_REGION || playerRegionID == GRAARDOR_REGION ||
playerRegionID == KRIL_TSUTSAROTH_REGION || playerRegionID == KREEARRA_REGION) playerRegionID == KRIL_TSUTSAROTH_REGION || playerRegionID == KREEARRA_REGION || playerRegionID == NIGHTMARE_REGION)
{ {
// GWD instances use the normal despawn timers // GWD and Nightmare instances use the normal despawn timers
despawnTime = spawnTime.plus(groundItem.getLootType() == LootType.DROPPED despawnTime = spawnTime.plus(groundItem.getLootType() == LootType.DROPPED
? DESPAWN_TIME_DROP ? DESPAWN_TIME_DROP
: DESPAWN_TIME_LOOT); : DESPAWN_TIME_LOOT);
@@ -461,9 +466,18 @@ public class GroundItemsOverlay extends Overlay
} }
else else
{ {
despawnTime = spawnTime.plus(groundItem.getLootType() == LootType.DROPPED switch (groundItem.getLootType())
? DESPAWN_TIME_DROP {
: DESPAWN_TIME_LOOT); case DROPPED:
despawnTime = spawnTime.plus(DESPAWN_TIME_DROP);
break;
case TABLE:
despawnTime = spawnTime.plus(DESPAWN_TIME_TABLE);
break;
default:
despawnTime = spawnTime.plus(DESPAWN_TIME_LOOT);
break;
}
} }
if (now.isBefore(spawnTime) || now.isAfter(despawnTime)) if (now.isBefore(spawnTime) || now.isAfter(despawnTime))
@@ -477,8 +491,11 @@ public class GroundItemsOverlay extends Overlay
private Color getItemTimerColor(GroundItem groundItem) private Color getItemTimerColor(GroundItem groundItem)
{ {
// We can only accurately guess despawn times for our own pvm loot and dropped items // We can only accurately guess despawn times for our own pvm loot, dropped items,
if (groundItem.getLootType() != LootType.PVM && groundItem.getLootType() != LootType.DROPPED) // and items we placed on tables
if (groundItem.getLootType() != LootType.PVM
&& groundItem.getLootType() != LootType.DROPPED
&& groundItem.getLootType() != LootType.TABLE)
{ {
return null; return null;
} }

View File

@@ -52,11 +52,13 @@ import lombok.Setter;
import lombok.Value; import lombok.Value;
import net.runelite.api.Client; import net.runelite.api.Client;
import net.runelite.api.GameState; import net.runelite.api.GameState;
import net.runelite.api.InventoryID;
import net.runelite.api.Item;
import net.runelite.api.ItemComposition; import net.runelite.api.ItemComposition;
import net.runelite.api.ItemContainer;
import net.runelite.api.ItemID; import net.runelite.api.ItemID;
import net.runelite.api.MenuAction; import net.runelite.api.MenuAction;
import net.runelite.api.MenuEntry; import net.runelite.api.MenuEntry;
import net.runelite.api.Player;
import net.runelite.api.Tile; import net.runelite.api.Tile;
import net.runelite.api.TileItem; import net.runelite.api.TileItem;
import net.runelite.api.coords.WorldPoint; import net.runelite.api.coords.WorldPoint;
@@ -180,6 +182,7 @@ public class GroundItemsPlugin extends Plugin
private LoadingCache<NamedQuantity, Boolean> highlightedItems; private LoadingCache<NamedQuantity, Boolean> highlightedItems;
private LoadingCache<NamedQuantity, Boolean> hiddenItems; private LoadingCache<NamedQuantity, Boolean> hiddenItems;
private final Queue<Integer> droppedItemQueue = EvictingQueue.create(16); // recently dropped items private final Queue<Integer> droppedItemQueue = EvictingQueue.create(16); // recently dropped items
private int lastUsedItem;
@Provides @Provides
GroundItemsConfig provideConfig(ConfigManager configManager) GroundItemsConfig provideConfig(ConfigManager configManager)
@@ -194,6 +197,7 @@ public class GroundItemsPlugin extends Plugin
mouseManager.registerMouseListener(inputListener); mouseManager.registerMouseListener(inputListener);
keyManager.registerKeyListener(inputListener); keyManager.registerKeyListener(inputListener);
executor.execute(this::reset); executor.execute(this::reset);
lastUsedItem = -1;
} }
@Override @Override
@@ -384,6 +388,7 @@ public class GroundItemsPlugin extends Plugin
final int realItemId = itemComposition.getNote() != -1 ? itemComposition.getLinkedNoteId() : itemId; final int realItemId = itemComposition.getNote() != -1 ? itemComposition.getLinkedNoteId() : itemId;
final int alchPrice = itemComposition.getHaPrice(); final int alchPrice = itemComposition.getHaPrice();
final boolean dropped = tile.getWorldLocation().equals(client.getLocalPlayer().getWorldLocation()) && droppedItemQueue.remove(itemId); final boolean dropped = tile.getWorldLocation().equals(client.getLocalPlayer().getWorldLocation()) && droppedItemQueue.remove(itemId);
final boolean table = itemId == lastUsedItem && tile.getItemLayer().getHeight() > 0;
final GroundItem groundItem = GroundItem.builder() final GroundItem groundItem = GroundItem.builder()
.id(itemId) .id(itemId)
@@ -394,12 +399,11 @@ public class GroundItemsPlugin extends Plugin
.haPrice(alchPrice) .haPrice(alchPrice)
.height(tile.getItemLayer().getHeight()) .height(tile.getItemLayer().getHeight())
.tradeable(itemComposition.isTradeable()) .tradeable(itemComposition.isTradeable())
.lootType(dropped ? LootType.DROPPED : LootType.UNKNOWN) .lootType(dropped ? LootType.DROPPED : (table ? LootType.TABLE : LootType.UNKNOWN))
.spawnTime(Instant.now()) .spawnTime(Instant.now())
.stackable(itemComposition.isStackable()) .stackable(itemComposition.isStackable())
.build(); .build();
// Update item price in case it is coins // Update item price in case it is coins
if (realItemId == COINS) if (realItemId == COINS)
{ {
@@ -638,11 +642,8 @@ public class GroundItemsPlugin extends Plugin
return; return;
} }
final Player local = client.getLocalPlayer();
final StringBuilder notificationStringBuilder = new StringBuilder() final StringBuilder notificationStringBuilder = new StringBuilder()
.append('[') .append("You received a ")
.append(local.getName())
.append("] received a ")
.append(dropType) .append(dropType)
.append(" drop: ") .append(" drop: ")
.append(item.getName()); .append(item.getName());
@@ -687,5 +688,21 @@ public class GroundItemsPlugin extends Plugin
// item spawns that are drops // item spawns that are drops
droppedItemQueue.add(itemId); droppedItemQueue.add(itemId);
} }
else if (menuOptionClicked.getMenuAction() == MenuAction.ITEM_USE_ON_GAME_OBJECT)
{
final ItemContainer inventory = client.getItemContainer(InventoryID.INVENTORY);
if (inventory == null)
{
return;
}
final Item clickedItem = inventory.getItem(menuOptionClicked.getSelectedItemIndex());
if (clickedItem == null)
{
return;
}
lastUsedItem = clickedItem.getId();
}
} }
} }

View File

@@ -27,6 +27,7 @@ package net.runelite.client.plugins.grounditems;
enum LootType enum LootType
{ {
UNKNOWN, UNKNOWN,
TABLE,
DROPPED, DROPPED,
PVP, PVP,
PVM; PVM;

View File

@@ -34,6 +34,7 @@ import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.inject.Inject; import javax.inject.Inject;
import lombok.AccessLevel; import lombok.AccessLevel;
@@ -313,21 +314,22 @@ public class GroundMarkerPlugin extends Plugin
WorldPoint worldPoint = WorldPoint.fromLocalInstance(client, localPoint); WorldPoint worldPoint = WorldPoint.fromLocalInstance(client, localPoint);
final int regionId = worldPoint.getRegionID(); final int regionId = worldPoint.getRegionID();
GroundMarkerPoint searchPoint = new GroundMarkerPoint(regionId, worldPoint.getRegionX(), worldPoint.getRegionY(), client.getPlane(), null, null);
Collection<GroundMarkerPoint> points = getPoints(regionId);
GroundMarkerPoint existing = points.stream()
.filter(p -> p.equals(searchPoint))
.findFirst().orElse(null);
if (existing == null)
{
return;
}
chatboxPanelManager.openTextInput("Tile label") chatboxPanelManager.openTextInput("Tile label")
.value(Optional.ofNullable(existing.getLabel()).orElse(""))
.onDone((input) -> .onDone((input) ->
{ {
input = Strings.emptyToNull(input); input = Strings.emptyToNull(input);
GroundMarkerPoint searchPoint = new GroundMarkerPoint(regionId, worldPoint.getRegionX(), worldPoint.getRegionY(), client.getPlane(), null, null);
Collection<GroundMarkerPoint> points = getPoints(regionId);
GroundMarkerPoint existing = points.stream()
.filter(p -> p.equals(searchPoint))
.findFirst().orElse(null);
if (existing == null)
{
return;
}
GroundMarkerPoint newPoint = new GroundMarkerPoint(regionId, worldPoint.getRegionX(), worldPoint.getRegionY(), client.getPlane(), existing.getColor(), input); GroundMarkerPoint newPoint = new GroundMarkerPoint(regionId, worldPoint.getRegionX(), worldPoint.getRegionY(), client.getPlane(), existing.getColor(), input);
points.remove(searchPoint); points.remove(searchPoint);
points.add(newPoint); points.add(newPoint);

View File

@@ -147,7 +147,7 @@ public class IdleNotifierPlugin extends Plugin
case COOKING_FIRE: case COOKING_FIRE:
case COOKING_RANGE: case COOKING_RANGE:
case COOKING_WINE: case COOKING_WINE:
/* Crafting(Gem Cutting, Glassblowing, Spinning, Battlestaves, Pottery) */ /* Crafting(Gem Cutting, Glassblowing, Spinning, Weaving, Battlestaves, Pottery) */
case GEM_CUTTING_OPAL: case GEM_CUTTING_OPAL:
case GEM_CUTTING_JADE: case GEM_CUTTING_JADE:
case GEM_CUTTING_REDTOPAZ: case GEM_CUTTING_REDTOPAZ:
@@ -158,6 +158,7 @@ public class IdleNotifierPlugin extends Plugin
case GEM_CUTTING_AMETHYST: case GEM_CUTTING_AMETHYST:
case CRAFTING_GLASSBLOWING: case CRAFTING_GLASSBLOWING:
case CRAFTING_SPINNING: case CRAFTING_SPINNING:
case CRAFTING_LOOM:
case CRAFTING_BATTLESTAVES: case CRAFTING_BATTLESTAVES:
case CRAFTING_LEATHER: case CRAFTING_LEATHER:
case CRAFTING_POTTERS_WHEEL: case CRAFTING_POTTERS_WHEEL:
@@ -268,6 +269,10 @@ public class IdleNotifierPlugin extends Plugin
case MAGIC_ENCHANTING_BOLTS: case MAGIC_ENCHANTING_BOLTS:
/* Prayer */ /* Prayer */
case USING_GILDED_ALTAR: case USING_GILDED_ALTAR:
case ECTOFUNTUS_FILL_SLIME_BUCKET:
case ECTOFUNTUS_INSERT_BONES:
case ECTOFUNTUS_GRIND_BONES:
case ECTOFUNTUS_EMPTY_BIN:
/* Farming */ /* Farming */
case FARMING_MIX_ULTRACOMPOST: case FARMING_MIX_ULTRACOMPOST:
case FARMING_HARVEST_BUSH: case FARMING_HARVEST_BUSH:
@@ -433,64 +438,64 @@ public class IdleNotifierPlugin extends Plugin
if (config.logoutIdle() && checkIdleLogout()) if (config.logoutIdle() && checkIdleLogout())
{ {
notifier.notify("[" + local.getName() + "] is about to log out from idling too long!"); notifier.notify("You are about to log out from idling too long!");
} }
if (check6hrLogout()) if (check6hrLogout())
{ {
notifier.notify("[" + local.getName() + "] is about to log out from being online for 6 hours!"); notifier.notify("You are about to log out from being online for 6 hours!");
} }
if (config.animationIdle() && checkAnimationIdle(waitDuration, local)) if (config.animationIdle() && checkAnimationIdle(waitDuration, local))
{ {
notifier.notify("[" + local.getName() + "] is now idle!"); notifier.notify("You are now idle!");
} }
if (config.movementIdle() && checkMovementIdle(waitDuration, local)) if (config.movementIdle() && checkMovementIdle(waitDuration, local))
{ {
notifier.notify("[" + local.getName() + "] has stopped moving!"); notifier.notify("You have stopped moving!");
} }
if (config.interactionIdle() && checkInteractionIdle(waitDuration, local)) if (config.interactionIdle() && checkInteractionIdle(waitDuration, local))
{ {
if (lastInteractWasCombat) if (lastInteractWasCombat)
{ {
notifier.notify("[" + local.getName() + "] is now out of combat!"); notifier.notify("You are now out of combat!");
} }
else else
{ {
notifier.notify("[" + local.getName() + "] is now idle!"); notifier.notify("You are now idle!");
} }
} }
if (checkLowHitpoints()) if (checkLowHitpoints())
{ {
notifier.notify("[" + local.getName() + "] has low hitpoints!"); notifier.notify("You have low hitpoints!");
} }
if (checkLowPrayer()) if (checkLowPrayer())
{ {
notifier.notify("[" + local.getName() + "] has low prayer!"); notifier.notify("You have low prayer!");
} }
if (checkLowEnergy()) if (checkLowEnergy())
{ {
notifier.notify("[" + local.getName() + "] has low run energy!"); notifier.notify("You have low run energy!");
} }
if (checkHighEnergy()) if (checkHighEnergy())
{ {
notifier.notify("[" + local.getName() + "] has restored run energy!"); notifier.notify("You have restored run energy!");
} }
if (checkLowOxygen()) if (checkLowOxygen())
{ {
notifier.notify("[" + local.getName() + "] has low oxygen!"); notifier.notify("You have low oxygen!");
} }
if (checkFullSpecEnergy()) if (checkFullSpecEnergy())
{ {
notifier.notify("[" + local.getName() + "] has restored spec energy!"); notifier.notify("You have restored spec energy!");
} }
} }

View File

@@ -144,6 +144,17 @@ public interface MenuEntrySwapperConfig extends Config
return false; return false;
} }
@ConfigItem(
keyName = "swapBattlestaves",
name = "Battlestaff",
description = "Swap Wield with Use on Battlestaves without orbs",
section = itemSection
)
default boolean swapBattlestaves()
{
return false;
}
@ConfigItem( @ConfigItem(
keyName = "swapPrayerBook", keyName = "swapPrayerBook",
name = "Recite-Prayer", name = "Recite-Prayer",

View File

@@ -334,6 +334,8 @@ public class MenuEntrySwapperPlugin extends Plugin
swap("bury", "use", config::swapBones); swap("bury", "use", config::swapBones);
swap("wield", "battlestaff", "use", config::swapBattlestaves);
swap("clean", "use", config::swapHerbs); swap("clean", "use", config::swapHerbs);
swap("read", "recite-prayer", config::swapPrayerBook); swap("read", "recite-prayer", config::swapPrayerBook);

View File

@@ -166,7 +166,7 @@ public class RegenMeterPlugin extends Plugin
if (config.getNotifyBeforeHpRegenSeconds() > 0 && currentHP < maxHP && shouldNotifyHpRegenThisTick(ticksPerHPRegen)) if (config.getNotifyBeforeHpRegenSeconds() > 0 && currentHP < maxHP && shouldNotifyHpRegenThisTick(ticksPerHPRegen))
{ {
notifier.notify("[" + client.getLocalPlayer().getName() + "] regenerates their next hitpoint soon!"); notifier.notify("Your next hitpoint will regenerate soon!");
} }
} }

View File

@@ -205,11 +205,23 @@ public interface ScreenshotConfig extends Config
return false; return false;
} }
@ConfigItem(
keyName = "valuableDropThreshold",
name = "Valuable Threshold",
description = "The minimum value to save screenshots of valuable drops.",
position = 14,
section = whatSection
)
default int valuableDropThreshold()
{
return 0;
}
@ConfigItem( @ConfigItem(
keyName = "untradeableDrop", keyName = "untradeableDrop",
name = "Screenshot Untradeable drops", name = "Screenshot Untradeable drops",
description = "Configures whether or not screenshots are automatically taken when you receive an untradeable drop.", description = "Configures whether or not screenshots are automatically taken when you receive an untradeable drop.",
position = 14, position = 15,
section = whatSection section = whatSection
) )
default boolean screenshotUntradeableDrop() default boolean screenshotUntradeableDrop()
@@ -221,7 +233,7 @@ public interface ScreenshotConfig extends Config
keyName = "ccKick", keyName = "ccKick",
name = "Screenshot Kicks from FC", name = "Screenshot Kicks from FC",
description = "Take a screenshot when you kick a user from a friends chat.", description = "Take a screenshot when you kick a user from a friends chat.",
position = 15, position = 16,
section = whatSection section = whatSection
) )
default boolean screenshotKick() default boolean screenshotKick()
@@ -233,7 +245,7 @@ public interface ScreenshotConfig extends Config
keyName = "baHighGamble", keyName = "baHighGamble",
name = "Screenshot BA high gambles", name = "Screenshot BA high gambles",
description = "Take a screenshot of your reward from a high gamble at Barbarian Assault.", description = "Take a screenshot of your reward from a high gamble at Barbarian Assault.",
position = 16, position = 17,
section = whatSection section = whatSection
) )
default boolean screenshotHighGamble() default boolean screenshotHighGamble()
@@ -245,7 +257,7 @@ public interface ScreenshotConfig extends Config
keyName = "hotkey", keyName = "hotkey",
name = "Screenshot hotkey", name = "Screenshot hotkey",
description = "When you press this key a screenshot will be taken", description = "When you press this key a screenshot will be taken",
position = 17 position = 18
) )
default Keybind hotkey() default Keybind hotkey()
{ {

View File

@@ -99,7 +99,7 @@ public class ScreenshotPlugin extends Plugin
private static final Pattern NUMBER_PATTERN = Pattern.compile("([0-9]+)"); private static final Pattern NUMBER_PATTERN = Pattern.compile("([0-9]+)");
private static final Pattern LEVEL_UP_PATTERN = Pattern.compile(".*Your ([a-zA-Z]+) (?:level is|are)? now (\\d+)\\."); private static final Pattern LEVEL_UP_PATTERN = Pattern.compile(".*Your ([a-zA-Z]+) (?:level is|are)? now (\\d+)\\.");
private static final Pattern BOSSKILL_MESSAGE_PATTERN = Pattern.compile("Your (.+) kill count is: <col=ff0000>(\\d+)</col>."); private static final Pattern BOSSKILL_MESSAGE_PATTERN = Pattern.compile("Your (.+) kill count is: <col=ff0000>(\\d+)</col>.");
private static final Pattern VALUABLE_DROP_PATTERN = Pattern.compile(".*Valuable drop: ([^<>]+)(?:</col>)?"); private static final Pattern VALUABLE_DROP_PATTERN = Pattern.compile(".*Valuable drop: ([^<>]+?\\(((?:\\d+,?)+) coins\\))(?:</col>)?");
private static final Pattern UNTRADEABLE_DROP_PATTERN = Pattern.compile(".*Untradeable drop: ([^<>]+)(?:</col>)?"); private static final Pattern UNTRADEABLE_DROP_PATTERN = Pattern.compile(".*Untradeable drop: ([^<>]+)(?:</col>)?");
private static final Pattern DUEL_END_PATTERN = Pattern.compile("You have now (won|lost) ([0-9]+) duels?\\."); private static final Pattern DUEL_END_PATTERN = Pattern.compile("You have now (won|lost) ([0-9]+) duels?\\.");
private static final Pattern QUEST_PATTERN_1 = Pattern.compile(".+?ve\\.*? (?<verb>been|rebuilt|.+?ed)? ?(?:the )?'?(?<quest>.+?)'?(?: [Qq]uest)?[!.]?$"); private static final Pattern QUEST_PATTERN_1 = Pattern.compile(".+?ve\\.*? (?<verb>been|rebuilt|.+?ed)? ?(?:the )?'?(?<quest>.+?)'?(?: [Qq]uest)?[!.]?$");
@@ -417,9 +417,13 @@ public class ScreenshotPlugin extends Plugin
Matcher m = VALUABLE_DROP_PATTERN.matcher(chatMessage); Matcher m = VALUABLE_DROP_PATTERN.matcher(chatMessage);
if (m.matches()) if (m.matches())
{ {
String valuableDropName = m.group(1); int valuableDropValue = Integer.parseInt(m.group(2).replaceAll(",", ""));
String fileName = "Valuable drop " + valuableDropName; if (valuableDropValue >= config.valuableDropThreshold())
takeScreenshot(fileName, "Valuable Drops"); {
String valuableDropName = m.group(1);
String fileName = "Valuable drop " + valuableDropName;
takeScreenshot(fileName, "Valuable Drops");
}
} }
} }

View File

@@ -30,6 +30,8 @@ import java.awt.Color;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.event.FocusAdapter; import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent; import java.awt.event.FocusEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter; import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.text.DecimalFormat; import java.text.DecimalFormat;
@@ -95,7 +97,14 @@ class SkillCalculator extends JPanel
searchBar.setBackground(ColorScheme.DARKER_GRAY_COLOR); searchBar.setBackground(ColorScheme.DARKER_GRAY_COLOR);
searchBar.setHoverBackgroundColor(ColorScheme.DARK_GRAY_HOVER_COLOR); searchBar.setHoverBackgroundColor(ColorScheme.DARK_GRAY_HOVER_COLOR);
searchBar.addClearListener(this::onSearch); searchBar.addClearListener(this::onSearch);
searchBar.addKeyListener(e -> onSearch()); searchBar.addKeyListener(new KeyAdapter()
{
@Override
public void keyTyped(KeyEvent e)
{
onSearch();
}
});
setLayout(new DynamicGridLayout(0, 1, 0, 5)); setLayout(new DynamicGridLayout(0, 1, 0, 5));

View File

@@ -202,6 +202,7 @@ enum Task
"Fremennik Slayer Dungeon", "Fremennik Slayer Dungeon",
"God Wars Dungeon", "God Wars Dungeon",
"Iorwerth Dungeon", "Iorwerth Dungeon",
"Isle of Souls",
"Jormungand's Prison", "Jormungand's Prison",
"Kalphite Lair", "Kalphite Lair",
"Karuulm Slayer Dungeon", "Karuulm Slayer Dungeon",

View File

@@ -89,6 +89,7 @@ public class TimersPlugin extends Plugin
private static final String CANNON_FURNACE_MESSAGE = "You add the furnace."; private static final String CANNON_FURNACE_MESSAGE = "You add the furnace.";
private static final String CANNON_PICKUP_MESSAGE = "You pick up the cannon. It's really heavy."; private static final String CANNON_PICKUP_MESSAGE = "You pick up the cannon. It's really heavy.";
private static final String CANNON_REPAIR_MESSAGE = "You repair your cannon, restoring it to working order."; private static final String CANNON_REPAIR_MESSAGE = "You repair your cannon, restoring it to working order.";
private static final String CANNON_DESTROYED_MESSAGE = "Your cannon has been destroyed!";
private static final String CHARGE_EXPIRED_MESSAGE = "<col=ef1020>Your magical charge fades away.</col>"; private static final String CHARGE_EXPIRED_MESSAGE = "<col=ef1020>Your magical charge fades away.</col>";
private static final String CHARGE_MESSAGE = "<col=ef1020>You feel charged with magic power.</col>"; private static final String CHARGE_MESSAGE = "<col=ef1020>You feel charged with magic power.</col>";
private static final String EXTENDED_ANTIFIRE_DRINK_MESSAGE = "You drink some of your extended antifire potion."; private static final String EXTENDED_ANTIFIRE_DRINK_MESSAGE = "You drink some of your extended antifire potion.";
@@ -517,7 +518,7 @@ public class TimersPlugin extends Plugin
cannonTimer.setTooltip(cannonTimer.getTooltip() + " - World " + client.getWorld()); cannonTimer.setTooltip(cannonTimer.getTooltip() + " - World " + client.getWorld());
} }
if (config.showCannon() && message.equals(CANNON_PICKUP_MESSAGE)) if (config.showCannon() && (message.equals(CANNON_PICKUP_MESSAGE) || message.equals(CANNON_DESTROYED_MESSAGE)))
{ {
removeGameTimer(CANNON); removeGameTimer(CANNON);
} }

View File

@@ -77,7 +77,7 @@ public abstract class TabContentPanel extends JPanel
LocalDateTime currentTime = LocalDateTime.now(); LocalDateTime currentTime = LocalDateTime.now();
if (endTime.getDayOfWeek() != currentTime.getDayOfWeek()) if (endTime.getDayOfWeek() != currentTime.getDayOfWeek())
{ {
sb.append(endTime.getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.getDefault())).append(" "); sb.append(endTime.getDayOfWeek().getDisplayName(TextStyle.SHORT, Locale.getDefault())).append(" ");
} }
sb.append("at "); sb.append("at ");
sb.append(formatter.format(endTime)); sb.append(formatter.format(endTime));

View File

@@ -41,6 +41,7 @@ public interface TimeTrackingConfig extends Config
String TIMERS = "timers"; String TIMERS = "timers";
String STOPWATCHES = "stopwatches"; String STOPWATCHES = "stopwatches";
String PREFER_SOONEST = "preferSoonest"; String PREFER_SOONEST = "preferSoonest";
String NOTIFY = "notify";
@ConfigItem( @ConfigItem(
keyName = "timeFormatMode", keyName = "timeFormatMode",

View File

@@ -29,7 +29,6 @@ import com.google.inject.Inject;
import com.google.inject.Provides; import com.google.inject.Provides;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.time.Instant; import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@@ -58,7 +57,6 @@ import net.runelite.client.plugins.timetracking.clocks.ClockManager;
import net.runelite.client.plugins.timetracking.farming.FarmingContractManager; import net.runelite.client.plugins.timetracking.farming.FarmingContractManager;
import net.runelite.client.plugins.timetracking.farming.FarmingTracker; import net.runelite.client.plugins.timetracking.farming.FarmingTracker;
import net.runelite.client.plugins.timetracking.hunter.BirdHouseTracker; import net.runelite.client.plugins.timetracking.hunter.BirdHouseTracker;
import net.runelite.client.task.Schedule;
import net.runelite.client.ui.ClientToolbar; import net.runelite.client.ui.ClientToolbar;
import net.runelite.client.ui.NavigationButton; import net.runelite.client.ui.NavigationButton;
import net.runelite.client.ui.overlay.infobox.InfoBoxManager; import net.runelite.client.ui.overlay.infobox.InfoBoxManager;
@@ -102,6 +100,8 @@ public class TimeTrackingPlugin extends Plugin
private ScheduledFuture panelUpdateFuture; private ScheduledFuture panelUpdateFuture;
private ScheduledFuture notifierFuture;
private TimeTrackingPanel panel; private TimeTrackingPanel panel;
private NavigationButton navButton; private NavigationButton navButton;
@@ -139,6 +139,7 @@ public class TimeTrackingPlugin extends Plugin
clientToolbar.addNavigation(navButton); clientToolbar.addNavigation(navButton);
panelUpdateFuture = executorService.scheduleAtFixedRate(this::updatePanel, 200, 200, TimeUnit.MILLISECONDS); panelUpdateFuture = executorService.scheduleAtFixedRate(this::updatePanel, 200, 200, TimeUnit.MILLISECONDS);
notifierFuture = executorService.scheduleAtFixedRate(this::checkCompletion, 10, 10, TimeUnit.SECONDS);
} }
@Override @Override
@@ -153,6 +154,7 @@ public class TimeTrackingPlugin extends Plugin
panelUpdateFuture = null; panelUpdateFuture = null;
} }
notifierFuture.cancel(true);
clientToolbar.removeNavigation(navButton); clientToolbar.removeNavigation(navButton);
infoBoxManager.removeInfoBox(farmingContractManager.getInfoBox()); infoBoxManager.removeInfoBox(farmingContractManager.getInfoBox());
farmingContractManager.setInfoBox(null); farmingContractManager.setInfoBox(null);
@@ -260,8 +262,7 @@ public class TimeTrackingPlugin extends Plugin
} }
} }
@Schedule(period = 10, unit = ChronoUnit.SECONDS) private void checkCompletion()
public void checkCompletion()
{ {
boolean birdHouseDataChanged = birdHouseTracker.checkCompletion(); boolean birdHouseDataChanged = birdHouseTracker.checkCompletion();
@@ -269,6 +270,8 @@ public class TimeTrackingPlugin extends Plugin
{ {
panel.update(); panel.update();
} }
farmingTracker.checkCompletion();
} }
private void updatePanel() private void updatePanel()

View File

@@ -29,22 +29,29 @@ import java.awt.BorderLayout;
import java.awt.Color; import java.awt.Color;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.GridLayout; import java.awt.GridLayout;
import javax.swing.ImageIcon;
import javax.swing.JLabel; import javax.swing.JLabel;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.JToggleButton;
import javax.swing.border.EmptyBorder; import javax.swing.border.EmptyBorder;
import lombok.Getter; import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Constants; import net.runelite.api.Constants;
import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.FontManager; import net.runelite.client.ui.FontManager;
import net.runelite.client.ui.components.ThinProgressBar; import net.runelite.client.ui.components.ThinProgressBar;
import net.runelite.client.ui.components.shadowlabel.JShadowedLabel; import net.runelite.client.ui.components.shadowlabel.JShadowedLabel;
import net.runelite.client.util.ImageUtil;
import net.runelite.client.util.SwingUtil;
@Slf4j
@Getter @Getter
public class TimeablePanel<T> extends JPanel public class TimeablePanel<T> extends JPanel
{ {
private final T timeable; private final T timeable;
private final JLabel icon = new JLabel(); private final JLabel icon = new JLabel();
private final JLabel farmingContractIcon = new JLabel(); private final JLabel farmingContractIcon = new JLabel();
private final JToggleButton notifyButton = new JToggleButton();
private final JLabel estimate = new JLabel(); private final JLabel estimate = new JLabel();
private final ThinProgressBar progress = new ThinProgressBar(); private final ThinProgressBar progress = new ThinProgressBar();
private final JLabel text; private final JLabel text;
@@ -79,8 +86,29 @@ public class TimeablePanel<T> extends JPanel
infoPanel.add(text); infoPanel.add(text);
infoPanel.add(estimate); infoPanel.add(estimate);
ImageIcon notifyIcon = new ImageIcon(ImageUtil.loadImageResource(TimeTrackingPlugin.class, "notify_icon.png"));
ImageIcon notifySelectedIcon = new ImageIcon(ImageUtil.loadImageResource(TimeTrackingPlugin.class, "notify_selected_icon.png"));
notifyButton.setPreferredSize(new Dimension(30, 16));
notifyButton.setBorder(new EmptyBorder(0, 0, 0, 10));
notifyButton.setIcon(notifyIcon);
notifyButton.setSelectedIcon(notifySelectedIcon);
SwingUtil.removeButtonDecorations(notifyButton);
SwingUtil.addModalTooltip(notifyButton, "Disable notifications", "Enable notifications");
JPanel notifyPanel = new JPanel();
notifyPanel.setLayout(new BorderLayout());
notifyPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR);
notifyPanel.add(notifyButton, BorderLayout.CENTER);
JPanel iconPanel = new JPanel();
iconPanel.setLayout(new BorderLayout());
iconPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR);
iconPanel.add(notifyPanel, BorderLayout.EAST);
iconPanel.add(farmingContractIcon, BorderLayout.WEST);
topContainer.add(iconPanel, BorderLayout.EAST);
topContainer.add(icon, BorderLayout.WEST); topContainer.add(icon, BorderLayout.WEST);
topContainer.add(farmingContractIcon, BorderLayout.EAST);
topContainer.add(infoPanel, BorderLayout.CENTER); topContainer.add(infoPanel, BorderLayout.CENTER);
progress.setValue(0); progress.setValue(0);

View File

@@ -29,6 +29,7 @@ import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.Setter; import lombok.Setter;
import net.runelite.api.Varbits; import net.runelite.api.Varbits;
import net.runelite.client.plugins.timetracking.TimeTrackingConfig;
@RequiredArgsConstructor( @RequiredArgsConstructor(
access = AccessLevel.PACKAGE access = AccessLevel.PACKAGE
@@ -41,4 +42,14 @@ class FarmingPatch
private final String name; private final String name;
private final Varbits varbit; private final Varbits varbit;
private final PatchImplementation implementation; private final PatchImplementation implementation;
}
String configKey()
{
return region.getRegionID() + "." + varbit.getId();
}
String notifyConfigKey()
{
return TimeTrackingConfig.NOTIFY + "." + region.getRegionID() + "." + varbit.getId();
}
}

View File

@@ -33,13 +33,15 @@ public class FarmingRegion
{ {
private final String name; private final String name;
private final int regionID; private final int regionID;
private final boolean definite;
private final FarmingPatch[] patches; private final FarmingPatch[] patches;
private final Varbits[] varbits; private final Varbits[] varbits;
FarmingRegion(String name, int regionID, FarmingPatch... patches) FarmingRegion(String name, int regionID, boolean definite, FarmingPatch... patches)
{ {
this.name = name; this.name = name;
this.regionID = regionID; this.regionID = regionID;
this.definite = definite;
this.patches = patches; this.patches = patches;
this.varbits = new Varbits[patches.length]; this.varbits = new Varbits[patches.length];
for (int i = 0; i < patches.length; i++) for (int i = 0; i < patches.length; i++)

View File

@@ -33,8 +33,11 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import javax.swing.JLabel; import javax.swing.JLabel;
import javax.swing.JToggleButton;
import javax.swing.border.EmptyBorder; import javax.swing.border.EmptyBorder;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.ItemID; import net.runelite.api.ItemID;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.game.ItemManager; import net.runelite.client.game.ItemManager;
import net.runelite.client.plugins.timetracking.TabContentPanel; import net.runelite.client.plugins.timetracking.TabContentPanel;
import net.runelite.client.plugins.timetracking.TimeTrackingConfig; import net.runelite.client.plugins.timetracking.TimeTrackingConfig;
@@ -42,10 +45,12 @@ import net.runelite.client.plugins.timetracking.TimeablePanel;
import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.FontManager; import net.runelite.client.ui.FontManager;
@Slf4j
public class FarmingTabPanel extends TabContentPanel public class FarmingTabPanel extends TabContentPanel
{ {
private final FarmingTracker farmingTracker; private final FarmingTracker farmingTracker;
private final ItemManager itemManager; private final ItemManager itemManager;
private final ConfigManager configManager;
private final TimeTrackingConfig config; private final TimeTrackingConfig config;
private final List<TimeablePanel<FarmingPatch>> patchPanels; private final List<TimeablePanel<FarmingPatch>> patchPanels;
private final FarmingContractManager farmingContractManager; private final FarmingContractManager farmingContractManager;
@@ -53,6 +58,7 @@ public class FarmingTabPanel extends TabContentPanel
FarmingTabPanel( FarmingTabPanel(
FarmingTracker farmingTracker, FarmingTracker farmingTracker,
ItemManager itemManager, ItemManager itemManager,
ConfigManager configManager,
TimeTrackingConfig config, TimeTrackingConfig config,
Set<FarmingPatch> patches, Set<FarmingPatch> patches,
FarmingContractManager farmingContractManager FarmingContractManager farmingContractManager
@@ -60,6 +66,7 @@ public class FarmingTabPanel extends TabContentPanel
{ {
this.farmingTracker = farmingTracker; this.farmingTracker = farmingTracker;
this.itemManager = itemManager; this.itemManager = itemManager;
this.configManager = configManager;
this.config = config; this.config = config;
this.patchPanels = new ArrayList<>(); this.patchPanels = new ArrayList<>();
this.farmingContractManager = farmingContractManager; this.farmingContractManager = farmingContractManager;
@@ -103,6 +110,18 @@ public class FarmingTabPanel extends TabContentPanel
lastImpl = patch.getImplementation(); lastImpl = patch.getImplementation();
} }
// Set toggle state of notification menu on icon click;
JToggleButton toggleNotify = p.getNotifyButton();
String configKey = patch.notifyConfigKey();
toggleNotify.addActionListener(e ->
{
if (configManager.getRSProfileKey() != null)
{
configManager.setRSProfileConfiguration(TimeTrackingConfig.CONFIG_GROUP, configKey, toggleNotify.isSelected());
}
});
patchPanels.add(p); patchPanels.add(p);
add(p, c); add(p, c);
c.gridy++; c.gridy++;
@@ -197,18 +216,26 @@ public class FarmingTabPanel extends TabContentPanel
{ {
panel.getProgress().setVisible(false); panel.getProgress().setVisible(false);
} }
JLabel farmingContractIcon = panel.getFarmingContractIcon();
if (farmingContractManager.shouldHighlightFarmingTabPanel(patch))
{
itemManager.getImage(ItemID.SEED_PACK).addTo(farmingContractIcon);
farmingContractIcon.setToolTipText(farmingContractManager.getContract().getName());
}
else
{
farmingContractIcon.setIcon(null);
farmingContractIcon.setToolTipText("");
}
} }
JLabel farmingContractIcon = panel.getFarmingContractIcon();
if (farmingContractManager.shouldHighlightFarmingTabPanel(patch))
{
itemManager.getImage(ItemID.SEED_PACK).addTo(farmingContractIcon);
farmingContractIcon.setToolTipText(farmingContractManager.getContract().getName());
}
else
{
farmingContractIcon.setIcon(null);
farmingContractIcon.setToolTipText("");
}
String configKey = patch.notifyConfigKey();
JToggleButton toggleNotify = panel.getNotifyButton();
boolean notifyEnabled = Boolean.TRUE
.equals(configManager.getRSProfileConfiguration(TimeTrackingConfig.CONFIG_GROUP, configKey, Boolean.class));
toggleNotify.setSelected(notifyEnabled);
} }
} }
} }

View File

@@ -24,25 +24,34 @@
*/ */
package net.runelite.client.plugins.timetracking.farming; package net.runelite.client.plugins.timetracking.farming;
import com.google.common.annotations.VisibleForTesting;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
import java.time.Instant; import java.time.Instant;
import java.util.Collection; import java.util.Collection;
import java.util.EnumMap; import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client; import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.Varbits; import net.runelite.api.Varbits;
import net.runelite.api.coords.WorldPoint; import net.runelite.api.coords.WorldPoint;
import net.runelite.api.vars.Autoweed; import net.runelite.api.vars.Autoweed;
import net.runelite.api.widgets.WidgetModalMode; import net.runelite.api.widgets.WidgetModalMode;
import net.runelite.client.Notifier;
import net.runelite.client.config.ConfigManager; import net.runelite.client.config.ConfigManager;
import net.runelite.client.config.RuneScapeProfile;
import net.runelite.client.config.RuneScapeProfileType;
import net.runelite.client.game.ItemManager; import net.runelite.client.game.ItemManager;
import net.runelite.client.plugins.timetracking.SummaryState; import net.runelite.client.plugins.timetracking.SummaryState;
import net.runelite.client.plugins.timetracking.Tab; import net.runelite.client.plugins.timetracking.Tab;
import net.runelite.client.plugins.timetracking.TimeTrackingConfig; import net.runelite.client.plugins.timetracking.TimeTrackingConfig;
import net.runelite.client.util.Text;
@Slf4j @Slf4j
@Singleton @Singleton
@@ -53,6 +62,7 @@ public class FarmingTracker
private final ConfigManager configManager; private final ConfigManager configManager;
private final TimeTrackingConfig config; private final TimeTrackingConfig config;
private final FarmingWorld farmingWorld; private final FarmingWorld farmingWorld;
private final Notifier notifier;
private final Map<Tab, SummaryState> summaries = new EnumMap<>(Tab.class); private final Map<Tab, SummaryState> summaries = new EnumMap<>(Tab.class);
@@ -61,23 +71,26 @@ public class FarmingTracker
* or {@code -1} if we have no data about any patch of the given type. * or {@code -1} if we have no data about any patch of the given type.
*/ */
private final Map<Tab, Long> completionTimes = new EnumMap<>(Tab.class); private final Map<Tab, Long> completionTimes = new EnumMap<>(Tab.class);
Map<ProfilePatch, Boolean> wasNotified = new HashMap<>();
private boolean newRegionLoaded; private boolean newRegionLoaded;
private Collection<FarmingRegion> lastRegions; private Collection<FarmingRegion> lastRegions;
private boolean firstNotifyCheck = true;
@Inject @Inject
private FarmingTracker(Client client, ItemManager itemManager, ConfigManager configManager, TimeTrackingConfig config, FarmingWorld farmingWorld) private FarmingTracker(Client client, ItemManager itemManager, ConfigManager configManager, TimeTrackingConfig config, FarmingWorld farmingWorld, Notifier notifier)
{ {
this.client = client; this.client = client;
this.itemManager = itemManager; this.itemManager = itemManager;
this.configManager = configManager; this.configManager = configManager;
this.config = config; this.config = config;
this.farmingWorld = farmingWorld; this.farmingWorld = farmingWorld;
this.notifier = notifier;
} }
public FarmingTabPanel createTabPanel(Tab tab, FarmingContractManager farmingContractManager) public FarmingTabPanel createTabPanel(Tab tab, FarmingContractManager farmingContractManager)
{ {
return new FarmingTabPanel(this, itemManager, config, farmingWorld.getTabs().get(tab), farmingContractManager); return new FarmingTabPanel(this, itemManager, configManager, config, farmingWorld.getTabs().get(tab), farmingContractManager);
} }
/** /**
@@ -130,7 +143,7 @@ public class FarmingTracker
{ {
// Write the config value if it doesn't match what is current, or it is more than 5 minutes old // Write the config value if it doesn't match what is current, or it is more than 5 minutes old
Varbits varbit = patch.getVarbit(); Varbits varbit = patch.getVarbit();
String key = region.getRegionID() + "." + varbit.getId(); String key = patch.configKey();
String strVarbit = Integer.toString(client.getVar(varbit)); String strVarbit = Integer.toString(client.getVar(varbit));
String storedValue = configManager.getRSProfileConfiguration(TimeTrackingConfig.CONFIG_GROUP, key); String storedValue = configManager.getRSProfileConfiguration(TimeTrackingConfig.CONFIG_GROUP, key);
@@ -189,6 +202,12 @@ public class FarmingTracker
configManager.setRSProfileConfiguration(TimeTrackingConfig.CONFIG_GROUP, TimeTrackingConfig.FARM_TICK_OFFSET, offsetMins); configManager.setRSProfileConfiguration(TimeTrackingConfig.CONFIG_GROUP, TimeTrackingConfig.FARM_TICK_OFFSET, offsetMins);
} }
} }
if (currentPatchState.getTickRate() != 0
// Don't set wasNotified to false if witnessing a check-health action
&& !(previousPatchState.getCropState() == CropState.GROWING && currentPatchState.getCropState() == CropState.HARVESTABLE && currentPatchState.getProduce().getPatchImplementation().isHealthCheckRequired()))
{
wasNotified.put(new ProfilePatch(patch, configManager.getRSProfileKey()), false);
}
} }
else else
{ {
@@ -258,17 +277,23 @@ public class FarmingTracker
@Nullable @Nullable
public PatchPrediction predictPatch(FarmingPatch patch) public PatchPrediction predictPatch(FarmingPatch patch)
{
return predictPatch(patch, configManager.getRSProfileKey());
}
@Nullable
public PatchPrediction predictPatch(FarmingPatch patch, String profile)
{ {
long unixNow = Instant.now().getEpochSecond(); long unixNow = Instant.now().getEpochSecond();
boolean autoweed = Integer.toString(Autoweed.ON.ordinal()) boolean autoweed = Integer.toString(Autoweed.ON.ordinal())
.equals(configManager.getRSProfileConfiguration(TimeTrackingConfig.CONFIG_GROUP, TimeTrackingConfig.AUTOWEED)); .equals(configManager.getConfiguration(TimeTrackingConfig.CONFIG_GROUP, profile, TimeTrackingConfig.AUTOWEED));
boolean botanist = Boolean.TRUE boolean botanist = Boolean.TRUE
.equals(configManager.getRSProfileConfiguration(TimeTrackingConfig.CONFIG_GROUP, TimeTrackingConfig.BOTANIST, Boolean.class)); .equals(configManager.getConfiguration(TimeTrackingConfig.CONFIG_GROUP, profile, TimeTrackingConfig.BOTANIST, Boolean.class));
String key = patch.getRegion().getRegionID() + "." + patch.getVarbit().getId(); String key = patch.configKey();
String storedValue = configManager.getRSProfileConfiguration(TimeTrackingConfig.CONFIG_GROUP, key); String storedValue = configManager.getConfiguration(TimeTrackingConfig.CONFIG_GROUP, profile, key);
if (storedValue == null) if (storedValue == null)
{ {
@@ -323,11 +348,11 @@ public class FarmingTracker
long doneEstimate = 0; long doneEstimate = 0;
if (tickrate > 0) if (tickrate > 0)
{ {
long tickNow = getTickTime(tickrate, 0, unixNow); long tickNow = getTickTime(tickrate, 0, unixNow, profile);
long tickTime = getTickTime(tickrate, 0, unixTime); long tickTime = getTickTime(tickrate, 0, unixTime, profile);
int delta = (int) (tickNow - tickTime) / (tickrate * 60); int delta = (int) (tickNow - tickTime) / (tickrate * 60);
doneEstimate = getTickTime(tickrate, stages - 1 - stage, tickTime); doneEstimate = getTickTime(tickrate, stages - 1 - stage, tickTime, profile);
stage += delta; stage += delta;
if (stage >= stages) if (stage >= stages)
@@ -347,13 +372,13 @@ public class FarmingTracker
public long getTickTime(int tickRate, int ticks) public long getTickTime(int tickRate, int ticks)
{ {
return getTickTime(tickRate, ticks, Instant.now().getEpochSecond()); return getTickTime(tickRate, ticks, Instant.now().getEpochSecond(), configManager.getRSProfileKey());
} }
public long getTickTime(int tickRate, int ticks, long requestedTime) public long getTickTime(int tickRate, int ticks, long requestedTime, String profile)
{ {
Integer offsetPrecisionMins = configManager.getRSProfileConfiguration(TimeTrackingConfig.CONFIG_GROUP, TimeTrackingConfig.FARM_TICK_OFFSET_PRECISION, int.class); Integer offsetPrecisionMins = configManager.getConfiguration(TimeTrackingConfig.CONFIG_GROUP, profile, TimeTrackingConfig.FARM_TICK_OFFSET_PRECISION, int.class);
Integer offsetTimeMins = configManager.getRSProfileConfiguration(TimeTrackingConfig.CONFIG_GROUP, TimeTrackingConfig.FARM_TICK_OFFSET, int.class); Integer offsetTimeMins = configManager.getConfiguration(TimeTrackingConfig.CONFIG_GROUP, profile, TimeTrackingConfig.FARM_TICK_OFFSET, int.class);
//All offsets are negative but are stored as positive //All offsets are negative but are stored as positive
long calculatedOffsetTime = 0L; long calculatedOffsetTime = 0L;
@@ -465,4 +490,133 @@ public class FarmingTracker
completionTimes.put(tab.getKey(), completionTime); completionTimes.put(tab.getKey(), completionTime);
} }
} }
public void checkCompletion()
{
List<RuneScapeProfile> rsProfiles = configManager.getRSProfiles();
long unixNow = Instant.now().getEpochSecond();
for (RuneScapeProfile profile : rsProfiles)
{
Integer offsetPrecisionMins = configManager.getConfiguration(TimeTrackingConfig.CONFIG_GROUP, profile.getKey(), TimeTrackingConfig.FARM_TICK_OFFSET_PRECISION, int.class);
Integer offsetTimeMins = configManager.getConfiguration(TimeTrackingConfig.CONFIG_GROUP, profile.getKey(), TimeTrackingConfig.FARM_TICK_OFFSET, int.class);
for (Map.Entry<Tab, Set<FarmingPatch>> tab : farmingWorld.getTabs().entrySet())
{
for (FarmingPatch patch : tab.getValue())
{
ProfilePatch profilePatch = new ProfilePatch(patch, profile.getKey());
boolean patchNotified = wasNotified.getOrDefault(profilePatch, false);
String configKey = patch.notifyConfigKey();
boolean shouldNotify = Boolean.TRUE
.equals(configManager.getConfiguration(TimeTrackingConfig.CONFIG_GROUP, profile.getKey(), configKey, Boolean.class));
PatchPrediction prediction = predictPatch(patch, profile.getKey());
if (prediction == null)
{
continue;
}
int tickRate = prediction.getProduce().getTickrate();
if (offsetPrecisionMins == null || offsetTimeMins == null || (offsetPrecisionMins < tickRate && offsetPrecisionMins < 40) || prediction.getProduce() == Produce.WEEDS
|| unixNow <= prediction.getDoneEstimate() || patchNotified || prediction.getCropState() == CropState.FILLING || prediction.getCropState() == CropState.EMPTY)
{
continue;
}
wasNotified.put(profilePatch, true);
if (!firstNotifyCheck && shouldNotify)
{
sendNotification(profile, prediction, patch);
}
}
}
}
firstNotifyCheck = false;
}
@VisibleForTesting
void sendNotification(RuneScapeProfile profile, PatchPrediction prediction, FarmingPatch patch)
{
final RuneScapeProfileType profileType = profile.getType();
final StringBuilder stringBuilder = new StringBuilder();
// Same RS account
if (client.getGameState() == GameState.LOGGED_IN && profile.getDisplayName().equals(client.getLocalPlayer().getName()))
{
// Same RS account but different profile type
if (profileType != RuneScapeProfileType.getCurrent(client))
{
stringBuilder.append("(")
.append(Text.titleCase(profile.getType()))
.append(") ");
}
// Same RS account AND profile falls through here so no bracketed prefix is added
}
else
{
// Different RS account AND profile type
if (profileType != RuneScapeProfileType.getCurrent(client) || client.getGameState() == GameState.LOGIN_SCREEN)
{
//Don't print profile type when logged out if is STANDARD
if (client.getGameState() == GameState.LOGIN_SCREEN && profileType == RuneScapeProfileType.STANDARD)
{
stringBuilder.append("(")
.append(profile.getDisplayName())
.append(") ");
}
else
{
stringBuilder.append("(")
.append(profile.getDisplayName())
.append(" - ")
.append(Text.titleCase(profile.getType()))
.append(") ");
}
}
// Different RS account but same profile type
else
{
stringBuilder.append("(")
.append(profile.getDisplayName())
.append(") ");
}
}
stringBuilder
.append("Your ")
.append(prediction.getProduce().getName());
switch (prediction.getCropState())
{
case HARVESTABLE:
case GROWING:
if (prediction.getProduce().getName().toLowerCase(Locale.ENGLISH).contains("compost"))
{
stringBuilder.append(" is ready to collect in ");
}
else
{
stringBuilder.append(" is ready to harvest in ");
}
break;
case DISEASED:
stringBuilder.append(" has become diseased in ");
break;
case DEAD:
stringBuilder.append(" has died in ");
break;
default:
// EMPTY and FILLING are caught above
throw new IllegalStateException();
}
stringBuilder.append(patch.getRegion().isDefinite() ? "the " : "")
.append(patch.getRegion().getName())
.append(".");
notifier.notify(stringBuilder.toString());
}
} }

View File

@@ -64,14 +64,14 @@ class FarmingWorld
{ {
// Some of these patches get updated in multiple regions. // Some of these patches get updated in multiple regions.
// It may be worth it to add a specialization for these patches // It may be worth it to add a specialization for these patches
add(new FarmingRegion("Al Kharid", 13106, add(new FarmingRegion("Al Kharid", 13106, false,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.CACTUS) new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.CACTUS)
), 13362, 13105); ), 13362, 13105);
add(new FarmingRegion("Ardougne", 10290, add(new FarmingRegion("Ardougne", 10290, false,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.BUSH) new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.BUSH)
), 10546); ), 10546);
add(new FarmingRegion("Ardougne", 10548, add(new FarmingRegion("Ardougne", 10548, false,
new FarmingPatch("North", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT), new FarmingPatch("North", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT),
new FarmingPatch("South", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT), new FarmingPatch("South", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT),
new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER), new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER),
@@ -79,12 +79,12 @@ class FarmingWorld
new FarmingPatch("", Varbits.FARMING_4775, PatchImplementation.COMPOST) new FarmingPatch("", Varbits.FARMING_4775, PatchImplementation.COMPOST)
)); ));
add(new FarmingRegion("Brimhaven", 11058, add(new FarmingRegion("Brimhaven", 11058, false,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.FRUIT_TREE), new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.FRUIT_TREE),
new FarmingPatch("", Varbits.FARMING_4772, PatchImplementation.SPIRIT_TREE) new FarmingPatch("", Varbits.FARMING_4772, PatchImplementation.SPIRIT_TREE)
), 11057); ), 11057);
add(new FarmingRegion("Catherby", 11062, add(new FarmingRegion("Catherby", 11062, false,
new FarmingPatch("North", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT), new FarmingPatch("North", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT),
new FarmingPatch("South", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT), new FarmingPatch("South", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT),
new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER), new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER),
@@ -103,7 +103,7 @@ class FarmingWorld
return true; return true;
} }
}, 11061, 11318, 11317); }, 11061, 11318, 11317);
add(new FarmingRegion("Catherby", 11317, add(new FarmingRegion("Catherby", 11317, false,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.FRUIT_TREE) new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.FRUIT_TREE)
) )
{ {
@@ -115,27 +115,27 @@ class FarmingWorld
} }
}); });
add(new FarmingRegion("Champions' Guild", 12596, add(new FarmingRegion("Champions' Guild", 12596, true,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.BUSH) new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.BUSH)
)); ));
add(new FarmingRegion("Draynor Manor", 12340, add(new FarmingRegion("Draynor Manor", 12340, false,
new FarmingPatch("Belladonna", Varbits.FARMING_4771, PatchImplementation.BELLADONNA) new FarmingPatch("Belladonna", Varbits.FARMING_4771, PatchImplementation.BELLADONNA)
)); ));
add(new FarmingRegion("Entrana", 11060, add(new FarmingRegion("Entrana", 11060, false,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.HOPS) new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.HOPS)
), 11316); ), 11316);
add(new FarmingRegion("Etceteria", 10300, add(new FarmingRegion("Etceteria", 10300, false,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.BUSH), new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.BUSH),
new FarmingPatch("", Varbits.FARMING_4772, PatchImplementation.SPIRIT_TREE) new FarmingPatch("", Varbits.FARMING_4772, PatchImplementation.SPIRIT_TREE)
)); ));
add(new FarmingRegion("Falador", 11828, add(new FarmingRegion("Falador", 11828, false,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.TREE) new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.TREE)
), 12084); ), 12084);
add(new FarmingRegion("Falador", 12083, add(new FarmingRegion("Falador", 12083, false,
new FarmingPatch("North West", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT), new FarmingPatch("North West", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT),
new FarmingPatch("South East", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT), new FarmingPatch("South East", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT),
new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER), new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER),
@@ -151,7 +151,7 @@ class FarmingWorld
} }
}); });
add(new FarmingRegion("Fossil Island", 14651, add(new FarmingRegion("Fossil Island", 14651, false,
new FarmingPatch("East", Varbits.FARMING_4771, PatchImplementation.HARDWOOD_TREE), new FarmingPatch("East", Varbits.FARMING_4771, PatchImplementation.HARDWOOD_TREE),
new FarmingPatch("Middle", Varbits.FARMING_4772, PatchImplementation.HARDWOOD_TREE), new FarmingPatch("Middle", Varbits.FARMING_4772, PatchImplementation.HARDWOOD_TREE),
new FarmingPatch("West", Varbits.FARMING_4773, PatchImplementation.HARDWOOD_TREE) new FarmingPatch("West", Varbits.FARMING_4773, PatchImplementation.HARDWOOD_TREE)
@@ -179,22 +179,22 @@ class FarmingWorld
return loc.getPlane() == 0; return loc.getPlane() == 0;
} }
}, 14907, 14908, 15164, 14652, 14906, 14650, 15162, 15163); }, 14907, 14908, 15164, 14652, 14906, 14650, 15162, 15163);
add(new FarmingRegion("Seaweed", 15008, add(new FarmingRegion("Seaweed", 15008, false,
new FarmingPatch("North", Varbits.FARMING_4771, PatchImplementation.SEAWEED), new FarmingPatch("North", Varbits.FARMING_4771, PatchImplementation.SEAWEED),
new FarmingPatch("South", Varbits.FARMING_4772, PatchImplementation.SEAWEED) new FarmingPatch("South", Varbits.FARMING_4772, PatchImplementation.SEAWEED)
)); ));
add(new FarmingRegion("Gnome Stronghold", 9781, add(new FarmingRegion("Gnome Stronghold", 9781, true,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.TREE), new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.TREE),
new FarmingPatch("", Varbits.FARMING_4772, PatchImplementation.FRUIT_TREE) new FarmingPatch("", Varbits.FARMING_4772, PatchImplementation.FRUIT_TREE)
), 9782, 9526, 9525); ), 9782, 9526, 9525);
add(new FarmingRegion("Harmony", 15148, add(new FarmingRegion("Harmony", 15148, false,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT), new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT),
new FarmingPatch("", Varbits.FARMING_4772, PatchImplementation.HERB) new FarmingPatch("", Varbits.FARMING_4772, PatchImplementation.HERB)
)); ));
add(new FarmingRegion("Kourend", 6967, add(new FarmingRegion("Kourend", 6967, false,
new FarmingPatch("North East", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT), new FarmingPatch("North East", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT),
new FarmingPatch("South West", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT), new FarmingPatch("South West", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT),
new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER), new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER),
@@ -202,7 +202,7 @@ class FarmingWorld
new FarmingPatch("", Varbits.FARMING_4775, PatchImplementation.COMPOST), new FarmingPatch("", Varbits.FARMING_4775, PatchImplementation.COMPOST),
new FarmingPatch("", Varbits.FARMING_7904, PatchImplementation.SPIRIT_TREE) new FarmingPatch("", Varbits.FARMING_7904, PatchImplementation.SPIRIT_TREE)
), 6711); ), 6711);
add(new FarmingRegion("Kourend", 7223, add(new FarmingRegion("Kourend", 7223, false,
new FarmingPatch("East 1", Varbits.GRAPES_4953, PatchImplementation.GRAPES), new FarmingPatch("East 1", Varbits.GRAPES_4953, PatchImplementation.GRAPES),
new FarmingPatch("East 2", Varbits.GRAPES_4954, PatchImplementation.GRAPES), new FarmingPatch("East 2", Varbits.GRAPES_4954, PatchImplementation.GRAPES),
new FarmingPatch("East 3", Varbits.GRAPES_4955, PatchImplementation.GRAPES), new FarmingPatch("East 3", Varbits.GRAPES_4955, PatchImplementation.GRAPES),
@@ -217,21 +217,21 @@ class FarmingWorld
new FarmingPatch("West 6", Varbits.GRAPES_4964, PatchImplementation.GRAPES) new FarmingPatch("West 6", Varbits.GRAPES_4964, PatchImplementation.GRAPES)
)); ));
add(new FarmingRegion("Lletya", 9265, add(new FarmingRegion("Lletya", 9265, false,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.FRUIT_TREE) new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.FRUIT_TREE)
), 11103); ), 11103);
add(new FarmingRegion("Lumbridge", 12851, add(new FarmingRegion("Lumbridge", 12851, false,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.HOPS) new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.HOPS)
)); ));
add(new FarmingRegion("Lumbridge", 12594, add(new FarmingRegion("Lumbridge", 12594, false,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.TREE) new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.TREE)
), 12850); ), 12850);
add(new FarmingRegion("Morytania", 13622, add(new FarmingRegion("Morytania", 13622, false,
new FarmingPatch("Mushroom", Varbits.FARMING_4771, PatchImplementation.MUSHROOM) new FarmingPatch("Mushroom", Varbits.FARMING_4771, PatchImplementation.MUSHROOM)
), 13878); ), 13878);
add(new FarmingRegion("Morytania", 14391, add(new FarmingRegion("Morytania", 14391, false,
new FarmingPatch("North West", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT), new FarmingPatch("North West", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT),
new FarmingPatch("South East", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT), new FarmingPatch("South East", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT),
new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER), new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER),
@@ -239,7 +239,7 @@ class FarmingWorld
new FarmingPatch("", Varbits.FARMING_4775, PatchImplementation.COMPOST) new FarmingPatch("", Varbits.FARMING_4775, PatchImplementation.COMPOST)
), 14390); ), 14390);
add(new FarmingRegion("Port Sarim", 12082, add(new FarmingRegion("Port Sarim", 12082, false,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.SPIRIT_TREE) new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.SPIRIT_TREE)
) )
{ {
@@ -250,48 +250,48 @@ class FarmingWorld
} }
}, 12083); }, 12083);
add(new FarmingRegion("Rimmington", 11570, add(new FarmingRegion("Rimmington", 11570, false,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.BUSH) new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.BUSH)
), 11826); ), 11826);
add(new FarmingRegion("Seers' Village", 10551, add(new FarmingRegion("Seers' Village", 10551, false,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.HOPS) new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.HOPS)
), 10550); ), 10550);
add(new FarmingRegion("Tai Bwo Wannai", 11056, add(new FarmingRegion("Tai Bwo Wannai", 11056, false,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.CALQUAT) new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.CALQUAT)
)); ));
add(new FarmingRegion("Taverley", 11573, add(new FarmingRegion("Taverley", 11573, false,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.TREE) new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.TREE)
), 11829); ), 11829);
add(new FarmingRegion("Tree Gnome Village", 9777, add(new FarmingRegion("Tree Gnome Village", 9777, true,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.FRUIT_TREE) new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.FRUIT_TREE)
), 10033); ), 10033);
add(new FarmingRegion("Troll Stronghold", 11321, add(new FarmingRegion("Troll Stronghold", 11321, true,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.HERB) new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.HERB)
)); ));
add(new FarmingRegion("Varrock", 12854, add(new FarmingRegion("Varrock", 12854, false,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.TREE) new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.TREE)
), 12853); ), 12853);
add(new FarmingRegion("Yanille", 10288, add(new FarmingRegion("Yanille", 10288, false,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.HOPS) new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.HOPS)
)); ));
add(new FarmingRegion("Weiss", 11325, add(new FarmingRegion("Weiss", 11325, false,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.HERB) new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.HERB)
)); ));
add(new FarmingRegion("Farming Guild", 5021, add(new FarmingRegion("Farming Guild", 5021, true,
new FarmingPatch("Hespori", Varbits.FARMING_7908, PatchImplementation.HESPORI) new FarmingPatch("Hespori", Varbits.FARMING_7908, PatchImplementation.HESPORI)
)); ));
//Full 3x3 region area centered on farming guild //Full 3x3 region area centered on farming guild
add(farmingGuildRegion = new FarmingRegion("Farming Guild", 4922, add(farmingGuildRegion = new FarmingRegion("Farming Guild", 4922, true,
new FarmingPatch("", Varbits.FARMING_7905, PatchImplementation.TREE), new FarmingPatch("", Varbits.FARMING_7905, PatchImplementation.TREE),
new FarmingPatch("", Varbits.FARMING_4775, PatchImplementation.HERB), new FarmingPatch("", Varbits.FARMING_4775, PatchImplementation.HERB),
new FarmingPatch("", Varbits.FARMING_4772, PatchImplementation.BUSH), new FarmingPatch("", Varbits.FARMING_4772, PatchImplementation.BUSH),
@@ -308,7 +308,7 @@ class FarmingWorld
), 5177, 5178, 5179, 4921, 4923, 4665, 4666, 4667); ), 5177, 5178, 5179, 4921, 4923, 4665, 4666, 4667);
//All of Prifddinas, and all of Prifddinas Underground //All of Prifddinas, and all of Prifddinas Underground
add(new FarmingRegion("Prifddinas", 13151, add(new FarmingRegion("Prifddinas", 13151, false,
new FarmingPatch("North", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT), new FarmingPatch("North", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT),
new FarmingPatch("South", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT), new FarmingPatch("South", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT),
new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER), new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER),

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) 2021 Hannah Ryan <https://github.com/loldudester>
* 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.plugins.timetracking.farming;
import lombok.Value;
@Value
class ProfilePatch
{
FarmingPatch patch;
String rsProfileKey;
}

View File

@@ -58,6 +58,7 @@ enum DungeonLocation
CRABCLAW_CAVES_TUNNEL("Crabclaw Caves Tunnel (quest)", new WorldPoint(1671, 9800, 0)), CRABCLAW_CAVES_TUNNEL("Crabclaw Caves Tunnel (quest)", new WorldPoint(1671, 9800, 0)),
CRANDOR("Crandor Dungeon", new WorldPoint(2833, 3256, 0)), CRANDOR("Crandor Dungeon", new WorldPoint(2833, 3256, 0)),
CRASH_ISLAND("Crash Island Dungeon", new WorldPoint(2920, 2721, 0)), CRASH_ISLAND("Crash Island Dungeon", new WorldPoint(2920, 2721, 0)),
CRUMBLING_TOWER("Crumbling Tower basement", new WorldPoint(2130, 2994, 0)),
DEEP_WILDERNESS("Deep Wilderness Dungeon", new WorldPoint(3044, 3924, 0)), DEEP_WILDERNESS("Deep Wilderness Dungeon", new WorldPoint(3044, 3924, 0)),
DRAYNOR_MANOR_E("Draynor Manor basement", new WorldPoint(3114, 3357, 0)), DRAYNOR_MANOR_E("Draynor Manor basement", new WorldPoint(3114, 3357, 0)),
DRAYNOR_MANOR_W("Draynor Manor basement", new WorldPoint(3091, 3362, 0)), DRAYNOR_MANOR_W("Draynor Manor basement", new WorldPoint(3091, 3362, 0)),
@@ -89,6 +90,7 @@ enum DungeonLocation
ICE_QUEEN_W("Ice Queen's Lair", new WorldPoint(2822, 3510, 0)), ICE_QUEEN_W("Ice Queen's Lair", new WorldPoint(2822, 3510, 0)),
ICE_TROLL_E("Ice Troll Caves", new WorldPoint(2400, 3889, 0)), ICE_TROLL_E("Ice Troll Caves", new WorldPoint(2400, 3889, 0)),
ICE_TROLL_W("Ice Troll Caves", new WorldPoint(2315, 3894, 0)), ICE_TROLL_W("Ice Troll Caves", new WorldPoint(2315, 3894, 0)),
ISLE_OF_SOULS_DUNGEON("Isle of Souls Dungeon", new WorldPoint(2308, 2919, 0)),
IORWERTH("Iorwerth Dungeon", new WorldPoint(3224, 6044, 0)), IORWERTH("Iorwerth Dungeon", new WorldPoint(3224, 6044, 0)),
IORWERTH_CAMP_CAVE("Iorwerth Camp cave", new WorldPoint(2200, 3262, 0)), IORWERTH_CAMP_CAVE("Iorwerth Camp cave", new WorldPoint(2200, 3262, 0)),
IORWERTH_CAMP_CAVE_PRIF("Iorwerth Camp cave", new WorldPoint(3224, 6014, 0)), IORWERTH_CAMP_CAVE_PRIF("Iorwerth Camp cave", new WorldPoint(3224, 6014, 0)),

View File

@@ -95,6 +95,9 @@ enum FishingSpotLocation
IORWERTH_CAMP_OUTSIDE(FishingSpot.SALMON, new WorldPoint(2215, 3245, 0)), IORWERTH_CAMP_OUTSIDE(FishingSpot.SALMON, new WorldPoint(2215, 3245, 0)),
ISAFDAR_NORTH_EAST_INSIDE(FishingSpot.SALMON, new WorldPoint(3293, 6005, 0)), ISAFDAR_NORTH_EAST_INSIDE(FishingSpot.SALMON, new WorldPoint(3293, 6005, 0)),
ISAFDAR_NORTH_EAST_OUTSIDE(FishingSpot.SALMON, new WorldPoint(2269, 3253, 0)), ISAFDAR_NORTH_EAST_OUTSIDE(FishingSpot.SALMON, new WorldPoint(2269, 3253, 0)),
ISLE_OF_SOULS_EAST(FishingSpot.SHARK, new WorldPoint(2281, 2841, 0)),
ISLE_OF_SOULS_NORTH(FishingSpot.LOBSTER, new WorldPoint(2280, 2975, 0)),
ISLE_OF_SOULS_SOUTH_WEST(FishingSpot.SHRIMP, new WorldPoint(2162, 2782, 0)),
JATISZO(new FishingSpot[]{FishingSpot.SHARK, FishingSpot.LOBSTER}, JATISZO(new FishingSpot[]{FishingSpot.SHARK, FishingSpot.LOBSTER},
new WorldPoint(2400, 3780, 0), new WorldPoint(2412, 3780, 0), new WorldPoint(2400, 3780, 0), new WorldPoint(2412, 3780, 0),
new WorldPoint(2419, 3789, 0)), new WorldPoint(2419, 3789, 0)),

View File

@@ -49,6 +49,9 @@ enum HunterAreaLocation
FOSSIL_ISLAND_UNDERWATER(new WorldPoint(3743, 10295, 0), HunterCreature.FISH_SHOAL), FOSSIL_ISLAND_UNDERWATER(new WorldPoint(3743, 10295, 0), HunterCreature.FISH_SHOAL),
GWENITH_HUNTER_AREA_OUTSIDE(new WorldPoint(2269, 3408, 0), HunterCreature.CARNIVOROUS_CHINCHOMPA), GWENITH_HUNTER_AREA_OUTSIDE(new WorldPoint(2269, 3408, 0), HunterCreature.CARNIVOROUS_CHINCHOMPA),
GWENITH_HUNTER_AREA_INSIDE(new WorldPoint(3293, 6160, 0), HunterCreature.CARNIVOROUS_CHINCHOMPA), GWENITH_HUNTER_AREA_INSIDE(new WorldPoint(3293, 6160, 0), HunterCreature.CARNIVOROUS_CHINCHOMPA),
ISLE_OF_SOULS_NORTH(new WorldPoint(2207, 2964, 0), HunterCreature.COPPER_LONGTAIL),
ISLE_OF_SOULS_NORTH_WEST(new WorldPoint(2127, 2950, 0), HunterCreature.CHINCHOMPA),
ISLE_OF_SOULS_SOUTH_WEST(new WorldPoint(2158, 2822, 0), HunterCreature.CRIMSON_SWIFT),
KARAMJA_HUNTER_AREA(new WorldPoint(2786, 3001, 0), HunterCreature.HORNED_GRAAHK), KARAMJA_HUNTER_AREA(new WorldPoint(2786, 3001, 0), HunterCreature.HORNED_GRAAHK),
KEBOS_SWAMP(new WorldPoint(1184, 3595, 0), HunterCreature.CRIMSON_SWIFT), KEBOS_SWAMP(new WorldPoint(1184, 3595, 0), HunterCreature.CRIMSON_SWIFT),
KOUREND_WOODLAND_CENTER(new WorldPoint(1512, 3478, 0), HunterCreature.RUBY_HARVEST), KOUREND_WOODLAND_CENTER(new WorldPoint(1512, 3478, 0), HunterCreature.RUBY_HARVEST),

View File

@@ -117,6 +117,11 @@ enum MiningSiteLocation
new Rock(10, Ore.CLAY), new Rock(11, Ore.COPPER), new Rock(4, Ore.TIN), new Rock(9, Ore.IRON), new Rock(10, Ore.CLAY), new Rock(11, Ore.COPPER), new Rock(4, Ore.TIN), new Rock(9, Ore.IRON),
new Rock(2, Ore.SILVER)), new Rock(2, Ore.SILVER)),
ISAFDAR(new WorldPoint(2277, 3159, 0), new Rock(4, Ore.ADAMANTITE), new Rock(2, Ore.RUNITE)), ISAFDAR(new WorldPoint(2277, 3159, 0), new Rock(4, Ore.ADAMANTITE), new Rock(2, Ore.RUNITE)),
ISLE_OF_SOULS_DUNGEON_EAST(new WorldPoint(1831, 9109, 0), new Rock(1, Ore.RUNITE)),
ISLE_OF_SOULS_DUNGEON_WEST(new WorldPoint(1814, 9116, 0), new Rock(2, Ore.ADAMANTITE)),
ISLE_OF_SOULS_SOUTH(new WorldPoint(2195, 2793, 0),
new Rock(3, Ore.CLAY), new Rock(3, Ore.TIN), new Rock(3, Ore.COPPER), new Rock(10, Ore.IRON),
new Rock(3, Ore.SILVER), new Rock(6, Ore.COAL), new Rock(4, Ore.GOLD), new Rock(2, Ore.MITHRIL)),
JATIZSO(new WorldPoint(2396, 3812, 0), JATIZSO(new WorldPoint(2396, 3812, 0),
new Rock(11, Ore.TIN), new Rock(7, Ore.IRON), new Rock(8, Ore.COAL), new Rock(15, Ore.MITHRIL), new Rock(11, Ore.TIN), new Rock(7, Ore.IRON), new Rock(8, Ore.COAL), new Rock(15, Ore.MITHRIL),
new Rock(11, Ore.ADAMANTITE)), new Rock(11, Ore.ADAMANTITE)),

View File

@@ -83,6 +83,9 @@ enum RareTreeLocation
new WorldPoint(2748, 3466, 0), new WorldPoint(2748, 3466, 0),
new WorldPoint(2710, 3570, 0), new WorldPoint(2710, 3570, 0),
// Isle of Souls
new WorldPoint(2254, 2808, 0),
// Prifddinas // Prifddinas
new WorldPoint(2209, 3427, 0), new WorldPoint(2209, 3427, 0),
new WorldPoint(3233, 6179, 0)), new WorldPoint(3233, 6179, 0)),
@@ -111,6 +114,9 @@ enum RareTreeLocation
// Mos Le'Harmless // Mos Le'Harmless
new WorldPoint(3810, 3058, 0), new WorldPoint(3810, 3058, 0),
// Isle of Souls
new WorldPoint(2194, 2991, 0),
// Karamja // Karamja
new WorldPoint(2821, 3084, 0)), new WorldPoint(2821, 3084, 0)),
@@ -180,6 +186,10 @@ enum RareTreeLocation
new WorldPoint(3674, 3447, 0), new WorldPoint(3674, 3447, 0),
new WorldPoint(3684, 3385, 0), new WorldPoint(3684, 3385, 0),
// Isle of Souls
new WorldPoint(2147, 2972, 0),
new WorldPoint(2165, 2863, 0),
// Zanaris // Zanaris
new WorldPoint(2412, 4464, 0), new WorldPoint(2412, 4464, 0),
new WorldPoint(2465, 4427, 0), new WorldPoint(2465, 4427, 0),

View File

@@ -33,14 +33,12 @@ import java.awt.Font;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter; import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent; import java.awt.event.FocusEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener; import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter; import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.awt.event.MouseListener; import java.awt.event.MouseListener;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.function.Consumer;
import javax.swing.DefaultListModel; import javax.swing.DefaultListModel;
import javax.swing.ImageIcon; import javax.swing.ImageIcon;
import javax.swing.JButton; import javax.swing.JButton;
@@ -335,30 +333,6 @@ public class IconTextField extends JPanel
clearListeners.add(clearListener); clearListeners.add(clearListener);
} }
public void addKeyListener(Consumer<KeyEvent> keyEventConsumer)
{
addKeyListener(new net.runelite.client.input.KeyListener()
{
@Override
public void keyTyped(KeyEvent e)
{
keyEventConsumer.accept(e);
}
@Override
public void keyPressed(KeyEvent e)
{
keyEventConsumer.accept(e);
}
@Override
public void keyReleased(KeyEvent e)
{
keyEventConsumer.accept(e);
}
});
}
@Override @Override
public void removeKeyListener(KeyListener keyListener) public void removeKeyListener(KeyListener keyListener)
{ {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 B

After

Width:  |  Height:  |  Size: 134 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 B

View File

@@ -0,0 +1,55 @@
/*
* Copyright (c) 2021, Adam <Adam@sigterm.info>
* 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.
*/
struct uniform {
int cameraYaw;
int cameraPitch;
int centerX;
int centerY;
int zoom;
int cameraX;
int cameraY;
int cameraZ;
int4 sinCosTable[2048];
};
struct shared_data {
int totalNum[12]; // number of faces with a given priority
int totalDistance[12]; // sum of distances to faces of a given priority
int totalMappedNum[18]; // number of faces with a given adjusted priority
int min10; // minimum distance to a face of priority 10
int dfs[0]; // packed face id and distance, size 512 for small, 4096 for large
};
struct modelinfo {
int offset; // offset into buffer
int uvOffset; // offset into uv buffer
int size; // length in faces
int idx; // write idx in target buffer
int flags; // radius, orientation
int x; // scene position x
int y; // scene position y
int z; // scene position z
};

View File

@@ -0,0 +1,104 @@
/*
* Copyright (c) 2021, Adam <Adam@sigterm.info>
* 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.
*/
#define PI 3.1415926535897932384626433832795f
#define UNIT PI / 1024.0f
float3 toScreen(int4 vertex, int cameraYaw, int cameraPitch, int centerX, int centerY, int zoom) {
float yawSin = sin(cameraYaw * UNIT);
float yawCos = cos(cameraYaw * UNIT);
float pitchSin = sin(cameraPitch * UNIT);
float pitchCos = cos(cameraPitch * UNIT);
float rotatedX = (vertex.z * yawSin) + (vertex.x * yawCos);
float rotatedZ = (vertex.z * yawCos) - (vertex.x * yawSin);
float var13 = (vertex.y * pitchCos) - (rotatedZ * pitchSin);
float var12 = (vertex.y * pitchSin) + (rotatedZ * pitchCos);
float x = rotatedX * zoom / var12 + centerX;
float y = var13 * zoom / var12 + centerY;
float z = -var12; // in OpenGL depth is negative
return (float3) (x, y, z);
}
/*
* Rotate a vertex by a given orientation in JAU
*/
int4 rotate_vertex(__constant struct uniform *uni, int4 vertex, int orientation) {
int4 sinCos = uni->sinCosTable[orientation];
int s = sinCos.x;
int c = sinCos.y;
int x = vertex.z * s + vertex.x * c >> 16;
int z = vertex.z * c - vertex.x * s >> 16;
return (int4)(x, vertex.y, z, vertex.w);
}
/*
* Calculate the distance to a vertex given the camera angle
*/
int vertex_distance(int4 vertex, int cameraYaw, int cameraPitch) {
int yawSin = (int)(65536.0f * sin(cameraYaw * UNIT));
int yawCos = (int)(65536.0f * cos(cameraYaw * UNIT));
int pitchSin = (int)(65536.0f * sin(cameraPitch * UNIT));
int pitchCos = (int)(65536.0f * cos(cameraPitch * UNIT));
int j = vertex.z * yawCos - vertex.x * yawSin >> 16;
int l = vertex.y * pitchSin + j * pitchCos >> 16;
return l;
}
/*
* Calculate the distance to a face
*/
int face_distance(int4 vA, int4 vB, int4 vC, int cameraYaw, int cameraPitch) {
int dvA = vertex_distance(vA, cameraYaw, cameraPitch);
int dvB = vertex_distance(vB, cameraYaw, cameraPitch);
int dvC = vertex_distance(vC, cameraYaw, cameraPitch);
int faceDistance = (dvA + dvB + dvC) / 3;
return faceDistance;
}
/*
* Test if a face is visible (not backward facing)
*/
bool face_visible(__constant struct uniform *uni, int4 vA, int4 vB, int4 vC, int4 position) {
// Move model to scene location, and account for camera offset
int4 cameraPos = (int4)(uni->cameraX, uni->cameraY, uni->cameraZ, 0);
vA += position - cameraPos;
vB += position - cameraPos;
vC += position - cameraPos;
float3 sA = toScreen(vA, uni->cameraYaw, uni->cameraPitch, uni->centerX, uni->centerY, uni->zoom);
float3 sB = toScreen(vB, uni->cameraYaw, uni->cameraPitch, uni->centerX, uni->centerY, uni->zoom);
float3 sC = toScreen(vC, uni->cameraYaw, uni->cameraPitch, uni->centerX, uni->centerY, uni->zoom);
return (sA.x - sB.x) * (sC.y - sB.y) - (sC.x - sB.x) * (sA.y - sB.y) > 0;
}

View File

@@ -0,0 +1,97 @@
/*
* Copyright (c) 2021, Adam <Adam@sigterm.info>
* 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.
*/
#include FACE_COUNT
#include cl_types.cl
#include to_screen.cl
#include common.cl
#include priority_render.cl
__kernel
__attribute__((work_group_size_hint(256, 1, 1)))
void computeLarge(
__local struct shared_data *shared,
__global const struct modelinfo *ol,
__global const int4 *vb,
__global const int4 *tempvb,
__global const float4 *uv,
__global const float4 *tempuv,
__global int4 *vout,
__global float4 *uvout,
__constant struct uniform *uni) {
size_t groupId = get_group_id(0);
size_t localId = get_local_id(0) * FACE_COUNT;
struct modelinfo minfo = ol[groupId];
int4 pos = (int4)(minfo.x, minfo.y, minfo.z, 0);
if (localId == 0) {
shared->min10 = 1600;
for (int i = 0; i < 12; ++i) {
shared->totalNum[i] = 0;
shared->totalDistance[i] = 0;
}
for (int i = 0; i < 18; ++i) {
shared->totalMappedNum[i] = 0;
}
}
int prio[FACE_COUNT];
int dis[FACE_COUNT];
int4 v1[FACE_COUNT];
int4 v2[FACE_COUNT];
int4 v3[FACE_COUNT];
for (int i = 0; i < FACE_COUNT; i++) {
get_face(shared, uni, vb, tempvb, localId + i, minfo, uni->cameraYaw, uni->cameraPitch, &prio[i], &dis[i], &v1[i], &v2[i], &v3[i]);
}
barrier(CLK_LOCAL_MEM_FENCE);
for (int i = 0; i < FACE_COUNT; i++) {
add_face_prio_distance(shared, uni, localId + i, minfo, v1[i], v2[i], v3[i], prio[i], dis[i], pos);
}
barrier(CLK_LOCAL_MEM_FENCE);
int prioAdj[FACE_COUNT];
int idx[FACE_COUNT];
for (int i = 0; i < FACE_COUNT; i++) {
idx[i] = map_face_priority(shared, localId + i, minfo, prio[i], dis[i], &prioAdj[i]);
}
barrier(CLK_LOCAL_MEM_FENCE);
for (int i = 0; i < FACE_COUNT; i++) {
insert_dfs(shared, localId + i, minfo, prioAdj[i], dis[i], idx[i]);
}
barrier(CLK_LOCAL_MEM_FENCE);
for (int i = 0; i < FACE_COUNT; i++) {
sort_and_insert(shared, uv, tempuv, vout, uvout, localId + i, minfo, prioAdj[i], dis[i], v1[i], v2[i], v3[i]);
}
}

View File

@@ -0,0 +1,87 @@
/*
* Copyright (c) 2021, Adam <Adam@sigterm.info>
* 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.
*/
#include cl_types.cl
__kernel
__attribute__((reqd_work_group_size(6, 1, 1)))
void computeUnordered(__global const struct modelinfo *ol,
__global const int4 *vb,
__global const int4 *tempvb,
__global const float4 *uv,
__global const float4 *tempuv,
__global int4 *vout,
__global float4 *uvout) {
size_t groupId = get_group_id(0);
size_t localId = get_local_id(0);
struct modelinfo minfo = ol[groupId];
int offset = minfo.offset;
int size = minfo.size;
int outOffset = minfo.idx;
int uvOffset = minfo.uvOffset;
int flags = minfo.flags;
int4 pos = (int4)(minfo.x, minfo.y, minfo.z, 0);
if (localId >= size) {
return;
}
uint ssboOffset = localId;
int4 thisA, thisB, thisC;
// Grab triangle vertices from the correct buffer
if (flags < 0) {
thisA = vb[offset + ssboOffset * 3];
thisB = vb[offset + ssboOffset * 3 + 1];
thisC = vb[offset + ssboOffset * 3 + 2];
} else {
thisA = tempvb[offset + ssboOffset * 3];
thisB = tempvb[offset + ssboOffset * 3 + 1];
thisC = tempvb[offset + ssboOffset * 3 + 2];
}
uint myOffset = localId;
// position vertices in scene and write to out buffer
vout[outOffset + myOffset * 3] = pos + thisA;
vout[outOffset + myOffset * 3 + 1] = pos + thisB;
vout[outOffset + myOffset * 3 + 2] = pos + thisC;
if (uvOffset < 0) {
uvout[outOffset + myOffset * 3] = (float4)(0.0f, 0.0f, 0.0f, 0.0f);
uvout[outOffset + myOffset * 3 + 1] = (float4)(0.0f, 0.0f, 0.0f, 0.0f);
uvout[outOffset + myOffset * 3 + 2] = (float4)(0.0f, 0.0f, 0.0f, 0.0f);
} else if (flags >= 0) {
uvout[outOffset + myOffset * 3] = tempuv[uvOffset + localId * 3];
uvout[outOffset + myOffset * 3 + 1] = tempuv[uvOffset + localId * 3 + 1];
uvout[outOffset + myOffset * 3 + 2] = tempuv[uvOffset + localId * 3 + 2];
} else {
uvout[outOffset + myOffset * 3] = uv[uvOffset + localId * 3];
uvout[outOffset + myOffset * 3 + 1] = uv[uvOffset + localId * 3 + 1];
uvout[outOffset + myOffset * 3 + 2] = uv[uvOffset + localId * 3 + 2];
}
}

View File

@@ -0,0 +1,298 @@
/*
* Copyright (c) 2021, Adam <Adam@sigterm.info>
* 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.
*/
// Calculate adjusted priority for a face with a given priority, distance, and
// model global min10 and face distance averages. This allows positioning faces
// with priorities 10/11 into the correct 'slots' resulting in 18 possible
// adjusted priorities
int priority_map(int p, int distance, int _min10, int avg1, int avg2, int avg3) {
// (10, 11) 0 1 2 (10, 11) 3 4 (10, 11) 5 6 7 8 9 (10, 11)
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
switch (p) {
case 0: return 2;
case 1: return 3;
case 2: return 4;
case 3: return 7;
case 4: return 8;
case 5: return 11;
case 6: return 12;
case 7: return 13;
case 8: return 14;
case 9: return 15;
case 10:
if (distance > avg1) {
return 0;
} else if (distance > avg2) {
return 5;
} else if (distance > avg3) {
return 9;
} else {
return 16;
}
case 11:
if (distance > avg1 && _min10 > avg1) {
return 1;
} else if (distance > avg2 && (_min10 > avg1 || _min10 > avg2)) {
return 6;
} else if (distance > avg3 && (_min10 > avg1 || _min10 > avg2 || _min10 > avg3)) {
return 10;
} else {
return 17;
}
default:
return -1;
}
}
// calculate the number of faces with a lower adjusted priority than
// the given adjusted priority
int count_prio_offset(__local struct shared_data *shared, int priority) {
int total = 0;
switch (priority) {
case 17:
total += shared->totalMappedNum[16];
case 16:
total += shared->totalMappedNum[15];
case 15:
total += shared->totalMappedNum[14];
case 14:
total += shared->totalMappedNum[13];
case 13:
total += shared->totalMappedNum[12];
case 12:
total += shared->totalMappedNum[11];
case 11:
total += shared->totalMappedNum[10];
case 10:
total += shared->totalMappedNum[9];
case 9:
total += shared->totalMappedNum[8];
case 8:
total += shared->totalMappedNum[7];
case 7:
total += shared->totalMappedNum[6];
case 6:
total += shared->totalMappedNum[5];
case 5:
total += shared->totalMappedNum[4];
case 4:
total += shared->totalMappedNum[3];
case 3:
total += shared->totalMappedNum[2];
case 2:
total += shared->totalMappedNum[1];
case 1:
total += shared->totalMappedNum[0];
case 0:
return total;
}
}
void get_face(
__local struct shared_data *shared,
__constant struct uniform *uni,
__global const int4 *vb,
__global const int4 *tempvb,
uint localId, struct modelinfo minfo, int cameraYaw, int cameraPitch,
/* out */ int *prio, int *dis, int4 *o1, int4 *o2, int4 *o3) {
int size = minfo.size;
int offset = minfo.offset;
int flags = minfo.flags;
uint ssboOffset;
if (localId < size) {
ssboOffset = localId;
} else {
ssboOffset = 0;
}
int4 thisA;
int4 thisB;
int4 thisC;
// Grab triangle vertices from the correct buffer
if (flags < 0) {
thisA = vb[offset + ssboOffset * 3];
thisB = vb[offset + ssboOffset * 3 + 1];
thisC = vb[offset + ssboOffset * 3 + 2];
} else {
thisA = tempvb[offset + ssboOffset * 3];
thisB = tempvb[offset + ssboOffset * 3 + 1];
thisC = tempvb[offset + ssboOffset * 3 + 2];
}
if (localId < size) {
int radius = (flags & 0x7fffffff) >> 12;
int orientation = flags & 0x7ff;
// rotate for model orientation
int4 thisrvA = rotate_vertex(uni, thisA, orientation);
int4 thisrvB = rotate_vertex(uni, thisB, orientation);
int4 thisrvC = rotate_vertex(uni, thisC, orientation);
// calculate distance to face
int thisPriority = (thisA.w >> 16) & 0xff;// all vertices on the face have the same priority
int thisDistance;
if (radius == 0) {
thisDistance = 0;
} else {
thisDistance = face_distance(thisrvA, thisrvB, thisrvC, cameraYaw, cameraPitch) + radius;
}
*o1 = thisrvA;
*o2 = thisrvB;
*o3 = thisrvC;
*prio = thisPriority;
*dis = thisDistance;
} else {
*o1 = (int4)(0, 0, 0, 0);
*o2 = (int4)(0, 0, 0, 0);
*o3 = (int4)(0, 0, 0, 0);
*prio = 0;
*dis = 0;
}
}
void add_face_prio_distance(
__local struct shared_data *shared,
__constant struct uniform *uni,
uint localId, struct modelinfo minfo, int4 thisrvA, int4 thisrvB, int4 thisrvC, int thisPriority, int thisDistance, int4 pos) {
if (localId < minfo.size) {
// if the face is not culled, it is calculated into priority distance averages
if (face_visible(uni, thisrvA, thisrvB, thisrvC, pos)) {
atomic_add(&shared->totalNum[thisPriority], 1);
atomic_add(&shared->totalDistance[thisPriority], thisDistance);
// calculate minimum distance to any face of priority 10 for positioning the 11 faces later
if (thisPriority == 10) {
atomic_min(&shared->min10, thisDistance);
}
}
}
}
int map_face_priority(__local struct shared_data *shared, uint localId, struct modelinfo minfo, int thisPriority, int thisDistance, int *prio) {
int size = minfo.size;
// Compute average distances for 0/2, 3/4, and 6/8
if (localId < size) {
int avg1 = 0;
int avg2 = 0;
int avg3 = 0;
if (shared->totalNum[1] > 0 || shared->totalNum[2] > 0) {
avg1 = (shared->totalDistance[1] + shared->totalDistance[2]) / (shared->totalNum[1] + shared->totalNum[2]);
}
if (shared->totalNum[3] > 0 || shared->totalNum[4] > 0) {
avg2 = (shared->totalDistance[3] + shared->totalDistance[4]) / (shared->totalNum[3] + shared->totalNum[4]);
}
if (shared->totalNum[6] > 0 || shared->totalNum[8] > 0) {
avg3 = (shared->totalDistance[6] + shared->totalDistance[8]) / (shared->totalNum[6] + shared->totalNum[8]);
}
int adjPrio = priority_map(thisPriority, thisDistance, shared->min10, avg1, avg2, avg3);
int prioIdx = atomic_add(&shared->totalMappedNum[adjPrio], 1);
*prio = adjPrio;
return prioIdx;
}
*prio = 0;
return 0;
}
void insert_dfs(__local struct shared_data *shared, uint localId, struct modelinfo minfo, int adjPrio, int distance, int prioIdx) {
int size = minfo.size;
if (localId < size) {
// calculate base offset into dfs based on number of faces with a lower priority
int baseOff = count_prio_offset(shared, adjPrio);
// store into face array offset array by unique index
shared->dfs[baseOff + prioIdx] = ((int) localId << 16) | distance;
}
}
void sort_and_insert(
__local struct shared_data *shared,
__global const float4 *uv,
__global const float4 *tempuv,
__global int4 *vout,
__global float4 *uvout,
uint localId, struct modelinfo minfo, int thisPriority, int thisDistance, int4 thisrvA, int4 thisrvB, int4 thisrvC) {
/* compute face distance */
int size = minfo.size;
if (localId < size) {
int outOffset = minfo.idx;
int uvOffset = minfo.uvOffset;
int flags = minfo.flags;
int4 pos = (int4)(minfo.x, minfo.y, minfo.z, 0);
const int priorityOffset = count_prio_offset(shared, thisPriority);
const int numOfPriority = shared->totalMappedNum[thisPriority];
int start = priorityOffset; // index of first face with this priority
int end = priorityOffset + numOfPriority; // index of last face with this priority
int myOffset = priorityOffset;
// we only have to order faces against others of the same priority
// calculate position this face will be in
for (int i = start; i < end; ++i) {
int d1 = shared->dfs[i];
int theirId = d1 >> 16;
int theirDistance = d1 & 0xffff;
// the closest faces draw last, so have the highest index
// if two faces have the same distance, the one with the
// higher id draws last
if ((theirDistance > thisDistance)
|| (theirDistance == thisDistance && theirId < localId)) {
++myOffset;
}
}
// position vertices in scene and write to out buffer
vout[outOffset + myOffset * 3] = pos + thisrvA;
vout[outOffset + myOffset * 3 + 1] = pos + thisrvB;
vout[outOffset + myOffset * 3 + 2] = pos + thisrvC;
if (uvOffset < 0) {
uvout[outOffset + myOffset * 3] = (float4)(0, 0, 0, 0);
uvout[outOffset + myOffset * 3 + 1] = (float4)(0, 0, 0, 0);
uvout[outOffset + myOffset * 3 + 2] = (float4)(0, 0, 0, 0);
} else if (flags >= 0) {
uvout[outOffset + myOffset * 3] = tempuv[uvOffset + localId * 3];
uvout[outOffset + myOffset * 3 + 1] = tempuv[uvOffset + localId * 3 + 1];
uvout[outOffset + myOffset * 3 + 2] = tempuv[uvOffset + localId * 3 + 2];
} else {
uvout[outOffset + myOffset * 3] = uv[uvOffset + localId * 3];
uvout[outOffset + myOffset * 3 + 1] = uv[uvOffset + localId * 3 + 1];
uvout[outOffset + myOffset * 3 + 2] = uv[uvOffset + localId * 3 + 2];
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 463 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 310 B

View File

@@ -0,0 +1,150 @@
/*
* Copyright (c) 2019, Adam <Adam@sigterm.info>
* 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.chat;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.testing.fieldbinder.Bind;
import com.google.inject.testing.fieldbinder.BoundFieldModule;
import java.awt.Color;
import net.runelite.api.ChatMessageType;
import net.runelite.api.Client;
import net.runelite.api.MessageNode;
import net.runelite.api.Player;
import net.runelite.api.events.ChatMessage;
import net.runelite.client.config.ChatColorConfig;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class ChatMessageManagerTest
{
@Mock
@Bind
private Client client;
@Mock
@Bind
private ChatColorConfig chatColorConfig;
@Inject
private ChatMessageManager chatMessageManager;
@Before
public void before()
{
Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this);
chatMessageManager.loadColors();
}
@Test
public void testMessageRecoloring()
{
when(chatColorConfig.opaqueServerMessage()).thenReturn(Color.decode("#b20000"));
chatMessageManager.loadColors();
ChatMessage chatMessage = new ChatMessage();
chatMessage.setType(ChatMessageType.GAMEMESSAGE);
MessageNode messageNode = mock(MessageNode.class);
chatMessage.setMessageNode(messageNode);
when(messageNode.getValue()).thenReturn("Your dodgy necklace protects you. It has <col=ff0000>1</col> charge left.");
chatMessageManager.onChatMessage(chatMessage);
verify(messageNode).setValue("<col=b20000>Your dodgy necklace protects you. It has <col=ff0000>1<col=b20000> charge left.</col>");
}
@Test
public void testPublicFriendUsernameRecolouring()
{
final String localPlayerName = "RuneLite";
final String friendName = "Zezima";
when(chatColorConfig.opaquePublicFriendUsernames()).thenReturn(Color.decode("#b20000"));
chatMessageManager.loadColors();
// Setup message
ChatMessage chatMessage = new ChatMessage();
chatMessage.setType(ChatMessageType.PUBLICCHAT);
chatMessage.setName(friendName);
MessageNode messageNode = mock(MessageNode.class);
chatMessage.setMessageNode(messageNode);
when(messageNode.getName()).thenReturn(friendName);
// Setup friend checking
Player localPlayer = mock(Player.class);
when(client.isFriended(friendName, true)).thenReturn(true);
when(client.getLocalPlayer()).thenReturn(localPlayer);
when(localPlayer.getName()).thenReturn(localPlayerName);
chatMessageManager.onChatMessage(chatMessage);
verify(messageNode).setName("<col=b20000>" + friendName + "</col>");
}
@Test
public void testPublicIronmanFriendUsernameRecolouring()
{
final String localPlayerName = "RuneLite";
final String friendName = "<img=3>BuddhaPuck";
final String sanitizedFriendName = "BuddhaPuck";
when(chatColorConfig.opaquePublicFriendUsernames()).thenReturn(Color.decode("#b20000"));
chatMessageManager.loadColors();
// Setup message
ChatMessage chatMessage = new ChatMessage();
chatMessage.setType(ChatMessageType.PUBLICCHAT);
chatMessage.setName(friendName);
MessageNode messageNode = mock(MessageNode.class);
chatMessage.setMessageNode(messageNode);
when(messageNode.getName()).thenReturn(friendName);
// Setup friend checking
Player localPlayer = mock(Player.class);
when(client.isFriended(sanitizedFriendName, true)).thenReturn(true);
when(client.getLocalPlayer()).thenReturn(localPlayer);
when(localPlayer.getName()).thenReturn(localPlayerName);
chatMessageManager.onChatMessage(chatMessage);
verify(messageNode).setName("<col=b20000>" + friendName + "</col>");
}
}

View File

@@ -0,0 +1,171 @@
/*
* Copyright (c) 2019 Hydrox6 <ikada@protonmail.ch>
* Copyright (c) 2019 Adam <Adam@sigterm.info>
* 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.plugins.cluescrolls;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.name.Named;
import com.google.inject.testing.fieldbinder.Bind;
import com.google.inject.testing.fieldbinder.BoundFieldModule;
import net.runelite.api.ChatMessageType;
import net.runelite.api.Client;
import net.runelite.api.NPC;
import net.runelite.api.Player;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.events.ChatMessage;
import net.runelite.api.events.GameTick;
import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetInfo;
import net.runelite.client.game.ItemManager;
import net.runelite.client.plugins.banktags.TagManager;
import net.runelite.client.plugins.cluescrolls.clues.hotcold.HotColdLocation;
import net.runelite.client.ui.overlay.OverlayManager;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.mockito.ArgumentMatchers.any;
import org.mockito.Mock;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class ClueScrollPluginTest
{
@Mock
@Bind
Client client;
@Inject
ClueScrollPlugin plugin;
@Bind
@Named("developerMode")
boolean developerMode;
@Mock
@Bind
ClueScrollConfig config;
@Mock
@Bind
OverlayManager overlayManager;
@Mock
@Bind
ItemManager itemManager;
@Mock
@Bind
TagManager tagManager;
@Before
public void before()
{
Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this);
}
@Test
public void getGetMirrorPoint()
{
WorldPoint point, converted;
// Zalcano's entrance portal
point = new WorldPoint(3282, 6058, 0);
converted = ClueScrollPlugin.getMirrorPoint(point, true);
assertNotEquals(point, converted);
// Elven Crystal Chest, which is upstairs
point = new WorldPoint(3273, 6082, 2);
converted = ClueScrollPlugin.getMirrorPoint(point, true);
assertNotEquals(point, converted);
// Around the area of the Elite coordinate clue
point = new WorldPoint(2185, 3280, 0);
// To overworld
converted = ClueScrollPlugin.getMirrorPoint(point, true);
assertEquals(point, converted);
// To real
converted = ClueScrollPlugin.getMirrorPoint(point, false);
assertNotEquals(point, converted);
// Brugsen Bursen, Grand Exchange
point = new WorldPoint(3165, 3477, 0);
converted = ClueScrollPlugin.getMirrorPoint(point, false);
assertEquals(point, converted);
}
@Test
public void testLocationHintArrowCleared()
{
final Widget clueWidget = mock(Widget.class);
when(clueWidget.getText()).thenReturn("Buried beneath the ground, who knows where it's found. Lucky for you, A man called Reldo may have a clue.");
final ChatMessage hotColdMessage = new ChatMessage();
hotColdMessage.setType(ChatMessageType.GAMEMESSAGE);
final Player localPlayer = mock(Player.class);
when(client.getWidget(WidgetInfo.CLUE_SCROLL_TEXT)).thenReturn(clueWidget);
when(client.getLocalPlayer()).thenReturn(localPlayer);
when(client.getPlane()).thenReturn(0);
when(client.getCachedNPCs()).thenReturn(new NPC[] {});
when(config.displayHintArrows()).thenReturn(true);
// The hint arrow should be reset each game tick from when the clue is read onward
// This is to verify the arrow is cleared the correct number of times during the clue updating process.
int clueSetupHintArrowClears = 0;
// Initialize a beginner hot-cold clue (which will have an end point of LUMBRIDGE_COW_FIELD)
plugin.onGameTick(new GameTick());
verify(client, times(++clueSetupHintArrowClears)).clearHintArrow();
// Perform the first hot-cold check in Lumbridge near sheep pen (get 2 possible points: LUMBRIDGE_COW_FIELD and DRAYNOR_WHEAT_FIELD)
when(localPlayer.getWorldLocation()).thenReturn(new WorldPoint(3208, 3254, 0));
hotColdMessage.setMessage("The device is hot.");
plugin.onChatMessage(hotColdMessage);
// Move to SW of DRAYNOR_WHEAT_FIELD (hint arrow should be visible here)
when(localPlayer.getWorldLocation()).thenReturn(new WorldPoint(3105, 3265, 0));
when(client.getBaseX()).thenReturn(3056);
when(client.getBaseY()).thenReturn(3216);
plugin.onGameTick(new GameTick());
verify(client, times(++clueSetupHintArrowClears)).clearHintArrow();
verify(client).setHintArrow(HotColdLocation.DRAYNOR_WHEAT_FIELD.getWorldPoint());
// Test in that location (get 1 possible location: LUMBRIDGE_COW_FIELD)
hotColdMessage.setMessage("The device is hot, and warmer than last time.");
plugin.onChatMessage(hotColdMessage);
plugin.onGameTick(new GameTick());
// Hint arrow should be cleared and not re-set now as the only remaining location is outside of the current
// scene
verify(client, times(++clueSetupHintArrowClears)).clearHintArrow();
verify(client, times(1)).setHintArrow(any(WorldPoint.class));
}
}

View File

@@ -0,0 +1,131 @@
/*
* Copyright (c) 2019, Adam <Adam@sigterm.info>
* 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.plugins.emojis;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.testing.fieldbinder.Bind;
import com.google.inject.testing.fieldbinder.BoundFieldModule;
import net.runelite.api.ChatMessageType;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.IndexedSprite;
import net.runelite.api.MessageNode;
import net.runelite.api.events.ChatMessage;
import net.runelite.api.events.GameStateChanged;
import net.runelite.client.chat.ChatMessageManager;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.mockito.junit.MockitoJUnitRunner;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
@RunWith(MockitoJUnitRunner.class)
public class EmojiPluginTest
{
@Mock
@Bind
private Client client;
@Mock
@Bind
private ChatMessageManager chatMessageManager;
@Inject
private EmojiPlugin emojiPlugin;
@Before
public void before()
{
Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this);
}
@Test
public void testOnChatMessage()
{
when(client.getGameState()).thenReturn(GameState.LOGGED_IN);
when(client.getModIcons()).thenReturn(new IndexedSprite[0]);
when(client.createIndexedSprite()).thenReturn(mock(IndexedSprite.class));
// Trip emoji loading
GameStateChanged gameStateChanged = new GameStateChanged();
gameStateChanged.setGameState(GameState.LOGGED_IN);
emojiPlugin.onGameStateChanged(gameStateChanged);
MessageNode messageNode = mock(MessageNode.class);
// With chat recolor, message may be wrapped in col tags
when(messageNode.getValue()).thenReturn("<col=ff0000>:) :) :)</col>");
ChatMessage chatMessage = new ChatMessage();
chatMessage.setType(ChatMessageType.PUBLICCHAT);
chatMessage.setMessageNode(messageNode);
emojiPlugin.onChatMessage(chatMessage);
verify(messageNode).setValue("<col=ff0000><img=0> <img=0> <img=0></col>");
}
@Test
public void testGtLt()
{
when(client.getGameState()).thenReturn(GameState.LOGGED_IN);
when(client.getModIcons()).thenReturn(new IndexedSprite[0]);
when(client.createIndexedSprite()).thenReturn(mock(IndexedSprite.class));
// Trip emoji loading
GameStateChanged gameStateChanged = new GameStateChanged();
gameStateChanged.setGameState(GameState.LOGGED_IN);
emojiPlugin.onGameStateChanged(gameStateChanged);
MessageNode messageNode = mock(MessageNode.class);
when(messageNode.getValue()).thenReturn("<gt>:D<lt>");
ChatMessage chatMessage = new ChatMessage();
chatMessage.setType(ChatMessageType.PUBLICCHAT);
chatMessage.setMessageNode(messageNode);
emojiPlugin.onChatMessage(chatMessage);
verify(messageNode).setValue("<img=10>");
}
@Test
public void testEmojiUpdateMessage()
{
String PARTY_POPPER = "<img=" + (-1 + Emoji.getEmoji("@@@").ordinal()) + '>';
String OPEN_MOUTH = "<img=" + (-1 + Emoji.getEmoji(":O").ordinal()) + '>';
assertNull(emojiPlugin.updateMessage("@@@@@"));
assertEquals(PARTY_POPPER, emojiPlugin.updateMessage("@@@"));
assertEquals(PARTY_POPPER + ' ' + PARTY_POPPER, emojiPlugin.updateMessage("@@@ @@@"));
assertEquals(PARTY_POPPER + ' ' + OPEN_MOUTH, emojiPlugin.updateMessage("@@@\u00A0:O"));
assertEquals(PARTY_POPPER + ' ' + OPEN_MOUTH + ' ' + PARTY_POPPER, emojiPlugin.updateMessage("@@@\u00A0:O @@@"));
assertEquals(PARTY_POPPER + " Hello World " + PARTY_POPPER, emojiPlugin.updateMessage("@@@\u00A0Hello World\u00A0@@@"));
}
}

View File

@@ -0,0 +1,148 @@
/*
* Copyright (c) 2020, Adam <Adam@sigterm.info>
* 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.plugins.grounditems;
import com.google.inject.Guice;
import com.google.inject.testing.fieldbinder.Bind;
import com.google.inject.testing.fieldbinder.BoundFieldModule;
import java.util.concurrent.ScheduledExecutorService;
import javax.inject.Inject;
import net.runelite.api.Client;
import net.runelite.api.ItemComposition;
import net.runelite.api.ItemID;
import net.runelite.api.ItemLayer;
import net.runelite.api.Player;
import net.runelite.api.Tile;
import net.runelite.api.TileItem;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.events.ItemSpawned;
import net.runelite.client.Notifier;
import net.runelite.client.events.ConfigChanged;
import net.runelite.client.game.ItemManager;
import net.runelite.client.input.KeyManager;
import net.runelite.client.input.MouseManager;
import net.runelite.client.plugins.grounditems.config.HighlightTier;
import net.runelite.client.ui.overlay.OverlayManager;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.mockito.ArgumentMatchers.any;
import org.mockito.Mock;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class GroundItemsPluginTest
{
@Inject
private GroundItemsPlugin groundItemsPlugin;
@Mock
@Bind
private MouseManager mouseManager;
@Mock
@Bind
private KeyManager keyManager;
@Mock
@Bind
private Client client;
@Mock
@Bind
private ItemManager itemManager;
@Mock
@Bind
private OverlayManager overlayManager;
@Mock
@Bind
private GroundItemsConfig config;
@Mock
@Bind
private GroundItemsOverlay overlay;
@Mock
@Bind
private Notifier notifier;
@Mock
@Bind
private ScheduledExecutorService executor;
@Before
public void setUp()
{
Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this);
doAnswer(a ->
{
a.<Runnable>getArgument(0).run();
return null;
}).when(executor).execute(any(Runnable.class));
when(client.getLocalPlayer()).thenReturn(mock(Player.class));
when(config.getHiddenItems()).thenReturn("");
}
@Test
public void testNotifyHighlightedItem()
{
when(config.getHighlightItems()).thenReturn("abyssal whip");
when(config.notifyTier()).thenReturn(HighlightTier.OFF);
when(config.notifyHighlightedDrops()).thenReturn(true);
when(itemManager.getItemComposition(ItemID.ABYSSAL_WHIP)).thenAnswer(a ->
{
ItemComposition itemComposition = mock(ItemComposition.class);
when(itemComposition.getName()).thenReturn("Abyssal whip");
return itemComposition;
});
// trigger reload of highlighted items list
ConfigChanged configChanged = new ConfigChanged();
configChanged.setGroup("grounditems");
groundItemsPlugin.onConfigChanged(configChanged);
// spawn whip
Tile tile = mock(Tile.class);
when(tile.getItemLayer()).thenReturn(mock(ItemLayer.class));
when(tile.getWorldLocation()).thenReturn(new WorldPoint(0, 0, 0));
TileItem tileItem = mock(TileItem.class);
when(tileItem.getId()).thenReturn(ItemID.ABYSSAL_WHIP);
when(tileItem.getQuantity()).thenReturn(1);
groundItemsPlugin.onItemSpawned(new ItemSpawned(tile, tileItem));
verify(notifier).notify("You received a highlighted drop: Abyssal whip");
}
}

View File

@@ -0,0 +1,323 @@
/*
* 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.client.plugins.idlenotifier;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.testing.fieldbinder.Bind;
import com.google.inject.testing.fieldbinder.BoundFieldModule;
import net.runelite.api.Actor;
import net.runelite.api.AnimationID;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.Hitsplat;
import net.runelite.api.NPC;
import net.runelite.api.NPCComposition;
import net.runelite.api.Player;
import net.runelite.api.VarPlayer;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.events.AnimationChanged;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.GameTick;
import net.runelite.api.events.HitsplatApplied;
import net.runelite.api.events.InteractingChanged;
import net.runelite.client.Notifier;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import org.mockito.Mock;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class IdleNotifierPluginTest
{
@Mock
@Bind
private Client client;
@Mock
@Bind
private IdleNotifierConfig config;
@Mock
@Bind
private Notifier notifier;
@Inject
private IdleNotifierPlugin plugin;
@Mock
private NPC monster;
@Mock
private NPC randomEvent;
@Mock
private NPC fishingSpot;
@Mock
private Player player;
@Before
public void setUp()
{
Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this);
// Mock monster
final String[] monsterActions = new String[] { "Attack", "Examine" };
final NPCComposition monsterComp = mock(NPCComposition.class);
when(monsterComp.getActions()).thenReturn(monsterActions);
when(monster.getComposition()).thenReturn(monsterComp);
// Mock random event
final String[] randomEventActions = new String[] { "Talk-to", "Dismiss", "Examine" };
final NPCComposition randomEventComp = mock(NPCComposition.class);
when(randomEventComp.getActions()).thenReturn(randomEventActions);
when(randomEvent.getComposition()).thenReturn(randomEventComp);
// Mock Fishing Spot
final String[] fishingSpotActions = new String[] { "Use-rod", "Examine" };
final NPCComposition fishingSpotComp = mock(NPCComposition.class);
when(fishingSpotComp.getActions()).thenReturn(fishingSpotActions);
when(fishingSpot.getComposition()).thenReturn(fishingSpotComp);
when(fishingSpot.getName()).thenReturn("Fishing spot");
// Mock player
when(player.getAnimation()).thenReturn(AnimationID.IDLE);
when(client.getLocalPlayer()).thenReturn(player);
// Mock config
when(config.logoutIdle()).thenReturn(true);
when(config.animationIdle()).thenReturn(true);
when(config.interactionIdle()).thenReturn(true);
when(config.getIdleNotificationDelay()).thenReturn(0);
when(config.getHitpointsThreshold()).thenReturn(42);
when(config.getPrayerThreshold()).thenReturn(42);
// Mock client
when(client.getGameState()).thenReturn(GameState.LOGGED_IN);
when(client.getKeyboardIdleTicks()).thenReturn(42);
when(client.getMouseLastPressedMillis()).thenReturn(System.currentTimeMillis() - 100_000L);
}
@Test
public void checkAnimationIdle()
{
when(player.getAnimation()).thenReturn(AnimationID.WOODCUTTING_BRONZE);
AnimationChanged animationChanged = new AnimationChanged();
animationChanged.setActor(player);
plugin.onAnimationChanged(animationChanged);
plugin.onGameTick(new GameTick());
when(player.getAnimation()).thenReturn(AnimationID.IDLE);
plugin.onAnimationChanged(animationChanged);
plugin.onGameTick(new GameTick());
verify(notifier).notify("You are now idle!");
}
@Test
public void checkAnimationReset()
{
when(player.getAnimation()).thenReturn(AnimationID.WOODCUTTING_BRONZE);
AnimationChanged animationChanged = new AnimationChanged();
animationChanged.setActor(player);
plugin.onAnimationChanged(animationChanged);
plugin.onGameTick(new GameTick());
when(player.getAnimation()).thenReturn(AnimationID.LOOKING_INTO);
plugin.onAnimationChanged(animationChanged);
plugin.onGameTick(new GameTick());
when(player.getAnimation()).thenReturn(AnimationID.IDLE);
plugin.onAnimationChanged(animationChanged);
plugin.onGameTick(new GameTick());
verify(notifier, times(0)).notify(any());
}
@Test
public void checkAnimationLogout()
{
when(player.getAnimation()).thenReturn(AnimationID.WOODCUTTING_BRONZE);
AnimationChanged animationChanged = new AnimationChanged();
animationChanged.setActor(player);
plugin.onAnimationChanged(animationChanged);
plugin.onGameTick(new GameTick());
// Logout
when(client.getGameState()).thenReturn(GameState.LOGIN_SCREEN);
GameStateChanged gameStateChanged = new GameStateChanged();
gameStateChanged.setGameState(GameState.LOGIN_SCREEN);
plugin.onGameStateChanged(gameStateChanged);
// Log back in
when(client.getGameState()).thenReturn(GameState.LOGGED_IN);
gameStateChanged.setGameState(GameState.LOGGED_IN);
plugin.onGameStateChanged(gameStateChanged);
// Tick
when(player.getAnimation()).thenReturn(AnimationID.IDLE);
plugin.onAnimationChanged(animationChanged);
plugin.onGameTick(new GameTick());
verify(notifier, times(0)).notify(any());
}
@Test
public void checkCombatIdle()
{
when(player.getInteracting()).thenReturn(monster);
plugin.onInteractingChanged(new InteractingChanged(player, monster));
plugin.onGameTick(new GameTick());
when(player.getInteracting()).thenReturn(null);
plugin.onInteractingChanged(new InteractingChanged(player, null));
plugin.onGameTick(new GameTick());
verify(notifier).notify("You are now out of combat!");
}
@Test
public void checkCombatReset()
{
when(player.getInteracting()).thenReturn(mock(Actor.class));
plugin.onInteractingChanged(new InteractingChanged(player, monster));
plugin.onGameTick(new GameTick());
plugin.onInteractingChanged(new InteractingChanged(player, randomEvent));
plugin.onGameTick(new GameTick());
plugin.onInteractingChanged(new InteractingChanged(player, null));
plugin.onGameTick(new GameTick());
verify(notifier, times(0)).notify(any());
}
@Test
public void checkCombatLogout()
{
plugin.onInteractingChanged(new InteractingChanged(player, monster));
when(player.getInteracting()).thenReturn(mock(Actor.class));
plugin.onGameTick(new GameTick());
// Logout
when(client.getGameState()).thenReturn(GameState.LOGIN_SCREEN);
GameStateChanged gameStateChanged = new GameStateChanged();
gameStateChanged.setGameState(GameState.LOGIN_SCREEN);
plugin.onGameStateChanged(gameStateChanged);
// Log back in
when(client.getGameState()).thenReturn(GameState.LOGGED_IN);
gameStateChanged.setGameState(GameState.LOGGED_IN);
plugin.onGameStateChanged(gameStateChanged);
// Tick
plugin.onInteractingChanged(new InteractingChanged(player, null));
plugin.onGameTick(new GameTick());
verify(notifier, times(0)).notify(any());
}
@Test
public void checkCombatLogoutIdle()
{
// Player is idle
when(client.getMouseIdleTicks()).thenReturn(80_000);
// But player is being damaged (is in combat)
final HitsplatApplied hitsplatApplied = new HitsplatApplied();
hitsplatApplied.setActor(player);
hitsplatApplied.setHitsplat(new Hitsplat(Hitsplat.HitsplatType.DAMAGE_ME, 0, 0));
plugin.onHitsplatApplied(hitsplatApplied);
plugin.onGameTick(new GameTick());
verify(notifier, times(0)).notify(any());
}
@Test
public void doubleNotifyOnMouseReset()
{
// Player is idle, but in combat so the idle packet is getting set repeatedly
// make sure we are not notifying
when(client.getKeyboardIdleTicks()).thenReturn(80_000);
when(client.getMouseIdleTicks()).thenReturn(14_500);
plugin.onGameTick(new GameTick());
plugin.onGameTick(new GameTick());
verify(notifier, times(1)).notify(any());
}
@Test
public void testSendOneNotificationForAnimationAndInteract()
{
when(player.getInteracting()).thenReturn(fishingSpot);
when(player.getAnimation()).thenReturn(AnimationID.FISHING_POLE_CAST);
AnimationChanged animationChanged = new AnimationChanged();
animationChanged.setActor(player);
plugin.onInteractingChanged(new InteractingChanged(player, fishingSpot));
plugin.onAnimationChanged(animationChanged);
plugin.onGameTick(new GameTick());
verify(notifier, never()).notify(anyString());
when(player.getAnimation()).thenReturn(AnimationID.IDLE);
lenient().when(player.getInteracting()).thenReturn(null);
plugin.onAnimationChanged(animationChanged);
plugin.onInteractingChanged(new InteractingChanged(player, null));
plugin.onGameTick(new GameTick());
verify(notifier).notify("You are now idle!");
}
@Test
public void testSpecRegen()
{
when(config.getSpecEnergyThreshold()).thenReturn(50);
when(client.getVar(eq(VarPlayer.SPECIAL_ATTACK_PERCENT))).thenReturn(400); // 40%
plugin.onGameTick(new GameTick()); // once to set lastSpecEnergy to 400
verify(notifier, never()).notify(any());
when(client.getVar(eq(VarPlayer.SPECIAL_ATTACK_PERCENT))).thenReturn(500); // 50%
plugin.onGameTick(new GameTick());
verify(notifier).notify(eq("You have restored spec energy!"));
}
@Test
public void testMovementIdle()
{
when(config.movementIdle()).thenReturn(true);
when(player.getWorldLocation()).thenReturn(new WorldPoint(0, 0, 0));
plugin.onGameTick(new GameTick());
when(player.getWorldLocation()).thenReturn(new WorldPoint(1, 0, 0));
plugin.onGameTick(new GameTick());
// No movement here
plugin.onGameTick(new GameTick());
verify(notifier).notify(eq("You have stopped moving!"));
}
}

View File

@@ -57,6 +57,7 @@ import org.mockito.Mock;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import org.mockito.junit.MockitoJUnitRunner; import org.mockito.junit.MockitoJUnitRunner;
@@ -67,7 +68,8 @@ public class ScreenshotPluginTest
private static final String BARROWS_CHEST = "Your Barrows chest count is <col=ff0000>310</col>"; private static final String BARROWS_CHEST = "Your Barrows chest count is <col=ff0000>310</col>";
private static final String CHAMBERS_OF_XERIC_CHEST = "Your completed Chambers of Xeric count is: <col=ff0000>489</col>."; private static final String CHAMBERS_OF_XERIC_CHEST = "Your completed Chambers of Xeric count is: <col=ff0000>489</col>.";
private static final String THEATRE_OF_BLOOD_CHEST = "Your completed Theatre of Blood count is: <col=ff0000>73</col>."; private static final String THEATRE_OF_BLOOD_CHEST = "Your completed Theatre of Blood count is: <col=ff0000>73</col>.";
private static final String VALUABLE_DROP = "<col=ef1020>Valuable drop: 6 x Bronze arrow (42 coins)</col>"; private static final String NOT_SO_VALUABLE_DROP = "<col=ef1020>Valuable drop: 6 x Bronze arrow (42 coins)</col>";
private static final String VALUABLE_DROP = "<col=ef1020>Valuable drop: Rune scimitar (25,600 coins)</col>";
private static final String UNTRADEABLE_DROP = "<col=ef1020>Untradeable drop: Rusty sword"; private static final String UNTRADEABLE_DROP = "<col=ef1020>Untradeable drop: Rusty sword";
private static final String BA_HIGH_GAMBLE_REWARD = "Raw shark (x 300)!<br>High level gamble count: <col=7f0000>100</col>"; private static final String BA_HIGH_GAMBLE_REWARD = "Raw shark (x 300)!<br>High level gamble count: <col=7f0000>100</col>";
private static final String HUNTER_LEVEL_2_TEXT = "<col=000080>Congratulations, you've just advanced a Hunter level.<col=000000><br><br>Your Hunter level is now 2."; private static final String HUNTER_LEVEL_2_TEXT = "<col=000080>Congratulations, you've just advanced a Hunter level.<col=000000><br><br>Your Hunter level is now 2.";
@@ -121,6 +123,7 @@ public class ScreenshotPluginTest
Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this); Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this);
when(screenshotConfig.screenshotLevels()).thenReturn(true); when(screenshotConfig.screenshotLevels()).thenReturn(true);
when(screenshotConfig.screenshotValuableDrop()).thenReturn(true); when(screenshotConfig.screenshotValuableDrop()).thenReturn(true);
when(screenshotConfig.valuableDropThreshold()).thenReturn(1000);
when(screenshotConfig.screenshotUntradeableDrop()).thenReturn(true); when(screenshotConfig.screenshotUntradeableDrop()).thenReturn(true);
} }
@@ -161,10 +164,30 @@ public class ScreenshotPluginTest
assertEquals(73, screenshotPlugin.gettheatreOfBloodNumber()); assertEquals(73, screenshotPlugin.gettheatreOfBloodNumber());
} }
@Test
public void testNotSoValuableDrop()
{
ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "", NOT_SO_VALUABLE_DROP, null, 0);
screenshotPlugin.onChatMessage(chatMessageEvent);
verifyNoInteractions(drawManager);
when(screenshotConfig.valuableDropThreshold()).thenReturn(0);
screenshotPlugin.onChatMessage(chatMessageEvent);
verify(drawManager).requestNextFrameListener(any(Consumer.class));
}
@Test @Test
public void testValuableDrop() public void testValuableDrop()
{ {
ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "", VALUABLE_DROP, null, 0); ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "", VALUABLE_DROP, null, 0);
when(screenshotConfig.valuableDropThreshold()).thenReturn(100_000);
screenshotPlugin.onChatMessage(chatMessageEvent);
verifyNoInteractions(drawManager);
when(screenshotConfig.valuableDropThreshold()).thenReturn(1000);
screenshotPlugin.onChatMessage(chatMessageEvent); screenshotPlugin.onChatMessage(chatMessageEvent);
verify(drawManager).requestNextFrameListener(any(Consumer.class)); verify(drawManager).requestNextFrameListener(any(Consumer.class));

View File

@@ -0,0 +1,132 @@
/*
* Copyright (c) 2021, Adam <Adam@sigterm.info>
* 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.plugins.timetracking.farming;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.testing.fieldbinder.Bind;
import com.google.inject.testing.fieldbinder.BoundFieldModule;
import java.util.EnumSet;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.Player;
import net.runelite.api.Varbits;
import net.runelite.api.WorldType;
import net.runelite.client.Notifier;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.config.RuneScapeProfile;
import net.runelite.client.config.RuneScapeProfileType;
import net.runelite.client.game.ItemManager;
import net.runelite.client.plugins.timetracking.TimeTrackingConfig;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class FarmingTrackerTest
{
@Inject
private FarmingTracker farmingTracker;
@Mock
@Bind
private Client client;
@Mock
@Bind
private ItemManager itemManager;
@Mock
@Bind
private ConfigManager configManager;
@Mock
@Bind
private TimeTrackingConfig config;
@Mock
@Bind
private FarmingWorld farmingWorld;
@Mock
@Bind
private Notifier notifier;
@Before
public void before()
{
Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this);
when(client.getGameState()).thenReturn(GameState.LOGGED_IN);
when(client.getWorldType()).thenReturn(EnumSet.noneOf(WorldType.class));
Player player = mock(Player.class);
when(player.getName()).thenReturn("Adam");
when(client.getLocalPlayer()).thenReturn(player);
}
@Test(expected = IllegalStateException.class)
public void testEmptyNotification()
{
RuneScapeProfile runeScapeProfile = new RuneScapeProfile("Adam", RuneScapeProfileType.STANDARD, null, null);
PatchPrediction patchPrediction = new PatchPrediction(Produce.EMPTY_COMPOST_BIN, CropState.EMPTY, 0L, 0, 0);
FarmingRegion region = new FarmingRegion("Ardougne", 10548, false,
new FarmingPatch("North", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT),
new FarmingPatch("South", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT),
new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER),
new FarmingPatch("", Varbits.FARMING_4774, PatchImplementation.HERB),
new FarmingPatch("", Varbits.FARMING_4775, PatchImplementation.COMPOST)
);
FarmingPatch patch = region.getPatches()[4];
patch.setRegion(region);
farmingTracker.sendNotification(runeScapeProfile, patchPrediction, patch);
}
@Test
public void testHarvestableNotification()
{
RuneScapeProfile runeScapeProfile = new RuneScapeProfile("Adam", RuneScapeProfileType.STANDARD, null, null);
PatchPrediction patchPrediction = new PatchPrediction(Produce.RANARR, CropState.HARVESTABLE, 0L, 0, 0);
FarmingRegion region = new FarmingRegion("Ardougne", 10548, false,
new FarmingPatch("North", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT),
new FarmingPatch("South", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT),
new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER),
new FarmingPatch("", Varbits.FARMING_4774, PatchImplementation.HERB),
new FarmingPatch("", Varbits.FARMING_4775, PatchImplementation.COMPOST)
);
FarmingPatch patch = region.getPatches()[3];
patch.setRegion(region);
farmingTracker.sendNotification(runeScapeProfile, patchPrediction, patch);
verify(notifier).notify("Your Ranarr is ready to harvest in Ardougne.");
}
}

View File

@@ -536,17 +536,36 @@ public abstract class RSClientMixin implements RSClient
} }
@Inject @Inject
public void addChatMessage(int type, String name, String message, String sender) public MessageNode addChatMessage(ChatMessageType type, String name, String message, String sender, boolean postEvent)
{ {
assert this.isClientThread() : "addChatMessage must be called on client thread"; assert this.isClientThread() : "addChatMessage must be called on client thread";
addRSChatMessage(type, name, message, sender); copy$addChatMessage(type.getType(), name, message, sender);
Logger logger = client.getLogger();
if (logger.isDebugEnabled())
{
logger.debug("Chat message type {}: {}", type.name(), message);
}
// Get the message node which was added
@SuppressWarnings("unchecked") Map<Integer, RSChatChannel> chatLineMap = client.getChatLineMap();
RSChatChannel chatLineBuffer = chatLineMap.get(type.getType());
MessageNode messageNode = chatLineBuffer.getLines()[0];
if (postEvent)
{
final ChatMessage chatMessage = new ChatMessage(messageNode, type, name, message, sender, messageNode.getTimestamp());
client.getCallbacks().post(chatMessage);
}
return messageNode;
} }
@Inject @Inject
@Override @Override
public void addChatMessage(ChatMessageType type, String name, String message, String sender) public MessageNode addChatMessage(ChatMessageType type, String name, String message, String sender)
{ {
addChatMessage(type.getType(), name, message, sender); return addChatMessage(type, name, message, sender, true);
} }
@Inject @Inject
@@ -1484,14 +1503,17 @@ public abstract class RSClientMixin implements RSClient
client.getCallbacks().updateNpcs(); client.getCallbacks().updateNpcs();
} }
@Inject @Copy("addChatMessage")
@MethodHook(value = "addChatMessage", end = true) @Replace("addChatMessage")
public static void onAddChatMessage(int type, String name, String message, String sender) public static void copy$addChatMessage(int type, String name, String message, String sender)
{ {
copy$addChatMessage(type, name, message, sender);
Logger logger = client.getLogger(); Logger logger = client.getLogger();
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
{ {
logger.debug("Chat message type {}: {}", ChatMessageType.of(type), message); ChatMessageType msgType = ChatMessageType.of(type);
logger.debug("Chat message type {}: {}", msgType == ChatMessageType.UNKNOWN ? String.valueOf(type) : msgType.name(), message);
} }
// Get the message node which was added // Get the message node which was added