party: add party panel

This commit is contained in:
JOROUSS-2070\JoRouss
2021-03-29 01:07:10 -04:00
committed by Adam
parent ebfc3dab89
commit 8450795bab
17 changed files with 863 additions and 137 deletions

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 2021, Jonathan Rousseau <https://github.com/JoRouss>
* 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.events;
import java.awt.image.BufferedImage;
import java.util.UUID;
import lombok.Value;
@Value
public class PartyMemberAvatar
{
private final UUID memberId;
private final BufferedImage image;
}

View File

@@ -1,6 +1,7 @@
/*
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
* Copyright (c) 2018, PandahRS <https://github.com/PandahRS>
* Copyright (c) 2021, Jonathan Rousseau <https://github.com/JoRouss>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -50,7 +51,6 @@ import net.runelite.api.events.StatChanged;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.discord.DiscordService;
import net.runelite.client.discord.events.DiscordJoinGame;
import net.runelite.client.discord.events.DiscordJoinRequest;
import net.runelite.client.discord.events.DiscordReady;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.events.ConfigChanged;
@@ -215,20 +215,6 @@ public class DiscordPlugin extends Plugin
partyService.setUsername(event.getUsername() + "#" + event.getDiscriminator());
}
@Subscribe
public void onDiscordJoinRequest(DiscordJoinRequest request)
{
// In order for the "Invite to join" message to work we need to have a valid party in Discord presence.
// We lazily create the party here in order to avoid the (1 of 15) being permanently in the Discord status.
if (!partyService.isInParty())
{
// Change to my party id, which is advertised in the Discord presence secret. This will open the socket,
// send a join, and cause a UserJoin later for me, which will then update the presence and allow the
// "Invite to join" to continue.
partyService.changeParty(partyService.getLocalPartyId());
}
}
@Subscribe
public void onDiscordJoinGame(DiscordJoinGame joinGame)
{
@@ -303,7 +289,8 @@ public class DiscordPlugin extends Plugin
{
image = ImageIO.read(inputStream);
}
memberById.setAvatar(image);
partyService.setPartyMemberAvatar(memberById.getMemberId(), image);
}
finally
{

View File

@@ -1,5 +1,6 @@
/*
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
* Copyright (c) 2021, Jonathan Rousseau <https://github.com/JoRouss>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -31,7 +32,6 @@ import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import javax.inject.Inject;
@@ -55,7 +55,6 @@ class DiscordState
private Instant updated;
}
private final UUID partyId = UUID.randomUUID();
private final List<EventWithTime> events = new ArrayList<>();
private final DiscordService discordService;
private final DiscordConfig config;
@@ -106,15 +105,10 @@ class DiscordState
.largeImageText(lastPresence.getLargeImageText())
.startTimestamp(lastPresence.getStartTimestamp())
.smallImageKey(lastPresence.getSmallImageKey())
.partyMax(lastPresence.getPartyMax())
.partySize(party.getMembers().size());
.partyMax(lastPresence.getPartyMax());
if (!party.isInParty() || party.isPartyOwner())
{
// This is only used to identify the invites on Discord's side. Our party ids are the secret.
presenceBuilder.partyId(partyId.toString());
presenceBuilder.joinSecret(party.getLocalPartyId().toString());
}
setPresencePartyInfo(presenceBuilder);
discordService.updatePresence(presenceBuilder.build());
}
@@ -205,8 +199,7 @@ class DiscordState
.details(MoreObjects.firstNonNull(details, ""))
.largeImageText(runeliteTitle + " v" + versionShortHand)
.smallImageKey(imageKey)
.partyMax(PARTY_MAX)
.partySize(party.getMembers().size());
.partyMax(PARTY_MAX);
final Instant startTime;
switch (config.elapsedTimeType())
@@ -233,11 +226,7 @@ class DiscordState
presenceBuilder.startTimestamp(startTime);
if (!party.isInParty() || party.isPartyOwner())
{
presenceBuilder.partyId(partyId.toString());
presenceBuilder.joinSecret(party.getLocalPartyId().toString());
}
setPresencePartyInfo(presenceBuilder);
final DiscordPresence presence = presenceBuilder.build();
@@ -286,4 +275,16 @@ class DiscordState
updatePresenceWithLatestEvent();
}
}
private void setPresencePartyInfo(DiscordPresence.DiscordPresenceBuilder presenceBuilder)
{
if (party.isInParty())
{
presenceBuilder.partySize(party.getMembers().size());
// Set public party id and secret
presenceBuilder.partyId(party.getPublicPartyId().toString());
presenceBuilder.joinSecret(party.getPartyId().toString());
}
}
}

View File

@@ -1,5 +1,6 @@
/*
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
* Copyright (c) 2021, Jonathan Rousseau <https://github.com/JoRouss>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -28,23 +29,16 @@ import net.runelite.client.config.Config;
import net.runelite.client.config.ConfigGroup;
import net.runelite.client.config.ConfigItem;
@ConfigGroup("party")
@ConfigGroup(PartyConfig.GROUP)
public interface PartyConfig extends Config
{
@ConfigItem(
keyName = "stats",
name = "Stats",
description = "Enables party stats overlay showing HP, prayer and player name"
)
default boolean stats()
{
return true;
}
String GROUP = "party";
@ConfigItem(
keyName = "pings",
name = "Pings",
description = "Enables party pings (shift + left-click)"
description = "Enables party pings (shift + left-click)",
position = 1
)
default boolean pings()
{
@@ -54,7 +48,8 @@ public interface PartyConfig extends Config
@ConfigItem(
keyName = "sounds",
name = "Sound on ping",
description = "Enables sound notification on party ping"
description = "Enables sound notification on party ping",
position = 2
)
default boolean sounds()
{
@@ -64,7 +59,8 @@ public interface PartyConfig extends Config
@ConfigItem(
keyName = "messages",
name = "Join messages",
description = "Enables join/leave game messages"
description = "Enables members join/leave game messages",
position = 3
)
default boolean messages()
{
@@ -74,10 +70,33 @@ public interface PartyConfig extends Config
@ConfigItem(
keyName = "recolorNames",
name = "Recolor names",
description = "Recolor stats overlay names based on unique color hash"
description = "Recolor party members names based on unique color hash",
position = 4
)
default boolean recolorNames()
{
return true;
}
@ConfigItem(
keyName = "autoOverlay",
name = "Auto overlay",
description = "Automatically add an overlay with player data when a member joins",
position = 5
)
default boolean autoOverlay()
{
return true;
}
@ConfigItem(
keyName = "includeSelf",
name = "Include yourself",
description = "Shows yourself in the panel as part of the party",
position = 6
)
default boolean includeSelf()
{
return false;
}
}

View File

@@ -0,0 +1,192 @@
/*
* Copyright (c) 2021, Jonathan Rousseau <https://github.com/JoRouss>
* 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.party;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.SwingConstants;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import lombok.AccessLevel;
import lombok.Getter;
import net.runelite.client.plugins.party.data.PartyData;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.DynamicGridLayout;
import net.runelite.client.ui.FontManager;
import net.runelite.client.ui.components.MouseDragEventForwarder;
import net.runelite.client.ui.components.ProgressBar;
import net.runelite.client.util.ImageUtil;
class PartyMemberBox extends JPanel
{
private static final Color HP_FG = new Color(0, 146, 54, 230);
private static final Color HP_BG = new Color(102, 15, 16, 230);
private static final Color PRAY_FG = new Color(0, 149, 151);
private static final Color PRAY_BG = Color.black;
@Getter(AccessLevel.PACKAGE)
private final PartyData memberPartyData;
private final ProgressBar hpBar = new ProgressBar();
private final ProgressBar prayerBar = new ProgressBar();
private final JLabel topName = new JLabel();
private final JLabel bottomName = new JLabel();
private final JLabel avatar = new JLabel();
private final PartyConfig config;
private boolean avatarSet;
PartyMemberBox(final PartyConfig config, final JComponent panel, final PartyData memberPartyData)
{
this.config = config;
this.memberPartyData = memberPartyData;
setLayout(new BorderLayout());
setBorder(new EmptyBorder(5, 0, 0, 0));
/* The box's wrapping container */
final JPanel container = new JPanel();
container.setLayout(new BorderLayout());
container.setBackground(ColorScheme.DARKER_GRAY_COLOR);
container.setBorder(new EmptyBorder(5, 5, 5, 5));
// Create Toggle overlay
final JMenuItem overlay = new JMenuItem("Toggle overlay");
overlay.addActionListener(e -> memberPartyData.setShowOverlay(!memberPartyData.isShowOverlay()));
// Create popup menu
final JPopupMenu popupMenu = new JPopupMenu();
popupMenu.setBorder(new EmptyBorder(5, 5, 5, 5));
popupMenu.add(overlay);
// create a line border with the specified color and width
Border border = BorderFactory.createLineBorder(Color.gray, 1);
avatar.setBorder(border);
avatar.setHorizontalAlignment(SwingConstants.CENTER);
avatar.setVerticalAlignment(SwingConstants.CENTER);
avatar.setPreferredSize(new Dimension(35, 35));
/* Contains the avatar and the names */
final JPanel headerPanel = new JPanel();
headerPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR);
headerPanel.setLayout(new BorderLayout());
headerPanel.setBorder(new EmptyBorder(0, 0, 3, 0));
/* Contains ServiceName name and osrs name */
final JPanel namesPanel = new JPanel();
namesPanel.setLayout(new DynamicGridLayout(2, 1));
namesPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR);
namesPanel.setBorder(new EmptyBorder(2, 5, 2, 5));
topName.setFont(FontManager.getRunescapeSmallFont());
bottomName.setFont(FontManager.getRunescapeSmallFont());
topName.putClientProperty("html.disable", Boolean.TRUE);
bottomName.putClientProperty("html.disable", Boolean.TRUE);
namesPanel.add(topName); // top
namesPanel.add(bottomName); // bottom
headerPanel.add(avatar, BorderLayout.WEST);
headerPanel.add(namesPanel, BorderLayout.CENTER);
JPanel progressWrapper = new JPanel();
progressWrapper.setBackground(ColorScheme.DARKER_GRAY_COLOR);
progressWrapper.setLayout(new DynamicGridLayout(2, 1, 0, 2));
hpBar.setBackground(HP_BG);
hpBar.setForeground(HP_FG);
prayerBar.setBackground(PRAY_BG);
prayerBar.setForeground(PRAY_FG);
progressWrapper.add(hpBar); // top
progressWrapper.add(prayerBar); // bottom
container.add(headerPanel, BorderLayout.NORTH);
container.add(progressWrapper, BorderLayout.SOUTH);
container.setComponentPopupMenu(popupMenu);
// forward mouse drag events to parent panel for drag and drop reordering
MouseDragEventForwarder mouseDragEventForwarder = new MouseDragEventForwarder(panel);
container.addMouseListener(mouseDragEventForwarder);
container.addMouseMotionListener(mouseDragEventForwarder);
add(container, BorderLayout.NORTH);
update();
}
void update()
{
// Avatar
if (!avatarSet && memberPartyData.getMember().getAvatar() != null)
{
ImageIcon icon = new ImageIcon(ImageUtil.resizeImage(memberPartyData.getMember().getAvatar(), 32, 32));
icon.getImage().flush();
avatar.setIcon(icon);
avatarSet = true;
}
// Update progress bars
hpBar.setValue(memberPartyData.getHitpoints());
hpBar.setMaximumValue(memberPartyData.getMaxHitpoints());
hpBar.setCenterLabel(progressBarLabel(memberPartyData.getHitpoints(), memberPartyData.getMaxHitpoints()));
prayerBar.setValue(memberPartyData.getPrayer());
prayerBar.setMaximumValue(memberPartyData.getMaxPrayer());
prayerBar.setCenterLabel(progressBarLabel(memberPartyData.getPrayer(), memberPartyData.getMaxPrayer()));
// Update name labels
Color playerColor = config.recolorNames() ? memberPartyData.getColor() : Color.WHITE;
boolean isLoggedIn = !memberPartyData.getCharacterName().isEmpty();
topName.setForeground(playerColor);
topName.setText(memberPartyData.getMember().getName());
bottomName.setForeground(isLoggedIn ? playerColor : Color.GRAY);
bottomName.setText(isLoggedIn ? memberPartyData.getCharacterName() : "Logged out");
}
private static String progressBarLabel(int current, int max)
{
return current + "/" + max;
}
}

