From 19b27d11fc07f79c386317f8a0f2e71d69f21a4a Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 27 Oct 2019 18:45:13 -0400 Subject: [PATCH 1/3] raids plugin: refactor RaidRoom --- .../runelite/client/plugins/raids/Raid.java | 41 +---- .../client/plugins/raids/RaidRoom.java | 159 +++--------------- .../client/plugins/raids/RaidsOverlay.java | 16 +- .../client/plugins/raids/RaidsPlugin.java | 69 +++----- .../client/plugins/raids/RoomType.java | 80 +++++++++ .../raids/{solver => }/RotationSolver.java | 67 +++----- .../plugins/raids/RotationSolverTest.java | 72 ++++++++ 7 files changed, 241 insertions(+), 263 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/raids/RoomType.java rename runelite-client/src/main/java/net/runelite/client/plugins/raids/{solver => }/RotationSolver.java (57%) create mode 100644 runelite-client/src/test/java/net/runelite/client/plugins/raids/RotationSolverTest.java diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/raids/Raid.java b/runelite-client/src/main/java/net/runelite/client/plugins/raids/Raid.java index a0baabe691..4f537eb44e 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/raids/Raid.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/raids/Raid.java @@ -60,19 +60,8 @@ public class Raid if (room == null) { - RaidRoom.Type type = RaidRoom.Type.fromCode(layout.getRoomAt(i).getSymbol()); - room = new RaidRoom(null, type); - - if (type == RaidRoom.Type.COMBAT) - { - room.setBoss(RaidRoom.Boss.UNKNOWN); - } - - if (type == RaidRoom.Type.PUZZLE) - { - room.setPuzzle(RaidRoom.Puzzle.UNKNOWN); - } - + RoomType type = RoomType.fromCode(layout.getRoomAt(i).getSymbol()); + room = type.getUnsolvedRoom(); setRoom(room, i); } } @@ -102,7 +91,7 @@ public class Raid continue; } - if (rooms[room.getPosition()].getType() == RaidRoom.Type.COMBAT) + if (rooms[room.getPosition()].getType() == RoomType.COMBAT) { combatRooms.add(rooms[room.getPosition()]); } @@ -113,7 +102,7 @@ public class Raid public String getRotationString() { - return Joiner.on(",").join(Arrays.stream(getCombatRooms()).map(r -> r.getBoss().getName()).toArray()); + return Joiner.on(",").join(Arrays.stream(getCombatRooms()).map(RaidRoom::getName).toArray()); } public String toCode() @@ -144,7 +133,7 @@ public class Raid final int position = r.getPosition(); final RaidRoom room = getRoom(position); - if (room == null || !(room.getType() == RaidRoom.Type.COMBAT || room.getType() == RaidRoom.Type.PUZZLE)) + if (room == null) { continue; } @@ -152,26 +141,8 @@ public class Raid switch (room.getType()) { case PUZZLE: - final RaidRoom.Puzzle puzzle = room.getPuzzle(); - sb.append(puzzle.getName()); - - if (puzzle == RaidRoom.Puzzle.UNKNOWN) - { - sb.append(" (puzzle)"); - } - - sb.append(", "); - break; case COMBAT: - final RaidRoom.Boss boss = room.getBoss(); - sb.append(boss.getName()); - - if (boss == RaidRoom.Boss.UNKNOWN) - { - sb.append(" (combat)"); - } - - sb.append(", "); + sb.append(room.getName()).append(", "); break; } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidRoom.java b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidRoom.java index 23df0ff407..06101d6e08 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidRoom.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidRoom.java @@ -24,144 +24,37 @@ */ package net.runelite.client.plugins.raids; -import lombok.AllArgsConstructor; import lombok.Getter; -import lombok.Setter; -import net.runelite.api.Tile; +import lombok.RequiredArgsConstructor; -public class RaidRoom +@RequiredArgsConstructor +@Getter +enum RaidRoom { - public static final int ROOM_MAX_SIZE = 32; + START("Start", RoomType.START), + END("End", RoomType.END), + SCAVENGERS("Scavengers", RoomType.SCAVENGERS), + FARMING("Farming", RoomType.FARMING), + EMPTY("Empty", RoomType.EMPTY), - @AllArgsConstructor - public enum Type - { - START("Start", "#"), - END("End", "¤"), - SCAVENGERS("Scavengers", "S"), - FARMING("Farming", "F"), - COMBAT("Combat", "C"), - PUZZLE("Puzzle", "P"), - EMPTY("Empty", " "); + TEKTON("Tekton", RoomType.COMBAT), + MUTTADILES("Muttadiles", RoomType.COMBAT), + GUARDIANS("Guardians", RoomType.COMBAT), + VESPULA("Vespula", RoomType.COMBAT), + SHAMANS("Shamans", RoomType.COMBAT), + VASA("Vasa", RoomType.COMBAT), + VANGUARDS("Vanguards", RoomType.COMBAT), + MYSTICS("Mystics", RoomType.COMBAT), + UNKNOWN_COMBAT("Unknown (combat)", RoomType.COMBAT), - @Getter - private final String name; + CRABS("Crabs", RoomType.PUZZLE), + ICE_DEMON("Ice Demon", RoomType.PUZZLE), + TIGHTROPE("Tightrope", RoomType.PUZZLE), + THIEVING("Thieving", RoomType.PUZZLE), + UNKNOWN_PUZZLE("Unknown (puzzle)", RoomType.PUZZLE); - @Getter - private final String code; + static final int ROOM_MAX_SIZE = 32; - public static Type fromCode(char code) - { - for (Type type : Type.values()) - { - if (type.getCode().equalsIgnoreCase(String.valueOf(code))) - { - return type; - } - } - - return Type.EMPTY; - } - } - - @AllArgsConstructor - public enum Boss - { - TEKTON("Tekton"), - MUTTADILES("Muttadiles"), - GUARDIANS("Guardians"), - VESPULA("Vespula"), - SHAMANS("Shamans"), - VASA("Vasa"), - VANGUARDS("Vanguards"), - MYSTICS("Mystics"), - UNKNOWN("Unknown"); - - @Getter - private final String name; - - public static Boss fromString(String name) - { - for (Boss boss : Boss.values()) - { - if (boss.getName().equalsIgnoreCase(name)) - { - return boss; - } - } - - return null; - } - } - - @AllArgsConstructor - public enum Puzzle - { - CRABS("Crabs"), - ICE_DEMON("Ice Demon"), - TIGHTROPE("Tightrope"), - THIEVING("Thieving"), - UNKNOWN("Unknown"); - - @Getter - private final String name; - - public static Puzzle fromString(String name) - { - for (Puzzle puzzle : Puzzle.values()) - { - if (puzzle.getName().equalsIgnoreCase(name)) - { - return puzzle; - } - } - - return null; - } - } - - @Getter - private final Tile base; - - @Getter - @Setter - private Type type; - - @Getter - @Setter - private Boss boss; - - @Getter - @Setter - private Puzzle puzzle; - - @Getter - @Setter - private RaidRoom previousRoom; - - @Getter - @Setter - private RaidRoom nextRoom; - - public RaidRoom(Tile base, Type type) - { - this.base = base; - this.type = type; - } - - @Override - public String toString() - { - switch (type) - { - case COMBAT: - return "RaidRoom (type: " + type.getName() + ", " + boss.getName() + ")"; - - case PUZZLE: - return "RaidRoom (type: " + type.getName() + ", " + puzzle.getName() + ")"; - - default: - return "RaidRoom (type: " + type.getName() + ")"; - } - } + private final String name; + private final RoomType type; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsOverlay.java index 9613d2e643..3f572adf2a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsOverlay.java @@ -125,37 +125,41 @@ public class RaidsOverlay extends Overlay { case COMBAT: bossCount++; - if (plugin.getRoomWhitelist().contains(room.getBoss().getName().toLowerCase())) + if (plugin.getRoomWhitelist().contains(room.getName().toLowerCase())) { color = Color.GREEN; } - else if (plugin.getRoomBlacklist().contains(room.getBoss().getName().toLowerCase()) + else if (plugin.getRoomBlacklist().contains(room.getName().toLowerCase()) || config.enableRotationWhitelist() && bossCount > bossMatches) { color = Color.RED; } + String name = room == RaidRoom.UNKNOWN_COMBAT ? "Unknown" : room.getName(); + panelComponent.getChildren().add(LineComponent.builder() .left(room.getType().getName()) - .right(room.getBoss().getName()) + .right(name) .rightColor(color) .build()); break; case PUZZLE: - if (plugin.getRoomWhitelist().contains(room.getPuzzle().getName().toLowerCase())) + if (plugin.getRoomWhitelist().contains(room.getName().toLowerCase())) { color = Color.GREEN; } - else if (plugin.getRoomBlacklist().contains(room.getPuzzle().getName().toLowerCase())) + else if (plugin.getRoomBlacklist().contains(room.getName().toLowerCase())) { color = Color.RED; } + name = room == RaidRoom.UNKNOWN_PUZZLE ? "Unknown" : room.getName(); + panelComponent.getChildren().add(LineComponent.builder() .left(room.getType().getName()) - .right(room.getPuzzle().getName()) + .right(name) .rightColor(color) .build()); break; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsPlugin.java index 7c075b8767..2dd6a321ec 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsPlugin.java @@ -63,7 +63,6 @@ import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.plugins.raids.solver.Layout; import net.runelite.client.plugins.raids.solver.LayoutSolver; -import net.runelite.client.plugins.raids.solver.RotationSolver; import net.runelite.client.ui.overlay.OverlayManager; import net.runelite.client.ui.overlay.infobox.InfoBoxManager; import net.runelite.client.util.Text; @@ -544,97 +543,69 @@ public class RaidsPlugin extends Plugin private RaidRoom determineRoom(Tile base) { - RaidRoom room = new RaidRoom(base, RaidRoom.Type.EMPTY); int chunkData = client.getInstanceTemplateChunks()[base.getPlane()][(base.getSceneLocation().getX()) / 8][base.getSceneLocation().getY() / 8]; InstanceTemplates template = InstanceTemplates.findMatch(chunkData); if (template == null) { - return room; + return RaidRoom.EMPTY; } switch (template) { case RAIDS_LOBBY: case RAIDS_START: - room.setType(RaidRoom.Type.START); - break; + return RaidRoom.START; case RAIDS_END: - room.setType(RaidRoom.Type.END); - break; + return RaidRoom.END; case RAIDS_SCAVENGERS: case RAIDS_SCAVENGERS2: - room.setType(RaidRoom.Type.SCAVENGERS); - break; + return RaidRoom.SCAVENGERS; case RAIDS_SHAMANS: - room.setType(RaidRoom.Type.COMBAT); - room.setBoss(RaidRoom.Boss.SHAMANS); - break; + return RaidRoom.SHAMANS; case RAIDS_VASA: - room.setType(RaidRoom.Type.COMBAT); - room.setBoss(RaidRoom.Boss.VASA); - break; + return RaidRoom.VASA; case RAIDS_VANGUARDS: - room.setType(RaidRoom.Type.COMBAT); - room.setBoss(RaidRoom.Boss.VANGUARDS); - break; + return RaidRoom.VANGUARDS; case RAIDS_ICE_DEMON: - room.setType(RaidRoom.Type.PUZZLE); - room.setPuzzle(RaidRoom.Puzzle.ICE_DEMON); - break; + return RaidRoom.ICE_DEMON; case RAIDS_THIEVING: - room.setType(RaidRoom.Type.PUZZLE); - room.setPuzzle(RaidRoom.Puzzle.THIEVING); - break; + return RaidRoom.THIEVING; case RAIDS_FARMING: case RAIDS_FARMING2: - room.setType(RaidRoom.Type.FARMING); - break; + return RaidRoom.FARMING; case RAIDS_MUTTADILES: - room.setType(RaidRoom.Type.COMBAT); - room.setBoss(RaidRoom.Boss.MUTTADILES); - break; + return RaidRoom.MUTTADILES; case RAIDS_MYSTICS: - room.setType(RaidRoom.Type.COMBAT); - room.setBoss(RaidRoom.Boss.MYSTICS); - break; + return RaidRoom.MYSTICS; case RAIDS_TEKTON: - room.setType(RaidRoom.Type.COMBAT); - room.setBoss(RaidRoom.Boss.TEKTON); - break; + return RaidRoom.TEKTON; case RAIDS_TIGHTROPE: - room.setType(RaidRoom.Type.PUZZLE); - room.setPuzzle(RaidRoom.Puzzle.TIGHTROPE); - break; + return RaidRoom.TIGHTROPE; case RAIDS_GUARDIANS: - room.setType(RaidRoom.Type.COMBAT); - room.setBoss(RaidRoom.Boss.GUARDIANS); - break; + return RaidRoom.GUARDIANS; case RAIDS_CRABS: - room.setType(RaidRoom.Type.PUZZLE); - room.setPuzzle(RaidRoom.Puzzle.CRABS); - break; + return RaidRoom.CRABS; case RAIDS_VESPULA: - room.setType(RaidRoom.Type.COMBAT); - room.setBoss(RaidRoom.Boss.VESPULA); - break; - } + return RaidRoom.VESPULA; - return room; + default: + return RaidRoom.EMPTY; + } } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/raids/RoomType.java b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RoomType.java new file mode 100644 index 0000000000..686391e563 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RoomType.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2018, Kamiel + * Copyright (c) 2019, 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.raids; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +enum RoomType +{ + START("Start", '#'), + END("End", '¤'), + SCAVENGERS("Scavengers", 'S'), + FARMING("Farming", 'F'), + EMPTY("Empty", ' '), + COMBAT("Combat", 'C'), + PUZZLE("Puzzle", 'P'); + + private final String name; + private final char code; + + RaidRoom getUnsolvedRoom() + { + switch (this) + { + case START: + return RaidRoom.START; + case END: + return RaidRoom.END; + case SCAVENGERS: + return RaidRoom.SCAVENGERS; + case FARMING: + return RaidRoom.FARMING; + case COMBAT: + return RaidRoom.UNKNOWN_COMBAT; + case PUZZLE: + return RaidRoom.UNKNOWN_PUZZLE; + case EMPTY: + default: + return RaidRoom.EMPTY; + } + } + + static RoomType fromCode(char code) + { + for (RoomType type : values()) + { + if (type.getCode() == code) + { + return type; + } + } + + return EMPTY; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/raids/solver/RotationSolver.java b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RotationSolver.java similarity index 57% rename from runelite-client/src/main/java/net/runelite/client/plugins/raids/solver/RotationSolver.java rename to runelite-client/src/main/java/net/runelite/client/plugins/raids/RotationSolver.java index 1acf3ed2df..b9179cea1b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/raids/solver/RotationSolver.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RotationSolver.java @@ -22,58 +22,45 @@ * (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.raids.solver; +package net.runelite.client.plugins.raids; -import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; -import net.runelite.client.plugins.raids.RaidRoom; -import net.runelite.client.plugins.raids.RaidRoom.Boss; +import java.util.List; +import static net.runelite.client.plugins.raids.RaidRoom.GUARDIANS; +import static net.runelite.client.plugins.raids.RaidRoom.MUTTADILES; +import static net.runelite.client.plugins.raids.RaidRoom.MYSTICS; +import static net.runelite.client.plugins.raids.RaidRoom.SHAMANS; +import static net.runelite.client.plugins.raids.RaidRoom.TEKTON; +import static net.runelite.client.plugins.raids.RaidRoom.UNKNOWN_COMBAT; +import static net.runelite.client.plugins.raids.RaidRoom.VANGUARDS; +import static net.runelite.client.plugins.raids.RaidRoom.VASA; +import static net.runelite.client.plugins.raids.RaidRoom.VESPULA; -public class RotationSolver +class RotationSolver { - private static class Rotation extends ArrayList - { - Rotation(Collection bosses) + private static final List[] ROTATIONS = { - super(bosses); - } + Arrays.asList(TEKTON, VASA, GUARDIANS, MYSTICS, SHAMANS, MUTTADILES, VANGUARDS, VESPULA), + Arrays.asList(TEKTON, MUTTADILES, GUARDIANS, VESPULA, SHAMANS, VASA, VANGUARDS, MYSTICS), + Arrays.asList(VESPULA, VANGUARDS, MUTTADILES, SHAMANS, MYSTICS, GUARDIANS, VASA, TEKTON), + Arrays.asList(MYSTICS, VANGUARDS, VASA, SHAMANS, VESPULA, GUARDIANS, MUTTADILES, TEKTON) + }; - @Override - public E get(int index) - { - if (index < 0) - { - index = index + size(); - } - - return super.get(index % size()); - } - } - - private static final Rotation[] ROTATIONS = - { - new Rotation<>(Arrays.asList(Boss.TEKTON, Boss.VASA, Boss.GUARDIANS, Boss.MYSTICS, Boss.SHAMANS, Boss.MUTTADILES, Boss.VANGUARDS, Boss.VESPULA)), - new Rotation<>(Arrays.asList(Boss.TEKTON, Boss.MUTTADILES, Boss.GUARDIANS, Boss.VESPULA, Boss.SHAMANS, Boss.VASA, Boss.VANGUARDS, Boss.MYSTICS)), - new Rotation<>(Arrays.asList(Boss.VESPULA, Boss.VANGUARDS, Boss.MUTTADILES, Boss.SHAMANS, Boss.MYSTICS, Boss.GUARDIANS, Boss.VASA, Boss.TEKTON)), - new Rotation<>(Arrays.asList(Boss.MYSTICS, Boss.VANGUARDS, Boss.VASA, Boss.SHAMANS, Boss.VESPULA, Boss.GUARDIANS, Boss.MUTTADILES, Boss.TEKTON)) - }; - - public static boolean solve(RaidRoom[] rooms) + static boolean solve(RaidRoom[] rooms) { if (rooms == null) { return false; } - Rotation match = null; + List match = null; Integer start = null; Integer index = null; int known = 0; for (int i = 0; i < rooms.length; i++) { - if (rooms[i] == null || rooms[i].getBoss() == null || rooms[i].getBoss() == Boss.UNKNOWN) + if (rooms[i] == null || rooms[i].getType() != RoomType.COMBAT || rooms[i] == UNKNOWN_COMBAT) { continue; } @@ -96,21 +83,21 @@ public class RotationSolver return true; } - for (Rotation rotation : ROTATIONS) + for (List rotation : ROTATIONS) { COMPARE: for (int i = 0; i < rotation.size(); i++) { - if (rooms[start].getBoss() == rotation.get(i)) + if (rooms[start] == rotation.get(i)) { for (int j = start + 1; j < rooms.length; j++) { - if (rooms[j].getBoss() == null || rooms[j].getBoss() == Boss.UNKNOWN) + if (rooms[j].getType() != RoomType.COMBAT || rooms[j] == UNKNOWN_COMBAT) { continue; } - if (rooms[j].getBoss() != rotation.get(i + j - start)) + if (rooms[j] != rotation.get((i + j - start) % rotation.size())) { break COMPARE; } @@ -139,9 +126,9 @@ public class RotationSolver continue; } - if (rooms[i].getBoss() == null || rooms[i].getBoss() == Boss.UNKNOWN) + if (rooms[i].getType() != RoomType.COMBAT || rooms[i] == UNKNOWN_COMBAT) { - rooms[i].setBoss(match.get(index + i)); + rooms[i] = match.get((index + i) % match.size()); } } diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/raids/RotationSolverTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/raids/RotationSolverTest.java new file mode 100644 index 0000000000..5c2bba30e6 --- /dev/null +++ b/runelite-client/src/test/java/net/runelite/client/plugins/raids/RotationSolverTest.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2019, 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.raids; + +import static net.runelite.client.plugins.raids.RaidRoom.GUARDIANS; +import static net.runelite.client.plugins.raids.RaidRoom.MUTTADILES; +import static net.runelite.client.plugins.raids.RaidRoom.MYSTICS; +import static net.runelite.client.plugins.raids.RaidRoom.SHAMANS; +import static net.runelite.client.plugins.raids.RaidRoom.TEKTON; +import static net.runelite.client.plugins.raids.RaidRoom.UNKNOWN_COMBAT; +import static net.runelite.client.plugins.raids.RaidRoom.VANGUARDS; +import static net.runelite.client.plugins.raids.RaidRoom.VASA; +import static net.runelite.client.plugins.raids.RaidRoom.VESPULA; +import static org.junit.Assert.assertArrayEquals; +import org.junit.Test; + +public class RotationSolverTest +{ + @Test + public void testSolve1() + { + RaidRoom[] rooms = new RaidRoom[]{VESPULA, UNKNOWN_COMBAT, UNKNOWN_COMBAT, VANGUARDS}; + RotationSolver.solve(rooms); + assertArrayEquals(new RaidRoom[]{VESPULA, SHAMANS, VASA, VANGUARDS}, rooms); + } + + @Test + public void testSolve2() + { + RaidRoom[] rooms = new RaidRoom[]{UNKNOWN_COMBAT, UNKNOWN_COMBAT, MUTTADILES, TEKTON}; + RotationSolver.solve(rooms); + assertArrayEquals(new RaidRoom[]{VESPULA, GUARDIANS, MUTTADILES, TEKTON}, rooms); + } + + @Test + public void testSolve3() + { + RaidRoom[] rooms = new RaidRoom[]{TEKTON, UNKNOWN_COMBAT, GUARDIANS, MYSTICS}; + RotationSolver.solve(rooms); + assertArrayEquals(new RaidRoom[]{TEKTON, VASA, GUARDIANS, MYSTICS}, rooms); + } + + @Test + public void testSolve4() + { + RaidRoom[] rooms = new RaidRoom[]{VASA, UNKNOWN_COMBAT, UNKNOWN_COMBAT, GUARDIANS}; + RotationSolver.solve(rooms); + assertArrayEquals(new RaidRoom[]{VASA, SHAMANS, VESPULA, GUARDIANS}, rooms); + } +} \ No newline at end of file From fce1b9518d4665e0a24996fc30481730beff63db Mon Sep 17 00:00:00 2001 From: Adam Date: Mon, 28 Oct 2019 18:03:41 -0400 Subject: [PATCH 2/3] raids plugin: clean up whitelist matching code --- .../runelite/client/plugins/raids/Raid.java | 11 +---- .../client/plugins/raids/RaidsPlugin.java | 41 ++++--------------- 2 files changed, 10 insertions(+), 42 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/raids/Raid.java b/runelite-client/src/main/java/net/runelite/client/plugins/raids/Raid.java index 4f537eb44e..a237fc03cd 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/raids/Raid.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/raids/Raid.java @@ -24,9 +24,7 @@ */ package net.runelite.client.plugins.raids; -import com.google.common.base.Joiner; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import lombok.Getter; import net.runelite.client.plugins.raids.solver.Layout; @@ -80,7 +78,7 @@ public class Raid } } - public RaidRoom[] getCombatRooms() + RaidRoom[] getCombatRooms() { List combatRooms = new ArrayList<>(); @@ -97,12 +95,7 @@ public class Raid } } - return combatRooms.toArray(new RaidRoom[combatRooms.size()]); - } - - public String getRotationString() - { - return Joiner.on(",").join(Arrays.stream(getCombatRooms()).map(RaidRoom::getName).toArray()); + return combatRooms.toArray(new RaidRoom[0]); } public String toCode() diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsPlugin.java index 2dd6a321ec..50f3649a09 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsPlugin.java @@ -29,9 +29,10 @@ import com.google.inject.Provides; import java.text.DecimalFormat; import java.time.Instant; import java.util.ArrayList; -import java.util.List; +import java.util.Arrays; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; import javax.inject.Inject; import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -407,39 +408,13 @@ public class RaidsPlugin extends Plugin int getRotationMatches() { - String rotation = raid.getRotationString().toLowerCase(); - List bosses = Text.fromCSV(rotation); + RaidRoom[] combatRooms = raid.getCombatRooms(); + String rotation = Arrays.stream(combatRooms) + .map(RaidRoom::getName) + .map(String::toLowerCase) + .collect(Collectors.joining(",")); - if (rotationWhitelist.contains(rotation)) - { - return bosses.size(); - } - - for (String whitelisted : rotationWhitelist) - { - int matches = 0; - List whitelistedBosses = Text.fromCSV(whitelisted); - - for (int i = 0; i < whitelistedBosses.size(); i++) - { - if (i < bosses.size() && whitelistedBosses.get(i).equals(bosses.get(i))) - { - matches++; - } - else - { - matches = 0; - break; - } - } - - if (matches >= 2) - { - return matches; - } - } - - return 0; + return rotationWhitelist.contains(rotation) ? combatRooms.length : 0; } private Point findLobbyBase() From f3a52b11136b6d8c22268455032602d55eadaa7b Mon Sep 17 00:00:00 2001 From: Adam Date: Mon, 28 Oct 2019 18:30:05 -0400 Subject: [PATCH 3/3] Add layout chat command --- .../runelite/http/api/chat/ChatClient.java | 47 ++++++++ .../runelite/http/api/chat/LayoutRoom.java | 50 +++++++++ .../http/service/chat/ChatController.java | 21 ++++ .../http/service/chat/ChatService.java | 31 ++++++ .../client/plugins/raids/RaidsPlugin.java | 104 ++++++++++++++++++ 5 files changed, 253 insertions(+) create mode 100644 http-api/src/main/java/net/runelite/http/api/chat/LayoutRoom.java diff --git a/http-api/src/main/java/net/runelite/http/api/chat/ChatClient.java b/http-api/src/main/java/net/runelite/http/api/chat/ChatClient.java index 79c4a889ed..3c1565effd 100644 --- a/http-api/src/main/java/net/runelite/http/api/chat/ChatClient.java +++ b/http-api/src/main/java/net/runelite/http/api/chat/ChatClient.java @@ -309,4 +309,51 @@ public class ChatClient throw new IOException(ex); } } + + public boolean submitLayout(String username, LayoutRoom[] rooms) throws IOException + { + HttpUrl url = RuneLiteAPI.getApiBase().newBuilder() + .addPathSegment("chat") + .addPathSegment("layout") + .addQueryParameter("name", username) + .build(); + + Request request = new Request.Builder() + .post(RequestBody.create(RuneLiteAPI.JSON, RuneLiteAPI.GSON.toJson(rooms))) + .url(url) + .build(); + + try (Response response = RuneLiteAPI.CLIENT.newCall(request).execute()) + { + return response.isSuccessful(); + } + } + + public LayoutRoom[] getLayout(String username) throws IOException + { + HttpUrl url = RuneLiteAPI.getApiBase().newBuilder() + .addPathSegment("chat") + .addPathSegment("layout") + .addQueryParameter("name", username) + .build(); + + Request request = new Request.Builder() + .url(url) + .build(); + + try (Response response = RuneLiteAPI.CLIENT.newCall(request).execute()) + { + if (!response.isSuccessful()) + { + throw new IOException("Unable to look up layout!"); + } + + InputStream in = response.body().byteStream(); + return RuneLiteAPI.GSON.fromJson(new InputStreamReader(in), LayoutRoom[].class); + } + catch (JsonParseException ex) + { + throw new IOException(ex); + } + } } diff --git a/http-api/src/main/java/net/runelite/http/api/chat/LayoutRoom.java b/http-api/src/main/java/net/runelite/http/api/chat/LayoutRoom.java new file mode 100644 index 0000000000..3498f83eed --- /dev/null +++ b/http-api/src/main/java/net/runelite/http/api/chat/LayoutRoom.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2019, 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.http.api.chat; + +public enum LayoutRoom +{ + START, + END, + SCAVENGERS, + FARMING, + EMPTY, + + TEKTON, + MUTTADILES, + GUARDIANS, + VESPULA, + SHAMANS, + VASA, + VANGUARDS, + MYSTICS, + UNKNOWN_COMBAT, + + CRABS, + ICE_DEMON, + TIGHTROPE, + THIEVING, + UNKNOWN_PUZZLE; +} diff --git a/http-service/src/main/java/net/runelite/http/service/chat/ChatController.java b/http-service/src/main/java/net/runelite/http/service/chat/ChatController.java index 7b5132e47a..15381845b5 100644 --- a/http-service/src/main/java/net/runelite/http/service/chat/ChatController.java +++ b/http-service/src/main/java/net/runelite/http/service/chat/ChatController.java @@ -30,11 +30,13 @@ import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; import net.runelite.http.api.chat.Duels; +import net.runelite.http.api.chat.LayoutRoom; import net.runelite.http.api.chat.Task; import net.runelite.http.service.util.exception.NotFoundException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @@ -208,4 +210,23 @@ public class ChatController } return duels; } + + @PostMapping("/layout") + public void submitLayout(@RequestParam String name, @RequestBody LayoutRoom[] rooms) + { + chatService.setLayout(name, rooms); + } + + @GetMapping("/layout") + public LayoutRoom[] getLayout(@RequestParam String name) + { + LayoutRoom[] layout = chatService.getLayout(name); + + if (layout == null) + { + throw new NotFoundException(); + } + + return layout; + } } diff --git a/http-service/src/main/java/net/runelite/http/service/chat/ChatService.java b/http-service/src/main/java/net/runelite/http/service/chat/ChatService.java index 0b0762d3ea..be497b40bd 100644 --- a/http-service/src/main/java/net/runelite/http/service/chat/ChatService.java +++ b/http-service/src/main/java/net/runelite/http/service/chat/ChatService.java @@ -24,9 +24,13 @@ */ package net.runelite.http.service.chat; +import com.google.common.base.Joiner; +import com.google.common.base.Splitter; import com.google.common.collect.ImmutableMap; import java.time.Duration; +import java.util.List; import java.util.Map; +import net.runelite.http.api.chat.LayoutRoom; import net.runelite.http.api.chat.Task; import net.runelite.http.api.chat.Duels; import net.runelite.http.service.util.redis.RedisPool; @@ -198,4 +202,31 @@ public class ChatService jedis.expire(key, (int) EXPIRE.getSeconds()); } } + + public LayoutRoom[] getLayout(String name) + { + String layout; + try (Jedis jedis = jedisPool.getResource()) + { + layout = jedis.get("layout." + name); + } + + if (layout == null) + { + return null; + } + + List roomList = Splitter.on(' ').splitToList(layout); + return roomList.stream() + .map(LayoutRoom::valueOf) + .toArray(LayoutRoom[]::new); + } + + public void setLayout(String name, LayoutRoom[] rooms) + { + try (Jedis jedis = jedisPool.getResource()) + { + jedis.setex("layout." + name, (int) EXPIRE.getSeconds(), Joiner.on(' ').join(rooms)); + } + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsPlugin.java index 50f3649a09..4501b5570a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsPlugin.java @@ -24,12 +24,15 @@ */ package net.runelite.client.plugins.raids; +import com.google.common.base.Joiner; import com.google.inject.Binder; import com.google.inject.Provides; +import java.io.IOException; import java.text.DecimalFormat; import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; +import java.util.concurrent.ScheduledExecutorService; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -41,6 +44,7 @@ import net.runelite.api.Client; import net.runelite.api.GameState; import net.runelite.api.InstanceTemplates; import net.runelite.api.MenuAction; +import net.runelite.api.MessageNode; import net.runelite.api.NullObjectID; import static net.runelite.api.Perspective.SCENE_SIZE; import net.runelite.api.Point; @@ -53,11 +57,13 @@ import net.runelite.api.events.ConfigChanged; import net.runelite.api.events.VarbitChanged; import net.runelite.client.callback.ClientThread; import net.runelite.client.chat.ChatColorType; +import net.runelite.client.chat.ChatCommandManager; import net.runelite.client.chat.ChatMessageBuilder; import net.runelite.client.chat.ChatMessageManager; import net.runelite.client.chat.QueuedMessage; import net.runelite.client.config.ConfigManager; import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.events.ChatInput; import net.runelite.client.events.OverlayMenuClicked; import net.runelite.client.game.SpriteManager; import net.runelite.client.plugins.Plugin; @@ -67,9 +73,12 @@ import net.runelite.client.plugins.raids.solver.LayoutSolver; import net.runelite.client.ui.overlay.OverlayManager; import net.runelite.client.ui.overlay.infobox.InfoBoxManager; import net.runelite.client.util.Text; +import static net.runelite.client.util.Text.sanitize; import net.runelite.client.ws.PartyMember; import net.runelite.client.ws.PartyService; import net.runelite.client.ws.WSClient; +import net.runelite.http.api.chat.ChatClient; +import net.runelite.http.api.chat.LayoutRoom; import net.runelite.http.api.ws.messages.party.PartyChatMessage; @PluginDescriptor( @@ -87,6 +96,7 @@ public class RaidsPlugin extends Plugin private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("###.##"); private static final DecimalFormat POINTS_FORMAT = new DecimalFormat("#,###"); private static final Pattern ROTATION_REGEX = Pattern.compile("\\[(.*?)]"); + private static final String LAYOUT_COMMAND = "!layout"; @Inject private ChatMessageManager chatMessageManager; @@ -121,6 +131,15 @@ public class RaidsPlugin extends Plugin @Inject private WSClient ws; + @Inject + private ChatCommandManager chatCommandManager; + + @Inject + private ChatClient chatClient; + + @Inject + private ScheduledExecutorService scheduledExecutorService; + @Getter private final ArrayList roomWhitelist = new ArrayList<>(); @@ -159,11 +178,13 @@ public class RaidsPlugin extends Plugin overlayManager.add(overlay); updateLists(); clientThread.invokeLater(() -> checkRaidPresence(true)); + chatCommandManager.registerCommandAsync(LAYOUT_COMMAND, this::lookupRaid, this::submitRaid); } @Override protected void shutDown() throws Exception { + chatCommandManager.unregisterCommand(LAYOUT_COMMAND); overlayManager.remove(overlay); infoBoxManager.removeInfoBox(timer); inRaidChambers = false; @@ -583,4 +604,87 @@ public class RaidsPlugin extends Plugin return RaidRoom.EMPTY; } } + + private void lookupRaid(ChatMessage chatMessage, String s) + { + ChatMessageType type = chatMessage.getType(); + + final String player; + if (type.equals(ChatMessageType.PRIVATECHATOUT)) + { + player = client.getLocalPlayer().getName(); + } + else + { + player = sanitize(chatMessage.getName()); + } + + LayoutRoom[] layout; + try + { + layout = chatClient.getLayout(player); + } + catch (IOException ex) + { + log.debug("unable to lookup layout", ex); + return; + } + + if (layout == null || layout.length == 0) + { + return; + } + + String layoutMessage = Joiner.on(", ").join(Arrays.stream(layout) + .map(l -> RaidRoom.valueOf(l.name())) + .filter(room -> room.getType() == RoomType.COMBAT || room.getType() == RoomType.PUZZLE) + .map(RaidRoom::getName) + .toArray()); + + String response = new ChatMessageBuilder() + .append(ChatColorType.HIGHLIGHT) + .append("Layout: ") + .append(ChatColorType.NORMAL) + .append(layoutMessage) + .build(); + + log.debug("Setting response {}", response); + final MessageNode messageNode = chatMessage.getMessageNode(); + messageNode.setRuneLiteFormatMessage(response); + chatMessageManager.update(messageNode); + client.refreshChat(); + } + + private boolean submitRaid(ChatInput chatInput, String s) + { + if (raid == null) + { + return false; + } + + final String playerName = client.getLocalPlayer().getName(); + RaidRoom[] rooms = raid.getRooms(); + + LayoutRoom[] layoutRooms = Arrays.stream(rooms) + .map(room -> LayoutRoom.valueOf(room.name())) + .toArray(LayoutRoom[]::new); + + scheduledExecutorService.execute(() -> + { + try + { + chatClient.submitLayout(playerName, layoutRooms); + } + catch (Exception ex) + { + log.warn("unable to submit layout", ex); + } + finally + { + chatInput.resume(); + } + }); + + return true; + } }