Merge pull request #83 from Kyleeld/master

Hydra plugin & Add encryption to account data - thx ThatGamerBlue
This commit is contained in:
Ganom
2019-04-23 21:10:32 -04:00
committed by GitHub
9 changed files with 1409 additions and 443 deletions

View File

@@ -0,0 +1,62 @@
package net.runelite.client.plugins.alchemicalhydra;
import javax.inject.Singleton;
import lombok.Getter;
import lombok.Setter;
import net.runelite.api.Prayer;
import net.runelite.api.ProjectileID;
@Singleton
class Hydra
{
enum AttackStyle
{
MAGIC(ProjectileID.HYDRA_MAGIC, Prayer.PROTECT_FROM_MAGIC),
RANGED(ProjectileID.HYDRA_RANGED, Prayer.PROTECT_FROM_MISSILES);
@Getter
private int projId;
@Getter
private Prayer prayer;
AttackStyle(int projId, Prayer prayer)
{
this.projId = projId;
this.prayer = prayer;
}
}
@Getter
@Setter
private HydraPhase phase;
@Getter
@Setter
private int attackCount;
@Getter
@Setter
private int nextSwitch;
@Getter
@Setter
private int nextSpecial;
@Getter
@Setter
private AttackStyle nextAttack;
@Getter
@Setter
private AttackStyle lastAttack;
Hydra()
{
this.phase = HydraPhase.ONE;
this.nextAttack = AttackStyle.MAGIC;
this.nextSpecial = 3;
this.nextSwitch = phase.getAttacksPerSwitch();
this.attackCount = 0;
}
}

View File

@@ -0,0 +1,132 @@
package net.runelite.client.plugins.alchemicalhydra;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import javax.inject.Inject;
import javax.inject.Singleton;
import net.runelite.api.Client;
import net.runelite.api.Prayer;
import net.runelite.api.SpriteID;
import net.runelite.client.game.SpriteManager;
import net.runelite.client.ui.overlay.Overlay;
import net.runelite.client.ui.overlay.OverlayPosition;
import net.runelite.client.ui.overlay.components.InfoBoxComponent;
import net.runelite.client.ui.overlay.components.PanelComponent;
@Singleton
class HydraOverlay extends Overlay
{
private final HydraPlugin plugin;
private final Client client;
private final SpriteManager spriteManager;
private final PanelComponent panelComponent = new PanelComponent();
private static final Color redBgCol = new Color(156, 0, 0, 156);
private static final Color yelBgCol = new Color(200, 156, 0, 156);
private static final Color grnBgCol = new Color(0, 156, 0, 156);
@Inject
HydraOverlay(HydraPlugin plugin, Client client, SpriteManager spriteManager)
{
this.plugin = plugin;
this.client = client;
this.spriteManager = spriteManager;
setPosition(OverlayPosition.BOTTOM_RIGHT);
panelComponent.setOrientation(PanelComponent.Orientation.VERTICAL);
}
@Override
public Dimension render(Graphics2D graphics2D)
{
Hydra hydra = plugin.getHydra();
panelComponent.getChildren().clear();
if (hydra == null || client == null)
{
return null;
}
//Add spec overlay first, to keep it above pray
HydraPhase phase = hydra.getPhase();
int attackCount = hydra.getAttackCount();
int nextSpec = hydra.getNextSpecial() - attackCount;
if (nextSpec <= 3)
{
InfoBoxComponent specComponent = new InfoBoxComponent();
if (nextSpec == 0)
{
specComponent.setBackgroundColor(redBgCol);
}
else if (nextSpec == 1)
{
specComponent.setBackgroundColor(yelBgCol);
}
Image specImg = scaleImg(spriteManager.getSprite(phase.getSpecImage(), 0));
specComponent.setImage(specImg);
specComponent.setText(" " + (nextSpec)); //hacky way to not have to figure out how to move text
specComponent.setPreferredSize(new Dimension(40, 40));
panelComponent.getChildren().add(specComponent);
}
Prayer nextPrayer = hydra.getNextAttack().getPrayer();
Image prayImg = scaleImg(getPrayerImage(hydra.getNextAttack().getPrayer()));
int nextSwitch = hydra.getNextSwitch();
InfoBoxComponent prayComponent = new InfoBoxComponent();
if (nextSwitch == 1)
{
prayComponent.setBackgroundColor(client.isPrayerActive(nextPrayer) ? yelBgCol : redBgCol);
}
else
{
prayComponent.setBackgroundColor(client.isPrayerActive(nextPrayer) ? grnBgCol : redBgCol);
}
prayComponent.setImage(prayImg);
prayComponent.setText(" " + nextSwitch);
prayComponent.setColor(Color.white);
prayComponent.setPreferredSize(new Dimension(40, 40));
panelComponent.getChildren().add(prayComponent);
panelComponent.setPreferredSize(new Dimension(40, 0));
panelComponent.setBorder(new Rectangle(0, 0, 0, 0));
return panelComponent.render(graphics2D);
}
private BufferedImage getPrayerImage(Prayer pray)
{
return pray == Prayer.PROTECT_FROM_MAGIC
? spriteManager.getSprite(SpriteID.PRAYER_PROTECT_FROM_MAGIC, 0)
: spriteManager.getSprite(SpriteID.PRAYER_PROTECT_FROM_MISSILES, 0);
}
private Image scaleImg(final Image img)
{
if (img == null)
{
return null;
}
final double width = img.getWidth(null);
final double height = img.getHeight(null);
final double size = 36; // Limit size to 2 as that is minimum size not causing breakage
final double scalex = size / width;
final double scaley = size / height;
final double scale = Math.min(scalex, scaley);
final int newWidth = (int) (width * scale);
final int newHeight = (int) (height * scale);
final BufferedImage scaledImage = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB);
final Graphics g = scaledImage.createGraphics();
g.drawImage(img, 0, 0, newWidth, newHeight, null);
g.dispose();
return scaledImage;
}
}

View File

@@ -0,0 +1,42 @@
package net.runelite.client.plugins.alchemicalhydra;
import lombok.Getter;
import net.runelite.api.AnimationID;
import net.runelite.api.ProjectileID;
import net.runelite.api.SpriteID;
enum HydraPhase
{
ONE(3, AnimationID.HYDRA_1_1, AnimationID.HYDRA_1_2, ProjectileID.HYDRA_POISON, 0, SpriteID.BIG_ASS_GUTHIX_SPELL),
TWO(3, AnimationID.HYDRA_2_1, AnimationID.HYDRA_2_2, 0, AnimationID.HYDRA_LIGHTNING, SpriteID.BIG_SPEC_TRANSFER),
THREE(3, AnimationID.HYDRA_3_1, AnimationID.HYDRA_3_2, 0, AnimationID.HYDRA_FIRE, SpriteID.BIG_SUPERHEAT),
FOUR(1, AnimationID.HYDRA_4_1, AnimationID.HYDRA_4_2, ProjectileID.HYDRA_POISON, 0, SpriteID.BIG_ASS_GUTHIX_SPELL);
@Getter
private int attacksPerSwitch;
@Getter
private int deathAnim1;
@Getter
private int deathAnim2;
@Getter
private int specProjectileId;
@Getter
private int specAnimationId;
@Getter
private int specImage;
HydraPhase(int attacksPerSwitch, int deathAnim1, int deathAnim2, int specProjectileId, int specAnimationId, int specImage)
{
this.attacksPerSwitch = attacksPerSwitch;
this.deathAnim1 = deathAnim1;
this.deathAnim2 = deathAnim2;
this.specProjectileId = specProjectileId;
this.specAnimationId = specAnimationId;
this.specImage = specImage;
}
}

