diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt index 2faa6f92ad..6576da943e 100644 --- a/buildSrc/src/main/kotlin/Dependencies.kt +++ b/buildSrc/src/main/kotlin/Dependencies.kt @@ -25,9 +25,9 @@ object ProjectVersions { const val launcherVersion = "2.2.0" - const val rlVersion = "1.7.19" + const val rlVersion = "1.7.20" - const val openosrsVersion = "4.9.10" + const val openosrsVersion = "4.9.11" const val rsversion = 198 const val cacheversion = 165 diff --git a/runelite-api/src/main/java/net/runelite/api/Actor.java b/runelite-api/src/main/java/net/runelite/api/Actor.java index b960461f64..5c93f47b78 100644 --- a/runelite-api/src/main/java/net/runelite/api/Actor.java +++ b/runelite-api/src/main/java/net/runelite/api/Actor.java @@ -171,6 +171,8 @@ public interface Actor extends Renderable, Locatable */ int getIdleRotateLeft(); + void setIdleRotateLeft(int animationID); + /** * Animation used for rotating right if the actor is also not walking * @@ -179,6 +181,8 @@ public interface Actor extends Renderable, Locatable */ int getIdleRotateRight(); + void setIdleRotateRight(int animationID); + /** * Animation used for walking * @@ -187,6 +191,8 @@ public interface Actor extends Renderable, Locatable */ int getWalkAnimation(); + void setWalkAnimation(int animationID); + /** * Animation used for rotating left while walking * @@ -195,6 +201,8 @@ public interface Actor extends Renderable, Locatable */ int getWalkRotateLeft(); + void setWalkRotateLeft(int animationID); + /** * Animation used for rotating right while walking * @@ -203,6 +211,8 @@ public interface Actor extends Renderable, Locatable */ int getWalkRotateRight(); + void setWalkRotateRight(int animationID); + /** * Animation used for an about-face while walking * @@ -211,6 +221,8 @@ public interface Actor extends Renderable, Locatable */ int getWalkRotate180(); + void setWalkRotate180(int animationID); + /** * Animation used for running * @@ -219,6 +231,8 @@ public interface Actor extends Renderable, Locatable */ int getRunAnimation(); + void setRunAnimation(int animationID); + /** * Sets an animation for the actor to perform. * diff --git a/runelite-api/src/main/java/net/runelite/api/Nameable.java b/runelite-api/src/main/java/net/runelite/api/Nameable.java index f627d98901..b75cd7c731 100644 --- a/runelite-api/src/main/java/net/runelite/api/Nameable.java +++ b/runelite-api/src/main/java/net/runelite/api/Nameable.java @@ -27,7 +27,7 @@ package net.runelite.api; /** * Represents a chat entity that has a name. */ -public interface Nameable extends Comparable +public interface Nameable extends Comparable { /** * The name of the player. diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/chatchannel/ChatChannelPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/chatchannel/ChatChannelPlugin.java index d58fa8ad27..e4584f60a5 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/chatchannel/ChatChannelPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/chatchannel/ChatChannelPlugin.java @@ -26,6 +26,7 @@ */ package net.runelite.client.plugins.chatchannel; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.MoreObjects; import com.google.common.base.Strings; import com.google.common.collect.Lists; @@ -36,9 +37,9 @@ import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; import java.util.Iterator; -import java.util.LinkedHashMap; +import java.util.LinkedList; import java.util.List; -import java.util.Map; +import java.util.ListIterator; import javax.inject.Inject; import lombok.AllArgsConstructor; import net.runelite.api.ChatLineBuffer; @@ -101,7 +102,8 @@ public class ChatChannelPlugin extends Plugin { private static final int MAX_CHATS = 10; private static final String RECENT_TITLE = "Recent FCs"; - private static final int MESSAGE_DELAY = 10; + @VisibleForTesting + static final int MESSAGE_DELAY = 10; @Inject private Client client; @@ -129,7 +131,7 @@ public class ChatChannelPlugin extends Plugin * queue of temporary messages added to the client */ private final Deque joinMessages = new ArrayDeque<>(); - private final Map activityBuffer = new LinkedHashMap<>(); + private final List activityBuffer = new LinkedList<>(); private int joinedTick; private boolean kickConfirmed = false; @@ -284,31 +286,38 @@ public class ChatChannelPlugin extends Plugin private void queueJoin(ChatPlayer member, MemberActivity.ChatType chatType) { - // attempt to filter out world hopping joins - if (!activityBuffer.containsKey(member)) + for (ListIterator iter = activityBuffer.listIterator(); iter.hasNext(); ) { - MemberActivity joinActivity = new MemberActivity(ActivityType.JOINED, chatType, - member, client.getTickCount()); - activityBuffer.put(member, joinActivity); - } - else - { - activityBuffer.remove(member); + MemberActivity activity = iter.next(); + + if (activity.getChatType() == chatType && activity.getMember().compareTo(member) == 0) + { + iter.remove(); + return; + } } + + MemberActivity activity = new MemberActivity(ActivityType.JOINED, chatType, + member, client.getTickCount()); + activityBuffer.add(activity); } private void queueLeave(ChatPlayer member, MemberActivity.ChatType chatType) { - if (!activityBuffer.containsKey(member)) + for (ListIterator iter = activityBuffer.listIterator(); iter.hasNext(); ) { - MemberActivity leaveActivity = new MemberActivity(ActivityType.LEFT, chatType, - member, client.getTickCount()); - activityBuffer.put(member, leaveActivity); - } - else - { - activityBuffer.remove(member); + MemberActivity activity = iter.next(); + + if (activity.getChatType() == chatType && activity.getMember().compareTo(member) == 0) + { + iter.remove(); + return; + } } + + MemberActivity activity = new MemberActivity(ActivityType.LEFT, chatType, + member, client.getTickCount()); + activityBuffer.add(activity); } @Subscribe @@ -386,32 +395,33 @@ public class ChatChannelPlugin extends Plugin } } - private void addActivityMessages() + @VisibleForTesting + void addActivityMessages() { if (activityBuffer.isEmpty()) { return; } - Iterator activityIt = activityBuffer.values().iterator(); - - while (activityIt.hasNext()) + for (ListIterator iter = activityBuffer.listIterator(); iter.hasNext(); ) { - MemberActivity activity = activityIt.next(); - - if (activity.getTick() < client.getTickCount() - MESSAGE_DELAY) + MemberActivity activity = iter.next(); + if (activity.getTick() >= client.getTickCount() - MESSAGE_DELAY) { - activityIt.remove(); - switch (activity.getChatType()) - { - case FRIENDS_CHAT: - addActivityMessage((FriendsChatMember) activity.getMember(), activity.getActivityType()); - break; - case CLAN_CHAT: - case GUEST_CHAT: - addClanActivityMessage((ClanChannelMember) activity.getMember(), activity.getActivityType(), activity.getChatType()); - break; - } + // everything after this is older + return; + } + + iter.remove(); + switch (activity.getChatType()) + { + case FRIENDS_CHAT: + addActivityMessage((FriendsChatMember) activity.getMember(), activity.getActivityType()); + break; + case CLAN_CHAT: + case GUEST_CHAT: + addClanActivityMessage((ClanChannelMember) activity.getMember(), activity.getActivityType(), activity.getChatType()); + break; } } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CrypticClue.java b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CrypticClue.java index 4884119467..0e8ce89b34 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CrypticClue.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CrypticClue.java @@ -322,7 +322,7 @@ public class CrypticClue extends ClueScroll implements TextClueScroll, NpcClueSc new CrypticClue("Has no one told you it is rude to ask a lady her age?", "Mawrth", new WorldPoint(2333, 3165, 0), "Talk to Mawrth in Lletya."), new CrypticClue("Elvish onions.", new WorldPoint(3303, 6092, 0), "Dig in the onion patch east of the Prifddinas allotments."), new CrypticClue("Dig by the Giant's Den entrance, looking out over Lake Molch.", new WorldPoint(1418, 3591, 0), "South-east of Lake Molch in Zeah, outside the cave entrance."), - new CrypticClue("Search the crates in the fruit store just east of the Hosidius town centre.", CRATES_27533, new WorldPoint(1798, 3612, 0), "Search the crates in the back room of the Hosidius fruit store."), + new CrypticClue("Search the crates in the fruit store just east of the Hosidius town centre.", CRATES_27533, new WorldPoint(1799, 3613, 0), "Search the crates in the back room of the Hosidius fruit store."), new CrypticClue("A graceful man of many colours, his crates must be full of many delights.", "Hill Giant", CRATE_42067, new WorldPoint(1506, 3590, 2), "Kill any Hill Giant for a medium key. Then search the crate on the top floor of Osten's clothing shop in Shayzien."), new CrypticClue("Search the basket of apples in an orchard, south of the unknown grave surrounded by white roses.", APPLE_BASKET, new WorldPoint(1718, 3626, 0), "Search the middle apple basket in the apple orchard north of Hosidius."), new CrypticClue("Dig in the lair of red wings, within the temple of the Sun and Moon.", new WorldPoint(1820, 9935, 0), "Forthos Dungeon. In the center of the red dragons.") diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xpupdater/XpUpdaterPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/xpupdater/XpUpdaterPlugin.java index 1a529b3328..73f295ccaa 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/xpupdater/XpUpdaterPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xpupdater/XpUpdaterPlugin.java @@ -28,6 +28,7 @@ package net.runelite.client.plugins.xpupdater; import com.google.inject.Provides; import java.io.IOException; +import java.util.EnumSet; import java.util.Objects; import javax.inject.Inject; import lombok.extern.slf4j.Slf4j; @@ -132,53 +133,72 @@ public class XpUpdaterPlugin extends Plugin private void update(String username) { - String reformedUsername = username.replace(" ", "_"); + EnumSet worldTypes = client.getWorldType(); + username = username.replace(" ", "_"); + updateCml(username, worldTypes); + updateTempleosrs(username, worldTypes); + updateWom(username, worldTypes); + } - if (config.cml()) + private void updateCml(String username, EnumSet worldTypes) + { + if (config.cml() + && !worldTypes.contains(WorldType.LEAGUE) + && !worldTypes.contains(WorldType.DEADMAN) + && !worldTypes.contains(WorldType.TOURNAMENT)) { HttpUrl url = new HttpUrl.Builder() - .scheme("https") - .host("crystalmathlabs.com") - .addPathSegment("tracker") - .addPathSegment("api.php") - .addQueryParameter("type", "update") - .addQueryParameter("player", reformedUsername) - .build(); + .scheme("https") + .host("crystalmathlabs.com") + .addPathSegment("tracker") + .addPathSegment("api.php") + .addQueryParameter("type", "update") + .addQueryParameter("player", username) + .build(); Request request = new Request.Builder() - .header("User-Agent", "RuneLite") - .url(url) - .build(); + .header("User-Agent", "RuneLite") + .url(url) + .build(); sendRequest("CrystalMathLabs", request); } + } - if (config.templeosrs()) + private void updateTempleosrs(String username, EnumSet worldTypes) + { + if (config.templeosrs() + && !worldTypes.contains(WorldType.LEAGUE) + && !worldTypes.contains(WorldType.DEADMAN) + && !worldTypes.contains(WorldType.TOURNAMENT)) { HttpUrl url = new HttpUrl.Builder() - .scheme("https") - .host("templeosrs.com") - .addPathSegment("php") - .addPathSegment("add_datapoint.php") - .addQueryParameter("player", reformedUsername) - .build(); + .scheme("https") + .host("templeosrs.com") + .addPathSegment("php") + .addPathSegment("add_datapoint.php") + .addQueryParameter("player", username) + .build(); Request request = new Request.Builder() - .header("User-Agent", "RuneLite") - .url(url) - .build(); + .header("User-Agent", "RuneLite") + .url(url) + .build(); sendRequest("TempleOSRS", request); } + } - if (config.wiseoldman()) + private void updateWom(String username, EnumSet worldTypes) + { + if (config.wiseoldman() + && !worldTypes.contains(WorldType.LEAGUE) + && !worldTypes.contains(WorldType.DEADMAN) + && !worldTypes.contains(WorldType.TOURNAMENT)) { - final boolean leagueWorld = client.getWorldType().contains(WorldType.LEAGUE); - final String host = leagueWorld ? "trailblazer.wiseoldman.net" : "wiseoldman.net"; - HttpUrl url = new HttpUrl.Builder() .scheme("https") - .host(host) + .host("wiseoldman.net") .addPathSegment("api") .addPathSegment("players") .addPathSegment("track") diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/roofremoval/overrides.jsonc b/runelite-client/src/main/resources/net/runelite/client/plugins/roofremoval/overrides.jsonc index b359eea839..b831d41f43 100644 --- a/runelite-client/src/main/resources/net/runelite/client/plugins/roofremoval/overrides.jsonc +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/roofremoval/overrides.jsonc @@ -456,7 +456,7 @@ "rx2": 29, "ry2": 35, "z1": 0, - "z2": 0 + "z2": 1 }, { "rx1": 16, @@ -464,7 +464,7 @@ "rx2": 19, "ry2": 37, "z1": 0, - "z2": 0 + "z2": 1 } ], "12854": [ // Varrock Castle diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/chatchannel/ChatChannelPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/chatchannel/ChatChannelPluginTest.java new file mode 100644 index 0000000000..00f5307cb4 --- /dev/null +++ b/runelite-client/src/test/java/net/runelite/client/plugins/chatchannel/ChatChannelPluginTest.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2021, Adam + * 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.chatchannel; + +import com.google.inject.Guice; +import com.google.inject.testing.fieldbinder.Bind; +import com.google.inject.testing.fieldbinder.BoundFieldModule; +import javax.inject.Inject; +import net.runelite.api.ChatMessageType; +import net.runelite.api.Client; +import net.runelite.api.MessageNode; +import net.runelite.api.clan.ClanChannel; +import net.runelite.api.clan.ClanChannelMember; +import net.runelite.api.clan.ClanRank; +import net.runelite.api.clan.ClanSettings; +import net.runelite.api.events.ClanMemberJoined; +import net.runelite.api.events.ClanMemberLeft; +import net.runelite.client.callback.ClientThread; +import net.runelite.client.chat.ChatMessageManager; +import net.runelite.client.config.ChatColorConfig; +import net.runelite.client.game.ChatIconManager; +import net.runelite.client.game.chatbox.ChatboxPanelManager; +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 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.verify; +import static org.mockito.Mockito.when; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class ChatChannelPluginTest +{ + @Inject + private ChatChannelPlugin chatChannelPlugin; + + @Mock + @Bind + private Client client; + + @Mock + @Bind + private ChatIconManager chatIconManager; + + @Mock + @Bind + private ChatChannelConfig config; + + @Mock + @Bind + private ClientThread clientThread; + + @Mock + @Bind + private ChatboxPanelManager chatboxPanelManager; + + @Mock + @Bind + private ChatColorConfig chatColorConfig; + + @Mock + @Bind + private ChatMessageManager chatMessageManager; + + @Before + public void before() + { + Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this); + } + + @Test + public void testJoinLeave() + { + ClanChannel channel = mock(ClanChannel.class); + ClanSettings settings = mock(ClanSettings.class); + + when(client.getClanChannel()).thenReturn(channel); + lenient().when(client.getClanSettings()).thenReturn(settings); + when(config.clanChatShowJoinLeave()).thenReturn(true); + lenient().when(client.addChatMessage(any(ChatMessageType.class), anyString(), anyString(), anyString())).thenAnswer(a -> mock(MessageNode.class)); + + ClanChannelMember member = mock(ClanChannelMember.class); + lenient().when(member.getRank()).thenReturn(ClanRank.OWNER); + chatChannelPlugin.onClanMemberLeft(new ClanMemberLeft(channel, member)); + + ClanChannelMember member2 = mock(ClanChannelMember.class); + lenient().when(member2.getRank()).thenReturn(ClanRank.OWNER); + chatChannelPlugin.onClanMemberJoined(new ClanMemberJoined(channel, member2)); + + lenient().when(client.getTickCount()).thenReturn(ChatChannelPlugin.MESSAGE_DELAY + 1); + chatChannelPlugin.addActivityMessages(); + + verify(client, never()).addChatMessage(any(ChatMessageType.class), anyString(), anyString(), anyString()); + verify(member).compareTo(member2); + } +} \ No newline at end of file diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSClanChannelMemberMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSClanChannelMemberMixin.java index 0c9556f5ad..1369e9618c 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSClanChannelMemberMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSClanChannelMemberMixin.java @@ -24,6 +24,7 @@ */ package net.runelite.mixins; +import net.runelite.api.Nameable; import net.runelite.api.clan.ClanRank; import net.runelite.api.mixins.Inject; import net.runelite.api.mixins.Mixin; @@ -39,9 +40,9 @@ public abstract class RSClanChannelMemberMixin implements RSClanChannelMember @Inject @Override - public int compareTo(Object other) + public int compareTo(Nameable other) { - return getName().compareTo(((RSClanChannelMember) other).getName()); + return getName().compareTo(other.getName()); } @Inject diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSUser.java b/runescape-api/src/main/java/net/runelite/rs/api/RSUser.java index 798e7a8aa8..fdedc8633e 100644 --- a/runescape-api/src/main/java/net/runelite/rs/api/RSUser.java +++ b/runescape-api/src/main/java/net/runelite/rs/api/RSUser.java @@ -3,7 +3,7 @@ package net.runelite.rs.api; import net.runelite.api.Nameable; import net.runelite.mapping.Import; -public interface RSUser extends Nameable, Comparable +public interface RSUser extends Nameable, Comparable { @Import("username") RSUsername getRsName();