View File

@@ -0,0 +1,214 @@
/*
* Copyright (c) 2021, Jonathan Rousseau <https://github.com/JoRouss>
* 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.party;
import com.google.inject.Inject;
import java.awt.BorderLayout;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import net.runelite.client.plugins.party.data.PartyData;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.PluginPanel;
import net.runelite.client.ui.components.DragAndDropReorderPane;
import net.runelite.client.ui.components.PluginErrorPanel;
import net.runelite.client.ws.PartyService;
class PartyPanel extends PluginPanel
{
private static final String BTN_CREATE_TEXT = "Create party";
private static final String BTN_LEAVE_TEXT = "Leave party";
private final PartyPlugin plugin;
private final PartyService party;
private final PartyConfig config;
private final Map<String, PartyRequestBox> requestBoxes = new HashMap<>();
private final Map<UUID, PartyMemberBox> memberBoxes = new HashMap<>();
private final JButton startButton = new JButton();
private final PluginErrorPanel noPartyPanel = new PluginErrorPanel();
private final PluginErrorPanel partyEmptyPanel = new PluginErrorPanel();
private final JComponent memberBoxPanel = new DragAndDropReorderPane();
private final JComponent requestBoxPanel = new DragAndDropReorderPane();
@Inject
PartyPanel(final PartyPlugin plugin, final PartyConfig config, final PartyService party)
{
this.plugin = plugin;
this.party = party;
this.config = config;
setBorder(new EmptyBorder(10, 10, 10, 10));
setBackground(ColorScheme.DARK_GRAY_COLOR);
setLayout(new BorderLayout());
final JPanel layoutPanel = new JPanel();
BoxLayout boxLayout = new BoxLayout(layoutPanel, BoxLayout.Y_AXIS);
layoutPanel.setLayout(boxLayout);
add(layoutPanel, BorderLayout.NORTH);
final JPanel topPanel = new JPanel();
topPanel.setBorder(new EmptyBorder(0, 0, 10, 0));
topPanel.setLayout(new BorderLayout());
topPanel.add(startButton, BorderLayout.CENTER);
layoutPanel.add(topPanel);
layoutPanel.add(requestBoxPanel);
layoutPanel.add(memberBoxPanel);
startButton.setText(party.isInParty() ? BTN_LEAVE_TEXT : BTN_CREATE_TEXT);
startButton.setFocusable(false);
topPanel.add(startButton);
startButton.addActionListener(e ->
{
if (party.isInParty())
{
// Leave party
final int result = JOptionPane.showOptionDialog(startButton,
"Are you sure you want to leave the party?",
"Leave party?", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE,
null, new String[]{"Yes", "No"}, "No");
if (result == JOptionPane.YES_OPTION)
{
party.changeParty(null);
}
}
else
{
// Create party
party.changeParty(party.getLocalPartyId());
}
});
noPartyPanel.setContent("Not in a party", "Create a party to begin.");
partyEmptyPanel.setContent("Party created", "You can now invite friends!");
updateParty();
}
void updateParty()
{
remove(noPartyPanel);
remove(partyEmptyPanel);
startButton.setText(party.isInParty() ? BTN_LEAVE_TEXT : BTN_CREATE_TEXT);
if (!party.isInParty())
{
add(noPartyPanel);
}
else if (plugin.getPartyDataMap().size() <= 1)
{
add(partyEmptyPanel);
}
}
void addMember(PartyData partyData)
{
if (!memberBoxes.containsKey(partyData.getMember().getMemberId()))
{
PartyMemberBox partyMemberBox = new PartyMemberBox(config, memberBoxPanel, partyData);
memberBoxes.put(partyData.getMember().getMemberId(), partyMemberBox);
memberBoxPanel.add(partyMemberBox);
memberBoxPanel.revalidate();
}
updateParty();
}
void removeAllMembers()
{
memberBoxes.forEach((key, value) -> memberBoxPanel.remove(value));
memberBoxPanel.revalidate();
memberBoxes.clear();
updateParty();
}
void removeMember(UUID memberId)
{
final PartyMemberBox memberBox = memberBoxes.remove(memberId);
if (memberBox != null)
{
memberBoxPanel.remove(memberBox);
memberBoxPanel.revalidate();
}
updateParty();
}
void updateMember(UUID userId)
{
final PartyMemberBox memberBox = memberBoxes.get(userId);
if (memberBox != null)
{
memberBox.update();
}
}
void updateAll()
{
memberBoxes.forEach((key, value) -> value.update());
}
void addRequest(String userId, String userName)
{
PartyRequestBox partyRequestBox = new PartyRequestBox(plugin, requestBoxPanel, userId, userName);
requestBoxes.put(userId, partyRequestBox);
requestBoxPanel.add(partyRequestBox);
requestBoxPanel.revalidate();
}
void removeAllRequests()
{
requestBoxes.forEach((key, value) -> requestBoxPanel.remove(value));
requestBoxPanel.revalidate();
requestBoxes.clear();
}
void removeRequest(String userId)
{
final PartyRequestBox requestBox = requestBoxes.remove(userId);
if (requestBox != null)
{
requestBoxPanel.remove(requestBox);
requestBoxPanel.revalidate();
}
}
}

View File

@@ -1,5 +1,6 @@
/*
* Copyright (c) 2019, Tomas Slusny <slusnucky@gmail.com>
* Copyright (c) 2021, Jonathan Rousseau <https://github.com/JoRouss>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,9 +25,11 @@
*/
package net.runelite.client.plugins.party;
import com.google.common.base.Strings;
import com.google.inject.Binder;
import com.google.inject.Provides;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Collections;
@@ -37,18 +40,20 @@ import java.util.UUID;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Named;
import javax.swing.SwingUtilities;
import lombok.Getter;
import net.runelite.api.ChatMessageType;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.KeyCode;
import net.runelite.api.MenuAction;
import net.runelite.api.MenuEntry;
import net.runelite.api.Player;
import net.runelite.api.Skill;
import net.runelite.api.SoundEffectID;
import net.runelite.api.Tile;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.events.CommandExecuted;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.GameTick;
import net.runelite.api.events.MenuOptionClicked;
import net.runelite.client.callback.ClientThread;
@@ -57,21 +62,29 @@ 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.discord.DiscordService;
import net.runelite.client.discord.events.DiscordJoinRequest;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.events.OverlayMenuClicked;
import net.runelite.client.events.ConfigChanged;
import net.runelite.client.events.PartyChanged;
import net.runelite.client.events.PartyMemberAvatar;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.plugins.party.data.PartyData;
import net.runelite.client.plugins.party.data.PartyTilePingData;
import net.runelite.client.plugins.party.messages.CharacterNameUpdate;
import net.runelite.client.plugins.party.messages.LocationUpdate;
import net.runelite.client.plugins.party.messages.SkillUpdate;
import net.runelite.client.plugins.party.messages.TilePing;
import net.runelite.client.task.Schedule;
import net.runelite.client.ui.ClientToolbar;
import net.runelite.client.ui.NavigationButton;
import net.runelite.client.ui.overlay.OverlayManager;
import net.runelite.client.ui.overlay.worldmap.WorldMapPoint;
import net.runelite.client.ui.overlay.worldmap.WorldMapPointManager;
import net.runelite.client.util.ColorUtil;
import net.runelite.client.util.ImageUtil;
import net.runelite.client.util.Text;
import net.runelite.client.ws.PartyMember;
import net.runelite.client.ws.PartyService;
import net.runelite.client.ws.WSClient;
@@ -81,7 +94,8 @@ import net.runelite.http.api.ws.messages.party.UserSync;
@PluginDescriptor(
name = "Party",
description = "Shows useful information about current party"
description = "Party management and basic info",
enabledByDefault = false
)
public class PartyPlugin extends Plugin
{
@@ -118,6 +132,12 @@ public class PartyPlugin extends Plugin
@Inject
private ClientThread clientThread;
@Inject
private ClientToolbar clientToolbar;
@Inject
private DiscordService discordService;
@Inject
@Named("developerMode")
boolean developerMode;
@@ -128,8 +148,11 @@ public class PartyPlugin extends Plugin
@Getter
private final List<PartyTilePingData> pendingTilePings = Collections.synchronizedList(new ArrayList<>());
private PartyPanel panel;
private NavigationButton navButton;
private int lastHp, lastPray;
private boolean doSync;
private String lastCharacterName = "";
private boolean sendAlert;
@Override
@@ -141,17 +164,36 @@ public class PartyPlugin extends Plugin
@Override
protected void startUp() throws Exception
{
panel = injector.getInstance(PartyPanel.class);
final BufferedImage icon = ImageUtil.loadImageResource(PartyPlugin.class, "panel_icon.png");
navButton = NavigationButton.builder()
.tooltip("Party")
.icon(icon)
.priority(9)
.panel(panel)
.build();
clientToolbar.addNavigation(navButton);
overlayManager.add(partyStatsOverlay);
overlayManager.add(partyPingOverlay);
wsClient.registerMessage(SkillUpdate.class);
wsClient.registerMessage(TilePing.class);
wsClient.registerMessage(LocationUpdate.class);
doSync = true; // Delay sync so eventbus can process correctly.
wsClient.registerMessage(CharacterNameUpdate.class);
// Delay sync so the eventbus can register prior to the sync response
SwingUtilities.invokeLater(this::requestSync);
}
@Override
protected void shutDown() throws Exception
{
clientToolbar.removeNavigation(navButton);
panel = null;
partyDataMap.clear();
pendingTilePings.clear();
worldMapManager.removeIf(PartyWorldMapPoint.class::isInstance);
@@ -160,7 +202,7 @@ public class PartyPlugin extends Plugin
wsClient.unregisterMessage(SkillUpdate.class);
wsClient.unregisterMessage(TilePing.class);
wsClient.unregisterMessage(LocationUpdate.class);
doSync = false;
wsClient.unregisterMessage(CharacterNameUpdate.class);
sendAlert = false;
}
@@ -171,28 +213,28 @@ public class PartyPlugin extends Plugin
}
@Subscribe
public void onOverlayMenuClicked(OverlayMenuClicked event)
public void onConfigChanged(ConfigChanged event)
{
if (event.getEntry().getMenuAction() == MenuAction.RUNELITE_OVERLAY &&
event.getEntry().getTarget().equals("Party") &&
event.getEntry().getOption().equals("Leave"))
if (event.getGroup().equals(PartyConfig.GROUP))
{
party.changeParty(null);
final PartyMember localMember = party.getLocalMember();
if (!config.messages())
if (localMember != null)
{
return;
if (config.includeSelf())
{
final PartyData partyData = getPartyData(localMember.getMemberId());
assert partyData != null;
SwingUtilities.invokeLater(() -> panel.addMember(partyData));
}
else
{
SwingUtilities.invokeLater(() -> panel.removeMember(localMember.getMemberId()));
}
}
final String leaveMessage = new ChatMessageBuilder()
.append(ChatColorType.HIGHLIGHT)
.append("You have left the party.")
.build();
chatMessageManager.queue(QueuedMessage.builder()
.type(ChatMessageType.FRIENDSCHATNOTIFICATION)
.runeLiteFormattedMessage(leaveMessage)
.build());
// rebuild the panel in the event the "Recolor names" option changes
SwingUtilities.invokeLater(panel::updateAll);
}
}
@@ -236,6 +278,35 @@ public class PartyPlugin extends Plugin
wsClient.send(tilePing);
}
@Subscribe
public void onDiscordJoinRequest(DiscordJoinRequest request)
{
final String requestMessage = new ChatMessageBuilder()
.append(ChatColorType.HIGHLIGHT)
.append("New join request received. Check your Party panel.")
.build();
chatMessageManager.queue(QueuedMessage.builder()
.type(ChatMessageType.FRIENDSCHATNOTIFICATION)
.runeLiteFormattedMessage(requestMessage)
.build());
String userName = request.getUsername() + "#" + request.getDiscriminator();
SwingUtilities.invokeLater(() -> panel.addRequest(request.getUserId(), userName));
}
@Subscribe
public void onGameStateChanged(GameStateChanged event)
{
checkStateChanged(false);
}
public void replyToRequest(String userId, int reply)
{
discordService.respondToRequest(userId, reply);
panel.removeRequest(userId);
}
@Subscribe
public void onTilePing(TilePing event)
{
@@ -291,41 +362,35 @@ public class PartyPlugin extends Plugin
sendInstructionMessage();
}
if (doSync && !party.getMembers().isEmpty())
checkStateChanged(false);
}
void requestSync()
{
if (!party.getMembers().isEmpty())
{
// Request sync
final UserSync userSync = new UserSync();
userSync.setMemberId(party.getLocalMember().getMemberId());
ws.send(userSync);
}
}
doSync = false;
@Subscribe
public void onCharacterNameUpdate(final CharacterNameUpdate event)
{
final PartyData partyData = getPartyData(event.getMemberId());
final int currentHealth = client.getBoostedSkillLevel(Skill.HITPOINTS);
final int currentPrayer = client.getBoostedSkillLevel(Skill.PRAYER);
final int realHealth = client.getRealSkillLevel(Skill.HITPOINTS);
final int realPrayer = client.getRealSkillLevel(Skill.PRAYER);
final PartyMember localMember = party.getLocalMember();
if (localMember != null)
if (partyData == null)
{
if (currentHealth != lastHp)
{
final SkillUpdate update = new SkillUpdate(Skill.HITPOINTS, currentHealth, realHealth);
update.setMemberId(localMember.getMemberId());
ws.send(update);
}
if (currentPrayer != lastPray)
{
final SkillUpdate update = new SkillUpdate(Skill.PRAYER, currentPrayer, realPrayer);
update.setMemberId(localMember.getMemberId());
ws.send(update);
}
return;
}
lastHp = currentHealth;
lastPray = currentPrayer;
String name = event.getCharacterName();
name = Text.removeTags(Text.toJagexName(name));
partyData.setCharacterName(name);
SwingUtilities.invokeLater(() -> panel.updateMember(partyData.getMember().getMemberId()));
}
@Subscribe
@@ -348,6 +413,8 @@ public class PartyPlugin extends Plugin
partyData.setPrayer(event.getValue());
partyData.setMaxPrayer(event.getMax());
}
SwingUtilities.invokeLater(() -> panel.updateMember(partyData.getMember().getMemberId()));
}
@Subscribe
@@ -375,7 +442,7 @@ public class PartyPlugin extends Plugin
final String joinMessage = new ChatMessageBuilder()
.append(ChatColorType.HIGHLIGHT)
.append(partyData.getName())
.append(partyData.getMember().getName())
.append(" has joined the party!")
.build();
@@ -386,7 +453,7 @@ public class PartyPlugin extends Plugin
final PartyMember localMember = party.getLocalMember();
if (localMember != null && partyData.getMemberId().equals(localMember.getMemberId()))
if (localMember != null && partyData.getMember().getMemberId().equals(localMember.getMemberId()))
{
sendAlert = true;
}
@@ -394,6 +461,11 @@ public class PartyPlugin extends Plugin
@Subscribe
public void onUserSync(final UserSync event)
{
checkStateChanged(true);
}
private void checkStateChanged(boolean forceSend)
{
final int currentHealth = client.getBoostedSkillLevel(Skill.HITPOINTS);
final int currentPrayer = client.getBoostedSkillLevel(Skill.PRAYER);
@@ -401,16 +473,36 @@ public class PartyPlugin extends Plugin
final int realPrayer = client.getRealSkillLevel(Skill.PRAYER);
final PartyMember localMember = party.getLocalMember();
final Player localPlayer = client.getLocalPlayer();
final String characterName = Strings.nullToEmpty(localPlayer != null && client.getGameState().getState() >= GameState.LOADING.getState() ? localPlayer.getName() : null);
if (localMember != null)
{
final SkillUpdate hpUpdate = new SkillUpdate(Skill.HITPOINTS, currentHealth, realHealth);
hpUpdate.setMemberId(localMember.getMemberId());
ws.send(hpUpdate);
if (forceSend || currentHealth != lastHp)
{
final SkillUpdate update = new SkillUpdate(Skill.HITPOINTS, currentHealth, realHealth);
update.setMemberId(localMember.getMemberId());
ws.send(update);
}
final SkillUpdate prayUpdate = new SkillUpdate(Skill.PRAYER, currentPrayer, realPrayer);
prayUpdate.setMemberId(localMember.getMemberId());
ws.send(prayUpdate);
if (forceSend || currentPrayer != lastPray)
{
final SkillUpdate update = new SkillUpdate(Skill.PRAYER, currentPrayer, realPrayer);
update.setMemberId(localMember.getMemberId());
ws.send(update);
}
if (forceSend || !characterName.equals(lastCharacterName))
{
final CharacterNameUpdate update = new CharacterNameUpdate(characterName);
update.setMemberId(localMember.getMemberId());
ws.send(update);
}
}
lastHp = currentHealth;
lastPray = currentPrayer;
lastCharacterName = characterName;
}
@Subscribe
@@ -424,7 +516,7 @@ public class PartyPlugin extends Plugin
{
final String joinMessage = new ChatMessageBuilder()
.append(ChatColorType.HIGHLIGHT)
.append(removed.getName())
.append(removed.getMember().getName())
.append(" has left the party!")
.build();
@@ -435,6 +527,8 @@ public class PartyPlugin extends Plugin
}
worldMapManager.remove(removed.getWorldMapPoint());
SwingUtilities.invokeLater(() -> panel.removeMember(event.getMemberId()));
}
}
@@ -445,6 +539,12 @@ public class PartyPlugin extends Plugin
partyDataMap.clear();
pendingTilePings.clear();
worldMapManager.removeIf(PartyWorldMapPoint.class::isInstance);
SwingUtilities.invokeLater(() ->
{
panel.removeAllMembers();
panel.removeAllRequests();
});
}
@Subscribe
@@ -464,6 +564,12 @@ public class PartyPlugin extends Plugin
}
}
@Subscribe
public void onPartyMemberAvatar(PartyMemberAvatar event)
{
SwingUtilities.invokeLater(() -> panel.updateMember(event.getMemberId()));
}
@Nullable
PartyData getPartyData(final UUID uuid)
{
@@ -479,18 +585,32 @@ public class PartyPlugin extends Plugin
return partyDataMap.computeIfAbsent(uuid, (u) ->
{
final String name = memberById.getName();
final WorldMapPoint worldMapPoint = new PartyWorldMapPoint(new WorldPoint(0, 0, 0), memberById);
worldMapPoint.setTooltip(name);
worldMapPoint.setTooltip(memberById.getName());
// When first joining a party, other members can join before getting a join for self
PartyMember partyMember = party.getLocalMember();
if (partyMember == null || !u.equals(partyMember.getMemberId()))
boolean isSelf = partyMember != null && u.equals(partyMember.getMemberId());
if (!isSelf)
{
worldMapManager.add(worldMapPoint);
}
return new PartyData(u, name, worldMapPoint, ColorUtil.fromObject(name));
PartyData partyData = new PartyData(memberById, worldMapPoint, ColorUtil.fromObject(memberById.getName()));
partyData.setShowOverlay(config.autoOverlay());
if (config.includeSelf() || !isSelf)
{
SwingUtilities.invokeLater(() -> panel.addMember(partyData));
}
else
{
SwingUtilities.invokeLater(panel::updateParty);
}
return partyData;
});
}
@@ -498,7 +618,7 @@ public class PartyPlugin extends Plugin
{
final String helpMessage = new ChatMessageBuilder()
.append(ChatColorType.HIGHLIGHT)
.append("To leave party hold SHIFT and right click party stats overlay.")
.append("To leave the party, click \"Leave party\" on the party panel.")
.build();
chatMessageManager.queue(QueuedMessage.builder()

View File

@@ -0,0 +1,111 @@
/*
* Copyright (c) 2021, Jonathan Rousseau <https://github.com/JoRouss>
* 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.party;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.DynamicGridLayout;
import net.runelite.client.ui.FontManager;
import net.runelite.client.ui.components.MouseDragEventForwarder;
import net.runelite.client.ui.components.shadowlabel.JShadowedLabel;
import net.runelite.client.util.ImageUtil;
import net.runelite.client.util.SwingUtil;
import net.runelite.discord.DiscordRPC;
class PartyRequestBox extends JPanel
{
private static final ImageIcon CONFIRM_ICON = new ImageIcon(ImageUtil.loadImageResource(PartyPlugin.class, "confirm_icon.png"));
private static final ImageIcon CONFIRM_HOVER_ICON = new ImageIcon(ImageUtil.alphaOffset(ImageUtil.bufferedImageFromImage(CONFIRM_ICON.getImage()), 0.54f));
private static final ImageIcon CANCEL_ICON = new ImageIcon(ImageUtil.loadImageResource(PartyPlugin.class, "cancel_icon.png"));
private static final ImageIcon CANCEL_HOVER_ICON = new ImageIcon(ImageUtil.alphaOffset(ImageUtil.bufferedImageFromImage(CANCEL_ICON.getImage()), 0.6f));
PartyRequestBox(final PartyPlugin plugin, final JComponent panel, String userId, String userName)
{
setLayout(new BorderLayout());
setBorder(new EmptyBorder(5, 0, 0, 0));
/* The box's wrapping container */
final JPanel container = new JPanel();
container.setLayout(new BorderLayout());
container.setBackground(ColorScheme.DARKER_GRAY_COLOR);
container.setBorder(new EmptyBorder(5, 5, 5, 5));
JPanel namesPanel = new JPanel();
namesPanel.setLayout(new DynamicGridLayout(2, 1));
namesPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR);
namesPanel.setBorder(new EmptyBorder(2, 5, 2, 5));
JShadowedLabel nameLabel = new JShadowedLabel();
nameLabel.setFont(FontManager.getRunescapeSmallFont());
nameLabel.setForeground(Color.WHITE);
nameLabel.setText(userName);
JShadowedLabel messageLabel = new JShadowedLabel();
messageLabel.setFont(FontManager.getRunescapeSmallFont());
messageLabel.setForeground(Color.WHITE);
messageLabel.setText("Wants to join your party!");
namesPanel.add(nameLabel);
namesPanel.add(messageLabel);
JPanel actionsContainer = new JPanel(new GridLayout(1, 2, 8, 0));
actionsContainer.setBackground(ColorScheme.DARKER_GRAY_COLOR);
JButton confirmButton = new JButton(CONFIRM_ICON);
SwingUtil.removeButtonDecorations(confirmButton);
confirmButton.setToolTipText("Invite");
confirmButton.setRolloverIcon(CONFIRM_HOVER_ICON);
confirmButton.addActionListener(e -> plugin.replyToRequest(userId, DiscordRPC.DISCORD_REPLY_YES));
confirmButton.setPreferredSize(new Dimension(18, 18));
JButton cancelButton = new JButton(CANCEL_ICON);
SwingUtil.removeButtonDecorations(cancelButton);
cancelButton.setToolTipText("Reject");
cancelButton.setRolloverIcon(CANCEL_HOVER_ICON);
cancelButton.addActionListener(e -> plugin.replyToRequest(userId, DiscordRPC.DISCORD_REPLY_NO));
cancelButton.setPreferredSize(new Dimension(18, 18));
actionsContainer.add(confirmButton);
actionsContainer.add(cancelButton);
container.add(namesPanel, BorderLayout.WEST);
container.add(actionsContainer, BorderLayout.EAST);
// forward mouse drag events to parent panel for drag and drop reordering
MouseDragEventForwarder mouseDragEventForwarder = new MouseDragEventForwarder(panel);
container.addMouseListener(mouseDragEventForwarder);
container.addMouseMotionListener(mouseDragEventForwarder);
add(container, BorderLayout.NORTH);
}
}