View File

@@ -0,0 +1,248 @@
package net.runelite.client.plugins.alchemicalhydra;
import java.util.Arrays;
import java.util.HashSet;
import javax.inject.Inject;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Actor;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.NpcID;
import net.runelite.api.Projectile;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.events.AnimationChanged;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.NpcSpawned;
import net.runelite.api.events.ProjectileMoved;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.plugins.PluginType;
import net.runelite.client.ui.overlay.OverlayManager;
@PluginDescriptor(
name = "Alchemical Hydra",
description = "Show what to pray against hydra",
tags = {"Hydra", "Lazy", "4 headed asshole"},
type = PluginType.PVM
)
@Slf4j
public class HydraPlugin extends Plugin
{
@Getter
private HashSet<LocalPoint> poisonPoints = new HashSet<>();
@Getter
private Hydra hydra;
private boolean inHydraInstance;
private int lastAttackTick;
private int lastPoisonTick;
private static final int[] HYDRA_REGIONS = {
5279, 5280,
5535, 5536
};
@Inject
private Client client;
@Inject
private OverlayManager overlayManager;
@Inject
private HydraOverlay overlay;
@Inject
private HydraPoisonOverlay poisonOverlay;
@Override
protected void startUp()
{
inHydraInstance = checkArea();
lastAttackTick = -1;
poisonPoints.clear();
}
@Override
protected void shutDown()
{
inHydraInstance = false;
hydra = null;
poisonPoints.clear();
removeOverlays();
lastAttackTick = -1;
}
@Subscribe
private void onGameStateChanged(GameStateChanged state)
{
if (state.getGameState() != GameState.LOGGED_IN)
{
return;
}
inHydraInstance = checkArea();
if (inHydraInstance)
{
hydra = new Hydra();
log.debug("Entered hydra instance");
addOverlays();
}
else if (hydra != null)
{
removeOverlays();
hydra = null;
log.debug("Left hydra instance");
}
}
@Subscribe
private void onNpcSpawned(NpcSpawned event)
{
if (!inHydraInstance || event.getNpc().getId() != NpcID.ALCHEMICAL_HYDRA)
{
return;
}
hydra = new Hydra();
log.debug("Hydra spawned");
addOverlays();
}
@Subscribe
public void onAnimationChanged(AnimationChanged animationChanged)
{
Actor actor = animationChanged.getActor();
if (!inHydraInstance || hydra == null || actor == client.getLocalPlayer())
{
return;
}
HydraPhase phase = hydra.getPhase();
// Using the first animation sometimes fucks shit up, so just use 2
if ( /* actor.getAnimation() == phase.getDeathAnim1() || */ actor.getAnimation() == phase.getDeathAnim2())
{
switch (phase)
{
case ONE:
changePhase(HydraPhase.TWO);
log.debug("Hydra phase 2");
return;
case TWO:
changePhase(HydraPhase.THREE);
log.debug("Hydra phase 3");
return;
case THREE:
changePhase(HydraPhase.FOUR);
log.debug("Hydra phase 4");
return;
case FOUR:
hydra = null;
poisonPoints.clear();
log.debug("Hydra dead");
removeOverlays();
return;
default:
log.debug("Tried some weird shit");
break;
}
if (actor.getAnimation() == phase.getDeathAnim1() && phase == HydraPhase.THREE)
{
changePhase(HydraPhase.FOUR);
}
}
else if (actor.getAnimation() == phase.getSpecAnimationId() && phase.getSpecAnimationId() != 0)
{
hydra.setNextSpecial(hydra.getNextSpecial() + 9);
}
if (!poisonPoints.isEmpty() && lastPoisonTick + 10 < client.getTickCount())
{
poisonPoints.clear();
}
}
@Subscribe
public void onProjectileMoved(ProjectileMoved event)
{
if (!inHydraInstance || hydra == null
|| client.getGameCycle() >= event.getProjectile().getStartMovementCycle())
{
return;
}
Projectile projectile = event.getProjectile();
int id = projectile.getId();
if (hydra.getPhase().getSpecProjectileId() != 0 && hydra.getPhase().getSpecProjectileId() == id)
{
poisonPoints.add(event.getPosition());
hydra.setNextSpecial(hydra.getNextSpecial() + 9);
lastPoisonTick = client.getTickCount();
}
else if (client.getTickCount() != lastAttackTick
&& (id == Hydra.AttackStyle.MAGIC.getProjId() || id == Hydra.AttackStyle.RANGED.getProjId()))
{
handleAttack(id);
lastAttackTick = client.getTickCount();
}
}
private boolean checkArea()
{
return Arrays.equals(client.getMapRegions(), HYDRA_REGIONS) && client.isInInstancedRegion();
}
private void addOverlays()
{
overlayManager.add(overlay);
overlayManager.add(poisonOverlay);
}
private void removeOverlays()
{
overlayManager.remove(overlay);
overlayManager.remove(poisonOverlay);
}
private void changePhase(HydraPhase newPhase)
{
hydra.setPhase(newPhase);
hydra.setNextSpecial(3);
hydra.setAttackCount(0);
if (newPhase == HydraPhase.FOUR)
{
switchStyles();
hydra.setNextSwitch(newPhase.getAttacksPerSwitch());
}
}
private void switchStyles()
{
hydra.setNextAttack(hydra.getLastAttack() == Hydra.AttackStyle.MAGIC
? Hydra.AttackStyle.RANGED
: Hydra.AttackStyle.MAGIC);
}
private void handleAttack(int id)
{
hydra.setNextSwitch(hydra.getNextSwitch() - 1);
hydra.setAttackCount(hydra.getAttackCount() + 1);
hydra.setLastAttack(hydra.getNextAttack());
if (id != hydra.getNextAttack().getProjId())
{
switchStyles();
}
else if (hydra.getNextSwitch() <= 0)
{
switchStyles();
hydra.setNextSwitch(hydra.getPhase().getAttacksPerSwitch());
}
}
}

View File

@@ -0,0 +1,61 @@
package net.runelite.client.plugins.alchemicalhydra;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.geom.Area;
import java.util.HashSet;
import javax.inject.Inject;
import javax.inject.Singleton;
import net.runelite.api.Client;
import static net.runelite.api.Perspective.getCanvasTileAreaPoly;
import net.runelite.api.coords.LocalPoint;
import net.runelite.client.ui.overlay.Overlay;
import net.runelite.client.ui.overlay.OverlayLayer;
import net.runelite.client.ui.overlay.OverlayPosition;
@Singleton
class HydraPoisonOverlay extends Overlay
{
private final HydraPlugin plugin;
private final Client client;
@Inject
public HydraPoisonOverlay(Client client, HydraPlugin plugin)
{
setPosition(OverlayPosition.DYNAMIC);
setLayer(OverlayLayer.UNDER_WIDGETS);
this.plugin = plugin;
this.client = client;
}
@Override
public Dimension render(Graphics2D graphics)
{
final HashSet<LocalPoint> initialPoints = plugin.getPoisonPoints();
Area poisonTiles = new Area();
for (LocalPoint point : initialPoints)
{
Polygon poly = getCanvasTileAreaPoly(client, point, 3);
if (poly == null)
{
break;
}
poisonTiles.add(new Area(poly));
}
if (poisonTiles.isEmpty())
{
return null;
}
graphics.setPaintMode();
graphics.setColor(new Color(255, 0, 0, 75));
graphics.draw(poisonTiles);
graphics.setColor(new Color(255, 0, 0, 30));
graphics.fill(poisonTiles);
return null;
}
}

View File