View File

@@ -1,5 +1,6 @@
/*
* Copyright (c) 2019, Tomas Slusny <slusnucky@gmail.com>
* Copyright (c) 2021, Jonathan Rousseau <https://github.com/JoRouss>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -32,9 +33,7 @@ import java.awt.Rectangle;
import java.util.Map;
import java.util.UUID;
import javax.inject.Inject;
import net.runelite.api.MenuAction;
import net.runelite.client.plugins.party.data.PartyData;
import net.runelite.client.ui.overlay.OverlayMenuEntry;
import net.runelite.client.ui.overlay.OverlayPanel;
import net.runelite.client.ui.overlay.components.ComponentConstants;
import net.runelite.client.ui.overlay.components.PanelComponent;
@@ -62,17 +61,11 @@ public class PartyStatsOverlay extends OverlayPanel
this.config = config;
panelComponent.setBorder(new Rectangle());
panelComponent.setGap(new Point(0, ComponentConstants.STANDARD_BORDER / 2));
getMenuEntries().add(new OverlayMenuEntry(MenuAction.RUNELITE_OVERLAY, "Leave", "Party"));
}
@Override
public Dimension render(Graphics2D graphics)
{
if (!config.stats())
{
return null;
}
final Map<UUID, PartyData> partyDataMap = plugin.getPartyDataMap();
if (partyDataMap.isEmpty())
{
@@ -81,22 +74,14 @@ public class PartyStatsOverlay extends OverlayPanel
panelComponent.setBackgroundColor(null);
boolean only1 = plugin.getPartyDataMap().size() == 1;
synchronized (plugin.getPartyDataMap())
{
partyDataMap.forEach((k, v) ->
{
if (party.getLocalMember() != null && party.getLocalMember().getMemberId().equals(k))
{
if (only1)
{
panelComponent.getChildren().add(TitleComponent.builder()
.text("No other party members")
.color(Color.RED)
.build());
}
boolean isSelf = party.getLocalMember() != null && party.getLocalMember().getMemberId().equals(k);
if (!v.isShowOverlay() || (!config.includeSelf() && isSelf))
{
return;
}
@@ -104,7 +89,7 @@ public class PartyStatsOverlay extends OverlayPanel
panel.getChildren().clear();
final TitleComponent name = TitleComponent.builder()
.text(v.getName())
.text(v.getCharacterName().isEmpty() ? v.getMember().getName() : v.getCharacterName())
.color(config.recolorNames() ? v.getColor() : Color.WHITE)
.build();

View File

@@ -1,5 +1,6 @@
/*
* Copyright (c) 2019, Tomas Slusny <slusnucky@gmail.com>
* Copyright (c) 2021, Jonathan Rousseau <https://github.com/JoRouss>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -25,20 +26,19 @@
package net.runelite.client.plugins.party.data;
import java.awt.Color;
import java.util.UUID;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import net.runelite.client.ui.overlay.components.PanelComponent;
import net.runelite.client.ui.overlay.worldmap.WorldMapPoint;
import net.runelite.client.ws.PartyMember;
@Setter
@Getter
@RequiredArgsConstructor
public class PartyData
{
private final UUID memberId;
private final String name;
private final PartyMember member;
private final WorldMapPoint worldMapPoint;
private final PanelComponent panel = new PanelComponent();
private final Color color;
@@ -47,4 +47,6 @@ public class PartyData
private int maxHitpoints;
private int prayer;
private int maxPrayer;
private String characterName = "";
private boolean showOverlay;
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) 2021, Jonathan Rousseau <https://github.com/JoRouss>
* 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.party.messages;
import lombok.Data;
import net.runelite.http.api.ws.messages.party.PartyMemberMessage;
@Data
public class CharacterNameUpdate extends PartyMemberMessage
{
private final String characterName;
}

View File

@@ -1,5 +1,6 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* Copyright (c) 2021, Jonathan Rousseau <https://github.com/JoRouss>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,10 +25,14 @@
*/
package net.runelite.client.ws;
import com.google.common.base.Charsets;
import com.google.common.hash.Hashing;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.Getter;
@@ -42,6 +47,7 @@ import net.runelite.client.eventbus.EventBus;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.events.PartyChanged;
import net.runelite.client.util.Text;
import net.runelite.client.events.PartyMemberAvatar;
import static net.runelite.client.util.Text.JAGEX_PRINTABLE_CHAR_MATCHER;
import net.runelite.http.api.ws.messages.party.Join;
import net.runelite.http.api.ws.messages.party.Part;
@@ -68,7 +74,10 @@ public class PartyService
private UUID localPartyId = UUID.randomUUID();
@Getter
private UUID partyId;
private UUID publicPartyId; // public party id, for advertising on discord, derived from the secret
@Getter
private UUID partyId; // secret party id
@Setter
private String username;
@@ -83,8 +92,14 @@ public class PartyService
eventBus.register(this);
}
public void changeParty(UUID newParty)
public void changeParty(@Nullable UUID newParty)
{
if (username == null)
{
log.warn("Tried to join a party with no username");
return;
}
if (wsClient.sessionExists())
{
wsClient.send(new Part());
@@ -93,6 +108,8 @@ public class PartyService
log.debug("Party change to {}", newParty);
members.clear();
partyId = newParty;
// The public party ID needs to be consistent across party members, but not a secret
publicPartyId = newParty != null ? UUID.nameUUIDFromBytes(Hashing.sha256().hashString(newParty.toString(), Charsets.UTF_8).asBytes()) : null;
if (partyId == null)
{
@@ -145,7 +162,7 @@ public class PartyService
}
}
@Subscribe
@Subscribe(priority = 1) // run prior to plugins so that the member is removed by the time the plugins see it.
public void onUserPart(final UserPart message)
{
members.removeIf(member -> member.getMemberId().equals(message.getMemberId()));
@@ -218,6 +235,17 @@ public class PartyService
return localPartyId.equals(partyId);
}
public void setPartyMemberAvatar(UUID memberID, BufferedImage image)
{
final PartyMember memberById = getMemberById(memberID);
if (memberById != null)
{
memberById.setAvatar(image);
eventBus.post(new PartyMemberAvatar(memberID, image));
}
}
private static String cleanUsername(String username)
{
String s = Text.removeTags(JAGEX_PRINTABLE_CHAR_MATCHER.retainFrom(username));

View File

@@ -191,7 +191,7 @@ public class WSClient extends WebSocketListener implements AutoCloseable
@Override
public void onFailure(WebSocket webSocket, Throwable t, Response response)
{
log.warn("Error in websocket {}:{}", response, t);
log.warn("Error in websocket: {}", response, t);
this.webSocket = null;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 299 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 710 B

View File

@@ -27,9 +27,7 @@ package net.runelite.client.plugins.discord;
import com.google.inject.Guice;
import com.google.inject.testing.fieldbinder.Bind;
import com.google.inject.testing.fieldbinder.BoundFieldModule;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.UUID;
import javax.inject.Inject;
import javax.inject.Named;
import net.runelite.api.Client;
@@ -83,7 +81,6 @@ public class DiscordStateTest
public void before()
{
Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this);
when(partyService.getLocalPartyId()).thenReturn(UUID.nameUUIDFromBytes("test".getBytes(StandardCharsets.UTF_8)));
}
@Test