@@ -1,15 +1,37 @@
/*
* Decompiled with CFR 0.139.
* Copyright (c) 2019, Spedwards <https://github.com/Spedwards>
* 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.profiles;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
@@ -17,107 +39,140 @@ import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.util.ImageUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class ProfilePanel
extends JPanel {
private static final Logger log = LoggerFactory.getLogger(ProfilePanel.class);
private static final ImageIcon DELETE_ICON;
private static final ImageIcon DELETE_HOVER_ICON;
private final String loginText;
private String password = null;
@Slf4j
class ProfilePanel extends JPanel
{
private static final ImageIcon DELETE_ICON;
private static final ImageIcon DELETE_HOVER_ICON;
ProfilePanel(final Client client, final String data, final ProfilesConfig config) {
String[] parts = data.split(":");
this.loginText = parts[1];
if (parts.length == 3) {
this.password = parts[2];
}
final ProfilePanel panel = this;
this.setLayout(new BorderLayout());
this.setBackground(ColorScheme.DARKER_GRAY_COLOR);
JPanel labelWrapper = new JPanel(new BorderLayout());
labelWrapper.setBackground(ColorScheme.DARKER_GRAY_COLOR);
labelWrapper.setBorder(new CompoundBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, ColorScheme.DARK_GRAY_COLOR), BorderFactory.createLineBorder(ColorScheme.DARKER_GRAY_COLOR)));
JPanel panelActions = new JPanel(new BorderLayout(3, 0));
panelActions.setBorder(new EmptyBorder(0, 0, 0, 8));
panelActions.setBackground(ColorScheme.DARKER_GRAY_COLOR);
final JLabel delete = new JLabel();
delete.setIcon(DELETE_ICON);
delete.setToolTipText("Delete account profile");
delete.addMouseListener(new MouseAdapter(){
static
{
final BufferedImage deleteImg = ImageUtil.getResourceStreamFromClass(ProfilesPlugin.class, "delete_icon.png");
DELETE_ICON = new ImageIcon(deleteImg);
DELETE_HOVER_ICON = new ImageIcon(ImageUtil.alphaOffset(deleteImg, -100));
}
@Override
public void mousePressed(MouseEvent e) {
panel.getParent().remove(panel);
ProfilesPanel.removeProfile(data);
}
private final String loginText;
private final ProfilesPanel parent;
private String password = null;
@Override
public void mouseEntered(MouseEvent e) {
delete.setIcon(DELETE_HOVER_ICON);
}
ProfilePanel(final Client client, String data, ProfilesConfig config, ProfilesPanel parent)
{
this.parent = parent;
String[] parts = data.split(":");
this.loginText = parts[1];
if (parts.length == 3)
{
this.password = parts[2];
}
@Override
public void mouseExited(MouseEvent e) {
delete.setIcon(DELETE_ICON);
}
});
panelActions.add((Component)delete, "East");
JLabel label = new JLabel();
label.setText(parts[0]);
label.setBorder(null);
label.setBackground(ColorScheme.DARKER_GRAY_COLOR);
label.setPreferredSize(new Dimension(0, 24));
label.setForeground(Color.WHITE);
label.setBorder(new EmptyBorder(0, 8, 0, 0));
labelWrapper.add((Component)label, "Center");
labelWrapper.add((Component)panelActions, "East");
label.addMouseListener(new MouseAdapter(){
final ProfilePanel panel = this;
@Override
public void mousePressed(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e) && client.getGameState() == GameState.LOGIN_SCREEN) {
client.setUsername(ProfilePanel.this.loginText);
if (config.rememberPassword() && ProfilePanel.this.password != null) {
client.setPassword(ProfilePanel.this.password);
}
}
}
});
JPanel bottomContainer = new JPanel(new BorderLayout());
bottomContainer.setBorder(new EmptyBorder(8, 0, 8, 0));
bottomContainer.setBackground(ColorScheme.DARKER_GRAY_COLOR);
bottomContainer.addMouseListener(new MouseAdapter(){
setLayout(new BorderLayout());
setBackground(ColorScheme.DARKER_GRAY_COLOR);
@Override
public void mousePressed(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e) && client.getGameState() == GameState.LOGIN_SCREEN) {
client.setUsername(ProfilePanel.this.loginText);
}
}
});
JLabel login = new JLabel();
login.setText(config.isStreamerMode() ? "Hidden email" : this.loginText);
login.setBorder(null);
login.setPreferredSize(new Dimension(0, 24));
login.setForeground(Color.WHITE);
login.setBorder(new EmptyBorder(0, 8, 0, 0));
bottomContainer.add((Component)login, "Center");
this.add((Component)labelWrapper, "North");
this.add((Component)bottomContainer, "Center");
}
JPanel labelWrapper = new JPanel(new BorderLayout());
labelWrapper.setBackground(ColorScheme.DARKER_GRAY_COLOR);
labelWrapper.setBorder(new CompoundBorder(
BorderFactory.createMatteBorder(0, 0, 1, 0, ColorScheme.DARK_GRAY_COLOR),
BorderFactory.createLineBorder(ColorScheme.DARKER_GRAY_COLOR)
));
static {
BufferedImage deleteImg = ImageUtil.getResourceStreamFromClass(ProfilesPlugin.class, "net/runelite/client/plugins/profiles/delete_icon.png");
DELETE_ICON = new ImageIcon(deleteImg);
DELETE_HOVER_ICON = new ImageIcon(ImageUtil.alphaOffset(deleteImg, -100));
}
JPanel panelActions = new JPanel(new BorderLayout(3, 0));
panelActions.setBorder(new EmptyBorder(0, 0, 0, 8));
panelActions.setBackground(ColorScheme.DARKER_GRAY_COLOR);
JLabel delete = new JLabel();
delete.setIcon(DELETE_ICON);
delete.setToolTipText("Delete account profile");
delete.addMouseListener(new MouseAdapter()
{
@Override
public void mousePressed(MouseEvent e)
{
panel.getParent().remove(panel);
try
{
parent.removeProfile(data);
}
catch (InvalidKeySpecException | NoSuchAlgorithmException ex)
{
ex.printStackTrace();
}
}
@Override
public void mouseEntered(MouseEvent e)
{
delete.setIcon(DELETE_HOVER_ICON);
}
@Override
public void mouseExited(MouseEvent e)
{
delete.setIcon(DELETE_ICON);
}
});
panelActions.add(delete, BorderLayout.EAST);
JLabel label = new JLabel();
label.setText(parts[0]);
label.setBorder(null);
label.setBackground(ColorScheme.DARKER_GRAY_COLOR);
label.setPreferredSize(new Dimension(0, 24));
label.setForeground(Color.WHITE);
label.setBorder(new EmptyBorder(0, 8, 0, 0));
labelWrapper.add(label, BorderLayout.CENTER);
labelWrapper.add(panelActions, BorderLayout.EAST);
label.addMouseListener(new MouseAdapter()
{
@Override
public void mousePressed(MouseEvent e)
{
if (SwingUtilities.isLeftMouseButton(e) && client.getGameState() == GameState.LOGIN_SCREEN)
{
client.setUsername(loginText);
if (config.rememberPassword() && password != null)
{
client.setPassword(password);
}
}
}
});
JPanel bottomContainer = new JPanel(new BorderLayout());
bottomContainer.setBorder(new EmptyBorder(8, 0, 8, 0));
bottomContainer.setBackground(ColorScheme.DARKER_GRAY_COLOR);
bottomContainer.addMouseListener(new MouseAdapter()
{
@Override
public void mousePressed(MouseEvent e)
{
if (SwingUtilities.isLeftMouseButton(e) && client.getGameState() == GameState.LOGIN_SCREEN)
{
client.setUsername(loginText);
}
}
});
JLabel login = new JLabel();
login.setText(config.isStreamerMode() ? "Hidden email" : loginText);
login.setBorder(null);
login.setPreferredSize(new Dimension(0, 24));
login.setForeground(Color.WHITE);
login.setBorder(new EmptyBorder(0, 8, 0, 0));
bottomContainer.add(login, BorderLayout.CENTER);
add(labelWrapper, BorderLayout.NORTH);
add(bottomContainer, BorderLayout.CENTER);
}
}

View File

@@ -1,5 +1,26 @@
/*
* Decompiled with CFR 0.139.
* Copyright (c) 2019, Spedwards <https://github.com/Spedwards>
* 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.profiles;
@@ -7,30 +28,74 @@ import net.runelite.client.config.Config;
import net.runelite.client.config.ConfigGroup;
import net.runelite.client.config.ConfigItem;
@ConfigGroup(value="profiles")
public interface ProfilesConfig
extends Config {
@ConfigItem(keyName="profilesData", name="", description="", hidden=true)
default public String profilesData() {
return "";
}
@ConfigGroup("profiles")
public interface ProfilesConfig extends Config
{
@ConfigItem(keyName="profilesData", name="", description="")
public void profilesData(String var1);
@ConfigItem(keyName="rememberPassword", name="Remember Password", description="Remembers passwords for accounts")
default public boolean rememberPassword() {
return true;
}
@ConfigItem(
keyName = "profilesData",
name = "",
description = "",
hidden = true
)
default String profilesData()
{
return "";
}
@ConfigItem(keyName="streamerMode", name="Hide email addresses", description="Hides your account emails")
default public boolean isStreamerMode() {
return false;
}
@ConfigItem(
keyName = "profilesData",
name = "",
description = ""
)
void profilesData(String str);
@ConfigItem(
keyName = "salt",
name = "",
description = "",
hidden = true
)
default String salt()
{
return "";
}
@ConfigItem(
keyName = "salt",
name = "",
description = ""
)
void salt(String key);
@ConfigItem(keyName="switchPanel", name="Auto-open Panel", description="Automatically switch to the account switcher panel on the login screen")
default public boolean switchPanel() {
return true;
}
@ConfigItem(
keyName = "rememberPassword",
name = "Remember Password",
description = "Remembers passwords for accounts"
)
default boolean rememberPassword()
{
return true;
}
@ConfigItem(
keyName = "streamerMode",
name = "Hide email addresses",
description = "Hides your account emails"
)
default boolean isStreamerMode()
{
return false;
}
@ConfigItem(
keyName = "switchPanel",
name = "Auto-open Panel",
description = "Automatically switch to the account switcher panel on the login screen"
)
default boolean switchPanel()
{
return false;
}
}

View File

@@ -1,248 +1,555 @@
/*
* Decompiled with CFR 0.139.
* Copyright (c) 2019, Spedwards <https://github.com/Spedwards>
* 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.profiles;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Arrays;
import java.util.function.Consumer;
import java.util.Base64;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import javax.inject.Inject;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;
import javax.swing.border.Border;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
import net.runelite.client.plugins.profiles.ProfilePanel;
import net.runelite.client.plugins.profiles.ProfilesConfig;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.PluginPanel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class ProfilesPanel
extends PluginPanel {
private static final Logger log = LoggerFactory.getLogger(ProfilesPanel.class);
private static final String ACCOUNT_USERNAME = "Account Username";
private static final String ACCOUNT_LABEL = "Account Label";
private static final String PASSWORD_LABEL = "Account Password";
private static final Dimension PREFERRED_SIZE = new Dimension(205, 30);
private static final Dimension MINIMUM_SIZE = new Dimension(0, 30);
private final Client client;
private static ProfilesConfig profilesConfig;
private final JTextField txtAccountLabel = new JTextField("Account Label");
private final JPasswordField txtAccountLogin = new JPasswordField("Account Username");
private final JPasswordField txtPasswordLogin = new JPasswordField("Account Password");
private final JPanel profilesPanel = new JPanel();
private GridBagConstraints c;
@Inject
public ProfilesPanel(Client client, final ProfilesConfig config) {
this.client = client;
profilesConfig = config;
this.setBorder(new EmptyBorder(18, 10, 0, 10));
this.setBackground(ColorScheme.DARK_GRAY_COLOR);
this.setLayout(new GridBagLayout());
this.c = new GridBagConstraints();
this.c.fill = 2;
this.c.gridx = 0;
this.c.gridy = 0;
this.c.weightx = 1.0;
this.c.weighty = 0.0;
this.c.insets = new Insets(0, 0, 4, 0);
this.txtAccountLabel.setPreferredSize(PREFERRED_SIZE);
this.txtAccountLabel.setForeground(ColorScheme.MEDIUM_GRAY_COLOR);
this.txtAccountLabel.setBackground(ColorScheme.DARKER_GRAY_COLOR);
this.txtAccountLabel.setMinimumSize(MINIMUM_SIZE);
this.txtAccountLabel.addFocusListener(new FocusListener(){
@Override
public void focusGained(FocusEvent e) {
if (ProfilesPanel.this.txtAccountLabel.getText().equals(ProfilesPanel.ACCOUNT_LABEL)) {
ProfilesPanel.this.txtAccountLabel.setText("");
ProfilesPanel.this.txtAccountLabel.setForeground(ColorScheme.LIGHT_GRAY_COLOR);
}
}
@Override
public void focusLost(FocusEvent e) {
if (ProfilesPanel.this.txtAccountLabel.getText().isEmpty()) {
ProfilesPanel.this.txtAccountLabel.setForeground(ColorScheme.MEDIUM_GRAY_COLOR);
ProfilesPanel.this.txtAccountLabel.setText(ProfilesPanel.ACCOUNT_LABEL);
}
}
});
this.add((Component)this.txtAccountLabel, this.c);
++this.c.gridy;
this.txtAccountLogin.setEchoChar('\u0000');
this.txtAccountLogin.setPreferredSize(PREFERRED_SIZE);
this.txtAccountLogin.setForeground(ColorScheme.MEDIUM_GRAY_COLOR);
this.txtAccountLogin.setBackground(ColorScheme.DARKER_GRAY_COLOR);
this.txtAccountLogin.setMinimumSize(MINIMUM_SIZE);
this.txtAccountLogin.addFocusListener(new FocusListener(){
@Override
public void focusGained(FocusEvent e) {
if (ProfilesPanel.ACCOUNT_USERNAME.equals(String.valueOf(ProfilesPanel.this.txtAccountLogin.getPassword()))) {
ProfilesPanel.this.txtAccountLogin.setText("");
if (config.isStreamerMode()) {
ProfilesPanel.this.txtAccountLogin.setEchoChar('*');
}
ProfilesPanel.this.txtAccountLogin.setForeground(ColorScheme.LIGHT_GRAY_COLOR);
}
}
@Override
public void focusLost(FocusEvent e) {
if (ProfilesPanel.this.txtAccountLogin.getPassword().length == 0) {
ProfilesPanel.this.txtAccountLogin.setForeground(ColorScheme.MEDIUM_GRAY_COLOR);
ProfilesPanel.this.txtAccountLogin.setText(ProfilesPanel.ACCOUNT_USERNAME);
ProfilesPanel.this.txtAccountLogin.setEchoChar('\u0000');
}
}
});
this.add((Component)this.txtAccountLogin, this.c);
++this.c.gridy;
this.txtPasswordLogin.setEchoChar('\u0000');
this.txtPasswordLogin.setPreferredSize(PREFERRED_SIZE);
this.txtPasswordLogin.setForeground(ColorScheme.MEDIUM_GRAY_COLOR);
this.txtPasswordLogin.setBackground(ColorScheme.DARKER_GRAY_COLOR);
this.txtPasswordLogin.setToolTipText("Account password");
this.txtPasswordLogin.setMinimumSize(MINIMUM_SIZE);
this.txtPasswordLogin.addFocusListener(new FocusListener(){
@Override
public void focusGained(FocusEvent e) {
if (ProfilesPanel.PASSWORD_LABEL.equals(String.valueOf(ProfilesPanel.this.txtPasswordLogin.getPassword()))) {
ProfilesPanel.this.txtPasswordLogin.setText("");
ProfilesPanel.this.txtPasswordLogin.setEchoChar('*');
ProfilesPanel.this.txtPasswordLogin.setForeground(ColorScheme.LIGHT_GRAY_COLOR);
}
}
@Override
public void focusLost(FocusEvent e) {
if (ProfilesPanel.this.txtPasswordLogin.getPassword().length == 0) {
ProfilesPanel.this.txtPasswordLogin.setForeground(ColorScheme.MEDIUM_GRAY_COLOR);
ProfilesPanel.this.txtPasswordLogin.setText(ProfilesPanel.PASSWORD_LABEL);
ProfilesPanel.this.txtPasswordLogin.setEchoChar('\u0000');
}
}
});
if (config.rememberPassword()) {
this.add((Component)this.txtPasswordLogin, this.c);
++this.c.gridy;
}
this.c.insets = new Insets(0, 0, 15, 0);
final JButton btnAddAccount = new JButton("Add Account");
btnAddAccount.setPreferredSize(PREFERRED_SIZE);
btnAddAccount.setBackground(ColorScheme.DARKER_GRAY_COLOR);
btnAddAccount.setMinimumSize(MINIMUM_SIZE);
btnAddAccount.addActionListener(e -> {
String labelText = String.valueOf(this.txtAccountLabel.getText());
String loginText = String.valueOf(this.txtAccountLogin.getPassword());
String passwordText = String.valueOf(this.txtPasswordLogin.getPassword());
if (labelText.equals(ACCOUNT_LABEL) || loginText.equals(ACCOUNT_USERNAME)) {
return;
}
String data = config.rememberPassword() && this.txtPasswordLogin.getPassword() != null ? labelText + ":" + loginText + ":" + passwordText : labelText + ":" + loginText;
log.info(data);
this.addAccount(data);
ProfilesPanel.addProfile(data);
this.txtAccountLabel.setText(ACCOUNT_LABEL);
this.txtAccountLabel.setForeground(ColorScheme.MEDIUM_GRAY_COLOR);
this.txtAccountLogin.setText(ACCOUNT_USERNAME);
this.txtAccountLogin.setEchoChar('\u0000');
this.txtAccountLogin.setForeground(ColorScheme.MEDIUM_GRAY_COLOR);
this.txtPasswordLogin.setText(PASSWORD_LABEL);
this.txtPasswordLogin.setEchoChar('\u0000');
this.txtPasswordLogin.setForeground(ColorScheme.MEDIUM_GRAY_COLOR);
});
this.txtAccountLogin.addKeyListener(new KeyAdapter(){
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == 10) {
btnAddAccount.doClick();
btnAddAccount.requestFocus();
}
}
});
this.txtAccountLogin.addMouseListener(new MouseListener(){
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mousePressed(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
});
this.add((Component)btnAddAccount, this.c);
++this.c.gridy;
this.profilesPanel.setLayout(new GridBagLayout());
this.add((Component)this.profilesPanel, this.c);
this.c.gridy = 0;
this.c.insets = new Insets(0, 0, 5, 0);
this.addAccounts(config.profilesData());
}
void redrawProfiles() {
this.profilesPanel.removeAll();
this.c.gridy = 0;
this.addAccounts(profilesConfig.profilesData());
}
private void addAccount(String data) {
ProfilePanel profile = new ProfilePanel(this.client, data, profilesConfig);
++this.c.gridy;
this.profilesPanel.add((Component)profile, this.c);
this.revalidate();
this.repaint();
}
void addAccounts(String data) {
if (!(data = data.trim()).contains(":")) {
return;
}
Arrays.stream(data.split("\\n")).forEach(this::addAccount);
}
static void addProfile(String data) {
profilesConfig.profilesData(profilesConfig.profilesData() + data + "\n");
}
static void removeProfile(String data) {
profilesConfig.profilesData(profilesConfig.profilesData().replaceAll(data + "\\n", ""));
}
@Slf4j
class ProfilesPanel extends PluginPanel
{
private static final int iterations = 100000;
private static final String UNLOCK_PASSWORD = "Encryption Password";
private static final String LOAD_ACCOUNTS = "Load Accounts";
private static final String ACCOUNT_USERNAME = "Account Username";
private static final String ACCOUNT_LABEL = "Account Label";
private static final String PASSWORD_LABEL = "Account Password";
private static final Dimension PREFERRED_SIZE = new Dimension(PluginPanel.PANEL_WIDTH - 20, 30);
private static final Dimension MINIMUM_SIZE = new Dimension(0, 30);
private final Client client;
private static ProfilesConfig profilesConfig;
private final JPasswordField txtDecryptPassword = new JPasswordField(UNLOCK_PASSWORD);
private final JButton btnLoadAccounts = new JButton(LOAD_ACCOUNTS);
private final JTextField txtAccountLabel = new JTextField(ACCOUNT_LABEL);
private final JPasswordField txtAccountLogin = new JPasswordField(ACCOUNT_USERNAME);
private final JPasswordField txtPasswordLogin = new JPasswordField(PASSWORD_LABEL);
private final JPanel profilesPanel = new JPanel();
private GridBagConstraints c;
@Inject
public ProfilesPanel(Client client, ProfilesConfig config)
{
super();
this.client = client;
profilesConfig = config;
setBorder(new EmptyBorder(18, 10, 0, 10));
setBackground(ColorScheme.DARK_GRAY_COLOR);
setLayout(new GridBagLayout());
c = new GridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 0;
c.gridy = 0;
c.weightx = 1;
c.weighty = 0;
c.insets = new Insets(0, 0, 4, 0);
txtDecryptPassword.setEchoChar((char) 0);
txtDecryptPassword.setPreferredSize(PREFERRED_SIZE);
txtDecryptPassword.setForeground(ColorScheme.MEDIUM_GRAY_COLOR);
txtDecryptPassword.setBackground(ColorScheme.DARKER_GRAY_COLOR);
txtDecryptPassword.setMinimumSize(MINIMUM_SIZE);
txtDecryptPassword.setToolTipText(UNLOCK_PASSWORD);
txtDecryptPassword.addFocusListener(new FocusListener()
{
@Override
public void focusGained(FocusEvent e)
{
txtDecryptPassword.setForeground(ColorScheme.LIGHT_GRAY_COLOR);
if (String.valueOf(txtDecryptPassword.getPassword()).equals(UNLOCK_PASSWORD))
{
txtDecryptPassword.setText("");
txtDecryptPassword.setEchoChar('*');
}
}
@Override
public void focusLost(FocusEvent e)
{
txtDecryptPassword.setForeground(ColorScheme.MEDIUM_GRAY_COLOR);
if (txtDecryptPassword.getPassword().length == 0)
{
txtDecryptPassword.setText(UNLOCK_PASSWORD);
txtDecryptPassword.setEchoChar((char) 0);
}
}
});
add(txtDecryptPassword, c);
c.gridy++;
btnLoadAccounts.setPreferredSize(PREFERRED_SIZE);
btnLoadAccounts.setBackground(ColorScheme.DARKER_GRAY_COLOR);
btnLoadAccounts.setMinimumSize(MINIMUM_SIZE);
btnLoadAccounts.setToolTipText(LOAD_ACCOUNTS);
btnLoadAccounts.addMouseListener(new MouseListener()
{
@Override
public void mouseClicked(MouseEvent e)
{
}
@Override
public void mousePressed(MouseEvent e)
{
}
@Override
public void mouseReleased(MouseEvent e)
{
try
{
redrawProfiles();
}
catch (InvalidKeySpecException | NoSuchAlgorithmException ex)
{
showErrorMessage("Unable to load data", "Incorrect password!");
}
}
@Override
public void mouseEntered(MouseEvent e)
{
}
@Override
public void mouseExited(MouseEvent e)
{
}
});
add(btnLoadAccounts, c);
c.gridy++;
txtAccountLabel.setPreferredSize(PREFERRED_SIZE);
txtAccountLabel.setForeground(ColorScheme.MEDIUM_GRAY_COLOR);
txtAccountLabel.setBackground(ColorScheme.DARKER_GRAY_COLOR);
txtAccountLabel.setMinimumSize(MINIMUM_SIZE);
txtAccountLabel.addFocusListener(new FocusListener()
{
@Override
public void focusGained(FocusEvent e)
{
if (txtAccountLabel.getText().equals(ACCOUNT_LABEL))
{
txtAccountLabel.setText("");
txtAccountLabel.setForeground(ColorScheme.LIGHT_GRAY_COLOR);
}
}
@Override
public void focusLost(FocusEvent e)
{
if (txtAccountLabel.getText().isEmpty())
{
txtAccountLabel.setForeground(ColorScheme.MEDIUM_GRAY_COLOR);
txtAccountLabel.setText(ACCOUNT_LABEL);
}
}
});
add(txtAccountLabel, c);
c.gridy++;
// Do not hide username characters until they focus or if in streamer mode
txtAccountLogin.setEchoChar((char) 0);
txtAccountLogin.setPreferredSize(PREFERRED_SIZE);
txtAccountLogin.setForeground(ColorScheme.MEDIUM_GRAY_COLOR);
txtAccountLogin.setBackground(ColorScheme.DARKER_GRAY_COLOR);
txtAccountLogin.setMinimumSize(MINIMUM_SIZE);
txtAccountLogin.addFocusListener(new FocusListener()
{
@Override
public void focusGained(FocusEvent e)
{
if (ACCOUNT_USERNAME.equals(String.valueOf(txtAccountLogin.getPassword())))
{
txtAccountLogin.setText("");
if (config.isStreamerMode())
{
txtAccountLogin.setEchoChar('*');
}
txtAccountLogin.setForeground(ColorScheme.LIGHT_GRAY_COLOR);
}
}
@Override
public void focusLost(FocusEvent e)
{
if (txtAccountLogin.getPassword().length == 0)
{
txtAccountLogin.setForeground(ColorScheme.MEDIUM_GRAY_COLOR);
txtAccountLogin.setText(ACCOUNT_USERNAME);
txtAccountLogin.setEchoChar((char) 0);
}
}
});
add(txtAccountLogin, c);
c.gridy++;
txtPasswordLogin.setEchoChar((char) 0);
txtPasswordLogin.setPreferredSize(PREFERRED_SIZE);
txtPasswordLogin.setForeground(ColorScheme.MEDIUM_GRAY_COLOR);
txtPasswordLogin.setBackground(ColorScheme.DARKER_GRAY_COLOR);
txtPasswordLogin.setToolTipText(PASSWORD_LABEL);
txtPasswordLogin.setMinimumSize(MINIMUM_SIZE);
txtPasswordLogin.addFocusListener(new FocusListener()
{
@Override
public void focusGained(FocusEvent e)
{
if (PASSWORD_LABEL.equals(String.valueOf(txtPasswordLogin.getPassword())))
{
txtPasswordLogin.setText("");
txtPasswordLogin.setEchoChar('*');
txtPasswordLogin.setForeground(ColorScheme.LIGHT_GRAY_COLOR);
}
}
@Override
public void focusLost(FocusEvent e)
{
if (txtPasswordLogin.getPassword().length == 0)
{
txtPasswordLogin.setForeground(ColorScheme.MEDIUM_GRAY_COLOR);
txtPasswordLogin.setText(PASSWORD_LABEL);
txtPasswordLogin.setEchoChar((char) 0);
}
}
});
if (config.rememberPassword())
{
add(txtPasswordLogin, c);
c.gridy++;
}
c.insets = new Insets(0, 0, 15, 0);
JButton btnAddAccount = new JButton("Add Account");
btnAddAccount.setPreferredSize(PREFERRED_SIZE);
btnAddAccount.setBackground(ColorScheme.DARKER_GRAY_COLOR);
btnAddAccount.setMinimumSize(MINIMUM_SIZE);
btnAddAccount.addActionListener(e ->
{
String labelText = String.valueOf(txtAccountLabel.getText());
String loginText = String.valueOf(txtAccountLogin.getPassword());
String passwordText = String.valueOf(txtPasswordLogin.getPassword());
if (labelText.equals(ACCOUNT_LABEL) || loginText.equals(ACCOUNT_USERNAME))
{
return;
}
String data;
if (config.rememberPassword() && txtPasswordLogin.getPassword() != null)
{
data = labelText + ":" + loginText + ":" + passwordText;
}
else
{
data = labelText + ":" + loginText;
}
try
{
if(!addProfile(data))
{
return;
}
}
catch (InvalidKeySpecException | NoSuchAlgorithmException ex)
{
ex.printStackTrace();
}
this.addAccount(data);
txtAccountLabel.setText(ACCOUNT_LABEL);
txtAccountLabel.setForeground(ColorScheme.MEDIUM_GRAY_COLOR);
txtAccountLogin.setText(ACCOUNT_USERNAME);
txtAccountLogin.setEchoChar((char) 0);
txtAccountLogin.setForeground(ColorScheme.MEDIUM_GRAY_COLOR);
txtPasswordLogin.setText(PASSWORD_LABEL);
txtPasswordLogin.setEchoChar((char) 0);
txtPasswordLogin.setForeground(ColorScheme.MEDIUM_GRAY_COLOR);
});
txtAccountLogin.addKeyListener(new KeyAdapter()
{
@Override
public void keyPressed(KeyEvent e)
{
if (e.getKeyCode() == KeyEvent.VK_ENTER)
{
btnAddAccount.doClick();
btnAddAccount.requestFocus();
}
}
});
txtAccountLogin.addMouseListener(new MouseListener()
{
@Override
public void mouseClicked(MouseEvent e)
{
}
@Override
public void mousePressed(MouseEvent e)
{
}
@Override
public void mouseReleased(MouseEvent e)
{
}
@Override
public void mouseEntered(MouseEvent e)
{
}
@Override
public void mouseExited(MouseEvent e)
{
}
});
add(btnAddAccount, c);
c.gridy++;
profilesPanel.setLayout(new GridBagLayout());
add(profilesPanel, c);
c.gridy = 0;
c.insets = new Insets(0, 0, 5, 0);
//addAccounts(config.profilesData());
}
void redrawProfiles() throws InvalidKeySpecException, NoSuchAlgorithmException
{
profilesPanel.removeAll();
c.gridy = 0;
addAccounts(getProfileData());
revalidate();
repaint();
}
private void addAccount(String data)
{
ProfilePanel profile = new ProfilePanel(client, data, profilesConfig, this);
c.gridy++;
profilesPanel.add(profile, c);
revalidate();
repaint();
}
void addAccounts(String data)
{
//log.info("Data: " + data);
data = data.trim();
if (!data.contains(":"))
{
return;
}
Arrays.stream(data.split("\\n")).forEach(this::addAccount);
}
boolean addProfile(String data) throws InvalidKeySpecException, NoSuchAlgorithmException
{
return setProfileData(
getProfileData() + data + "\n");
}
void removeProfile(String data) throws InvalidKeySpecException, NoSuchAlgorithmException
{
setProfileData(
getProfileData().replaceAll(data + "\\n", ""));
redrawProfiles();
}
void setSalt(byte[] bytes)
{
profilesConfig.salt(base64Encode(bytes));
}
byte[] getSalt()
{
if(profilesConfig.salt().length() == 0)
{
return new byte[0];
}
return base64Decode(profilesConfig.salt());
}
SecretKey getAesKey() throws NoSuchAlgorithmException, InvalidKeySpecException
{
if(getSalt().length == 0)
{
byte[] b = new byte[16];
SecureRandom.getInstanceStrong().nextBytes(b);
setSalt(b);
}
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(txtDecryptPassword.getPassword(), getSalt(), iterations, 128);
SecretKey key = factory.generateSecret(spec);
return key;
}
String getProfileData() throws InvalidKeySpecException, NoSuchAlgorithmException
{
String tmp = profilesConfig.profilesData();
if(tmp.startsWith("¬"))
{
if(txtDecryptPassword.getPassword().length == 0 || String.valueOf(txtDecryptPassword.getPassword()).equals(UNLOCK_PASSWORD))
{
showErrorMessage("Unable to load data", "Please enter a password!");
return tmp;
}
tmp = tmp.substring(1);
return decryptText(base64Decode(tmp), getAesKey());
}
return tmp;
}
boolean setProfileData(String data) throws InvalidKeySpecException, NoSuchAlgorithmException
{
if(txtDecryptPassword.getPassword().length == 0 || String.valueOf(txtDecryptPassword.getPassword()).equals(UNLOCK_PASSWORD))
{
showErrorMessage("Unable to save data", "Please enter a password!");
return false;
}
byte[] enc = encryptText(data, getAesKey());
if(enc.length == 0)
return false;
String s = "¬"+base64Encode(enc);
profilesConfig.profilesData(s);
return true;
}
public byte[] base64Decode(String data)
{
return Base64.getDecoder().decode(data);
}
public String base64Encode(byte[] data)
{
return Base64.getEncoder().encodeToString(data);
}
/**
* Encrypts login info
*
* @param text text to encrypt
* @return encrypted string
*/
public static byte[] encryptText(String text, SecretKey aesKey)
{
try
{
Cipher cipher = Cipher.getInstance("AES");
SecretKeySpec newKey = new SecretKeySpec(aesKey.getEncoded(), "AES");
cipher.init(Cipher.ENCRYPT_MODE, newKey);
return cipher.doFinal(text.getBytes());
}
catch (NoSuchAlgorithmException | IllegalBlockSizeException | InvalidKeyException | BadPaddingException | NoSuchPaddingException e)
{
e.printStackTrace();
}
return new byte[0];
}
public static String decryptText(byte[] enc, SecretKey aesKey)
{
try
{
Cipher cipher = Cipher.getInstance("AES");
SecretKeySpec newKey = new SecretKeySpec(aesKey.getEncoded(), "AES");
cipher.init(Cipher.DECRYPT_MODE, newKey);
return new String(cipher.doFinal(enc));
}
catch (NoSuchAlgorithmException | IllegalBlockSizeException | InvalidKeyException | BadPaddingException | NoSuchPaddingException e)
{
e.printStackTrace();
}
return "";
}
public static void showErrorMessage(String title, String text)
{
SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(null,
text,
title,
JOptionPane.ERROR_MESSAGE));
}
}

View File

@@ -1,10 +1,30 @@
/*
* Decompiled with CFR 0.139.
* Copyright (c) 2019, Spedwards <https://github.com/Spedwards>
* 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.profiles;
import com.google.inject.Provides;
import java.awt.image.BufferedImage;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
@@ -14,9 +34,10 @@ import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import javax.inject.Inject;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.events.ConfigChanged;
import net.runelite.api.events.GameStateChanged;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.plugins.Plugin;
@@ -25,112 +46,85 @@ import net.runelite.client.plugins.PluginType;
import net.runelite.client.ui.ClientToolbar;
import net.runelite.client.ui.NavigationButton;
import net.runelite.client.util.ImageUtil;
import javax.inject.Inject;
import java.awt.image.BufferedImage;
@PluginDescriptor(
name="Account Switcher",
description="Allow for a allows you to easily switch between multiple OSRS Accounts",
tags={"profile", "account", "login", "log in"},
type = PluginType.UTILITY
name = "Account Switcher",
description = "Allow for a allows you to easily switch between multiple OSRS Accounts",
tags = {"profile", "account", "login", "log in", "pklite"},
type = PluginType.UTILITY
)
public class ProfilesPlugin extends Plugin
{
@Inject
private ClientToolbar clientToolbar;
public class ProfilesPlugin
extends Plugin {
@Inject
private ClientToolbar clientToolbar;
@Inject
private Client client;
@Inject
private ProfilesConfig config;
private ProfilesPanel panel;
private NavigationButton navButton;
String text = "Hello World";
private static String key = "Bar12345Bar12345";
private static Key aesKey = new SecretKeySpec(key.getBytes(), "AES");
@Inject
private Client client;
@Provides
ProfilesConfig getConfig(ConfigManager configManager) {
return configManager.getConfig(ProfilesConfig.class);
}
@Inject
private ProfilesConfig config;
@Override
protected void startUp() throws Exception {
this.panel = this.injector.getInstance(ProfilesPanel.class);
BufferedImage icon = ImageUtil.getResourceStreamFromClass(this.getClass(), "/net/runelite/client/plugins/profiles/profiles_icon.png");
this.navButton = NavigationButton.builder().tooltip("Profiles").icon(icon).priority(8).panel(this.panel).build();
this.clientToolbar.addNavigation(this.navButton);
}
@Override
protected void shutDown() {
this.clientToolbar.removeNavigation(this.navButton);
}
@Subscribe
private void onConfigChanged(ConfigChanged event) throws Exception {
if (event.getGroup().equals("profiles") && event.getKey().equals("rememberPassword")) {
this.panel = this.injector.getInstance(ProfilesPanel.class);
this.shutDown();
this.startUp();
}
}
private ProfilesPanel panel;
private NavigationButton navButton;
public static String decryptText(String text) {
byte[] bb = new byte[text.length()];
for (int i = 0; i < text.length(); ++i) {
bb[i] = (byte)text.charAt(i);
}
Cipher cipher = null;
try {
cipher = Cipher.getInstance("AES");
}
catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
e.printStackTrace();
}
try {
cipher.init(2, aesKey);
}
catch (InvalidKeyException e) {
e.printStackTrace();
}
try {
Logger.getLogger("EncryptionLogger").info("Decrypted " + text + " to " + new String(cipher.doFinal(bb)));
return new String(cipher.doFinal(bb));
}
catch (BadPaddingException | IllegalBlockSizeException e) {
e.printStackTrace();
return "";
}
}
public static String encryptText(String text) {
try {
Cipher cipher = Cipher.getInstance("AES");
cipher.init(1, aesKey);
byte[] encrypted = cipher.doFinal(text.getBytes());
StringBuilder sb = new StringBuilder();
for (byte b : encrypted) {
sb.append((char)b);
}
Logger.getLogger("EncryptionLogger").info("Encrypted " + text + " to " + sb.toString());
return sb.toString();
}
catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
catch (NoSuchPaddingException e) {
e.printStackTrace();
}
catch (BadPaddingException e) {
e.printStackTrace();
}
catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
catch (InvalidKeyException e) {
e.printStackTrace();
}
return "";
}
@Provides
ProfilesConfig getConfig(ConfigManager configManager)
{
return configManager.getConfig(ProfilesConfig.class);
}
@Override
protected void startUp() throws Exception
{
panel = injector.getInstance(ProfilesPanel.class);
final BufferedImage icon = ImageUtil.getResourceStreamFromClass(getClass(), "profiles_icon.png");
navButton = NavigationButton.builder()
.tooltip("Profiles")
.icon(icon)
.priority(8)
.panel(panel)
.build();
clientToolbar.addNavigation(navButton);
}
@Override
protected void shutDown()
{
clientToolbar.removeNavigation(navButton);
}
@Subscribe
void onGameStateChanged(GameStateChanged event)
{
if (event.getGameState().equals(GameState.LOGIN_SCREEN) && config.switchPanel())
{
if (!navButton.isSelected())
{
navButton.getOnSelect().run();
}
}
}
@Subscribe
private void onConfigChanged(ConfigChanged event) throws Exception
{
if (event.getGroup().equals("profiles"))
{
if (event.getKey().equals("rememberPassword"))
{
panel = injector.getInstance(ProfilesPanel.class);
this.shutDown();
this.startUp();
}
}
}
}