Merge remote-tracking branch 'runelite/master'

This commit is contained in:
Owain van Brakel
2022-06-21 13:44:13 +02:00
30 changed files with 3082 additions and 289 deletions

View File

@@ -27,7 +27,8 @@
"-//Checkstyle//DTD SuppressionFilter Configuration 1.2//EN" "-//Checkstyle//DTD SuppressionFilter Configuration 1.2//EN"
"https://checkstyle.org/dtds/suppressions_1_2.dtd"> "https://checkstyle.org/dtds/suppressions_1_2.dtd">
<suppressions> <suppressions>
<suppress files="RuntimeTypeAdapterFactory\.java" checks="[a-zA-Z0-9]*"/> <suppress files="RuntimeTypeAdapterFactory\.java" checks=".*"/>
<suppress files="net[/\\]runelite[/\\]client[/\\]party[/\\]Party\.java" checks=".*"/>
<suppress files="[\\/]ScriptVarType[\./]" checks="[a-zA-Z0-9]*"/> <suppress files="[\\/]ScriptVarType[\./]" checks="[a-zA-Z0-9]*"/>
<suppress files="[\\/]RoomType[\./]" checks="[a-zA-Z0-9]*"/> <suppress files="[\\/]RoomType[\./]" checks="[a-zA-Z0-9]*"/>
<suppress files="[\\/]LayoutSolver[\./]" checks="[a-zA-Z0-9]*"/> <suppress files="[\\/]LayoutSolver[\./]" checks="[a-zA-Z0-9]*"/>

View File

@@ -63,6 +63,7 @@ dependencies {
exclude(group = "org.codehaus.mojo", module = "animal-sniffer-annotations") exclude(group = "org.codehaus.mojo", module = "animal-sniffer-annotations")
} }
implementation(group = "com.google.inject", name = "guice", version = "5.0.1") implementation(group = "com.google.inject", name = "guice", version = "5.0.1")
implementation(group = "com.google.protobuf", name = "protobuf-javalite", version = "3.21.1")
implementation(group = "com.jakewharton.rxrelay3", name = "rxrelay", version = "3.0.1") implementation(group = "com.jakewharton.rxrelay3", name = "rxrelay", version = "3.0.1")
implementation(group = "com.squareup.okhttp3", name = "okhttp", version = "4.9.1") implementation(group = "com.squareup.okhttp3", name = "okhttp", version = "4.9.1")
implementation(group = "io.reactivex.rxjava3", name = "rxjava", version = "3.1.2") implementation(group = "io.reactivex.rxjava3", name = "rxjava", version = "3.1.2")

View File

@@ -24,12 +24,17 @@
*/ */
package net.runelite.client.events; package net.runelite.client.events;
import java.util.UUID;
import lombok.Value; import lombok.Value;
@Value @Value
public class PartyChanged public class PartyChanged
{ {
/**
* The passphrase used to derive the party id
*/
private final String passphrase; private final String passphrase;
private final UUID partyId; /**
* The new party id, or null if no party
*/
private final Long partyId;
} }

View File

@@ -25,12 +25,11 @@
package net.runelite.client.events; package net.runelite.client.events;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.util.UUID;
import lombok.Value; import lombok.Value;
@Value @Value
public class PartyMemberAvatar public class PartyMemberAvatar
{ {
private final UUID memberId; private final long memberId;
private final BufferedImage image; private final BufferedImage image;
} }

File diff suppressed because it is too large Load Diff

View File

@@ -25,14 +25,12 @@
package net.runelite.client.party; package net.runelite.client.party;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.util.UUID;
import lombok.Data; import lombok.Data;
@Data @Data
public class PartyMember public class PartyMember
{ {
private final UUID memberId; private final long memberId;
private final String name;
private String displayName = "<unknown>"; private String displayName = "<unknown>";
private boolean loggedIn; private boolean loggedIn;
private BufferedImage avatar; private BufferedImage avatar;

View File

@@ -50,14 +50,11 @@ import net.runelite.client.eventbus.EventBus;
import net.runelite.client.eventbus.Subscribe; import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.events.PartyChanged; import net.runelite.client.events.PartyChanged;
import net.runelite.client.events.PartyMemberAvatar; import net.runelite.client.events.PartyMemberAvatar;
import net.runelite.client.party.messages.Join;
import net.runelite.client.party.messages.Part;
import net.runelite.client.party.messages.PartyChatMessage; import net.runelite.client.party.messages.PartyChatMessage;
import net.runelite.client.party.messages.PartyMessage; import net.runelite.client.party.messages.PartyMessage;
import net.runelite.client.party.messages.UserJoin; import net.runelite.client.party.events.UserJoin;
import net.runelite.client.party.messages.UserPart; import net.runelite.client.party.events.UserPart;
import net.runelite.client.party.messages.UserSync; import net.runelite.client.party.messages.UserSync;
import net.runelite.client.util.Text;
import static net.runelite.client.util.Text.JAGEX_PRINTABLE_CHAR_MATCHER; import static net.runelite.client.util.Text.JAGEX_PRINTABLE_CHAR_MATCHER;
@Slf4j @Slf4j
@@ -65,8 +62,6 @@ import static net.runelite.client.util.Text.JAGEX_PRINTABLE_CHAR_MATCHER;
public class PartyService public class PartyService
{ {
private static final int MAX_MESSAGE_LEN = 150; private static final int MAX_MESSAGE_LEN = 150;
private static final int MAX_USERNAME_LEN = 32; // same as Discord
private static final String USERNAME = "rluser-" + new Random().nextInt(Integer.MAX_VALUE);
private static final String ALPHABET = "bcdfghjklmnpqrstvwxyz"; private static final String ALPHABET = "bcdfghjklmnpqrstvwxyz";
private final Client client; private final Client client;
@@ -76,7 +71,8 @@ public class PartyService
private final List<PartyMember> members = new ArrayList<>(); private final List<PartyMember> members = new ArrayList<>();
@Getter @Getter
private UUID partyId; // secret party id private long partyId; // secret party id
private long memberId = randomMemberId();
@Getter @Getter
private String partyPassphrase; private String partyPassphrase;
@@ -146,7 +142,7 @@ public class PartyService
} }
String partyPassphrase = sb.toString(); String partyPassphrase = sb.toString();
log.debug("Generated party passpharse {}", partyPassphrase); log.debug("Generated party passphrase {}", partyPassphrase);
return partyPassphrase; return partyPassphrase;
} }
@@ -154,20 +150,21 @@ public class PartyService
{ {
if (wsClient.sessionExists()) if (wsClient.sessionExists())
{ {
wsClient.send(new Part()); wsClient.part();
memberId = randomMemberId(); // use a different member id between parties
} }
UUID id = passphrase != null ? passphraseToId(passphrase) : null; long id = passphrase != null ? passphraseToId(passphrase) : 0;
log.debug("Party change to {} (id {})", passphrase, id); log.debug("Party change to {} (id {})", passphrase, id);
members.clear(); members.clear();
partyId = id; partyId = id;
partyPassphrase = passphrase; partyPassphrase = passphrase;
if (partyId == null) if (passphrase == null)
{ {
wsClient.changeSession(null); wsClient.changeSession(null);
eventBus.post(new PartyChanged(partyPassphrase, partyId)); eventBus.post(new PartyChanged(partyPassphrase, null));
return; return;
} }
@@ -178,7 +175,7 @@ public class PartyService
} }
eventBus.post(new PartyChanged(partyPassphrase, partyId)); eventBus.post(new PartyChanged(partyPassphrase, partyId));
wsClient.send(new Join(partyId, USERNAME)); wsClient.join(partyId, memberId);
} }
public <T extends PartyMessage> void send(T message) public <T extends PartyMessage> void send(T message)
@@ -187,11 +184,10 @@ public class PartyService
{ {
log.debug("Reconnecting to server"); log.debug("Reconnecting to server");
PartyMember local = getLocalMember(); members.clear();
members.removeIf(m -> m != local);
wsClient.connect(); wsClient.connect();
wsClient.send(new Join(partyId, USERNAME)); wsClient.join(partyId, memberId);
} }
wsClient.send(message); wsClient.send(message);
@@ -200,7 +196,7 @@ public class PartyService
@Subscribe(priority = 1) // run prior to plugins so that the member is joined by the time the plugins see it. @Subscribe(priority = 1) // run prior to plugins so that the member is joined by the time the plugins see it.
public void onUserJoin(final UserJoin message) public void onUserJoin(final UserJoin message)
{ {
if (!partyId.equals(message.getPartyId())) if (partyId != message.getPartyId())
{ {
// This can happen when a session is resumed server side after the client party // This can happen when a session is resumed server side after the client party
// changes when disconnected. // changes when disconnected.
@@ -210,7 +206,7 @@ public class PartyService
PartyMember partyMember = getMemberById(message.getMemberId()); PartyMember partyMember = getMemberById(message.getMemberId());
if (partyMember == null) if (partyMember == null)
{ {
partyMember = new PartyMember(message.getMemberId(), cleanUsername(message.getName())); partyMember = new PartyMember(message.getMemberId());
members.add(partyMember); members.add(partyMember);
log.debug("User {} joins party, {} members", partyMember, members.size()); log.debug("User {} joins party, {} members", partyMember, members.size());
} }
@@ -219,8 +215,8 @@ public class PartyService
// Send info to other clients that this user successfully finished joining party // Send info to other clients that this user successfully finished joining party
if (localMember != null && localMember == partyMember) if (localMember != null && localMember == partyMember)
{ {
log.debug("Requesting sync");
final UserSync userSync = new UserSync(); final UserSync userSync = new UserSync();
userSync.setMemberId(message.getMemberId());
wsClient.send(userSync); wsClient.send(userSync);
} }
} }
@@ -228,7 +224,7 @@ public class PartyService
@Subscribe(priority = 1) // run prior to plugins so that the member is removed by the time the plugins see it. @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) public void onUserPart(final UserPart message)
{ {
if (members.removeIf(member -> member.getMemberId().equals(message.getMemberId()))) if (members.removeIf(member -> member.getMemberId() == message.getMemberId()))
{ {
log.debug("User {} leaves party, {} members", message.getMemberId(), members.size()); log.debug("User {} leaves party, {} members", message.getMemberId(), members.size());
} }
@@ -264,27 +260,14 @@ public class PartyService
public PartyMember getLocalMember() public PartyMember getLocalMember()
{ {
return getMemberByName(USERNAME); return getMemberById(memberId);
} }
public PartyMember getMemberById(final UUID id) public PartyMember getMemberById(final long id)
{ {
for (PartyMember member : members) for (PartyMember member : members)
{ {
if (id.equals(member.getMemberId())) if (id == member.getMemberId())
{
return member;
}
}
return null;
}
public PartyMember getMemberByName(final String name)
{
for (PartyMember member : members)
{
if (name.equals(member.getName()))
{ {
return member; return member;
} }
@@ -300,10 +283,10 @@ public class PartyService
public boolean isInParty() public boolean isInParty()
{ {
return partyId != null; return partyId != 0;
} }
public void setPartyMemberAvatar(UUID memberID, BufferedImage image) public void setPartyMemberAvatar(long memberID, BufferedImage image)
{ {
final PartyMember memberById = getMemberById(memberID); final PartyMember memberById = getMemberById(memberID);
@@ -314,22 +297,15 @@ public class PartyService
} }
} }
private static String cleanUsername(String username) private static long passphraseToId(String passphrase)
{ {
String s = Text.removeTags(JAGEX_PRINTABLE_CHAR_MATCHER.retainFrom(username)); return Hashing.sha256().hashBytes(
if (s.length() >= MAX_USERNAME_LEN) passphrase.getBytes(StandardCharsets.UTF_8)
{ ).asLong() & Long.MAX_VALUE;
s = s.substring(0, MAX_USERNAME_LEN);
}
return s;
} }
private static UUID passphraseToId(String passphrase) private static long randomMemberId()
{ {
return UUID.nameUUIDFromBytes( return new Random().nextLong() & Long.MAX_VALUE;
Hashing.sha256().hashBytes(
passphrase.getBytes(StandardCharsets.UTF_8)
).asBytes()
);
} }
} }

View File

@@ -26,6 +26,8 @@ package net.runelite.client.party;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.JsonParseException; import com.google.gson.JsonParseException;
import com.google.protobuf.InvalidProtocolBufferException;
import java.io.InputStreamReader;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.Objects; import java.util.Objects;
@@ -37,8 +39,9 @@ import lombok.Getter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.runelite.client.RuneLite; import net.runelite.client.RuneLite;
import net.runelite.client.eventbus.EventBus; import net.runelite.client.eventbus.EventBus;
import net.runelite.client.party.messages.Handshake; import net.runelite.client.party.events.UserJoin;
import net.runelite.client.party.messages.PartyMessage; import net.runelite.client.party.events.UserPart;
import net.runelite.client.party.messages.PartyMemberMessage;
import net.runelite.client.party.messages.WebsocketMessage; import net.runelite.client.party.messages.WebsocketMessage;
import okhttp3.HttpUrl; import okhttp3.HttpUrl;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
@@ -46,6 +49,7 @@ import okhttp3.Request;
import okhttp3.Response; import okhttp3.Response;
import okhttp3.WebSocket; import okhttp3.WebSocket;
import okhttp3.WebSocketListener; import okhttp3.WebSocketListener;
import okio.ByteString;
@Slf4j @Slf4j
@Singleton @Singleton
@@ -104,15 +108,13 @@ public class WSClient extends WebSocketListener implements AutoCloseable
} }
Request request = new Request.Builder() Request request = new Request.Builder()
.url(runeliteWs) .url(runeliteWs.newBuilder()
.addQueryParameter("sessionId", sessionId.toString())
.build())
.header("User-Agent", RuneLite.USER_AGENT) .header("User-Agent", RuneLite.USER_AGENT)
.build(); .build();
webSocket = okHttpClient.newWebSocket(request, this); webSocket = okHttpClient.newWebSocket(request, this);
Handshake handshake = new Handshake();
handshake.setSession(sessionId);
send(handshake);
} }
boolean isOpen() boolean isOpen()
@@ -136,7 +138,42 @@ public class WSClient extends WebSocketListener implements AutoCloseable
} }
} }
public void send(WebsocketMessage message) void join(long partyId, long memberId)
{
final Party.Join join = Party.Join.newBuilder()
.setPartyId(partyId)
.setMemberId(memberId)
.build();
final Party.C2S c2s = Party.C2S.newBuilder()
.setJoin(join)
.build();
send(c2s);
}
void part()
{
final Party.Part part = Party.Part.newBuilder()
.build();
final Party.C2S c2s = Party.C2S.newBuilder()
.setPart(part)
.build();
send(c2s);
}
void send(WebsocketMessage message)
{
log.debug("Sending: {}", message);
final String json = gson.toJson(message, WebsocketMessage.class);
final Party.Data data = Party.Data.newBuilder()
.setData(com.google.protobuf.ByteString.copyFromUtf8(json))
.build();
final Party.C2S c2s = Party.C2S.newBuilder()
.setData(data)
.build();
send(c2s);
}
private void send(Party.C2S message)
{ {
if (webSocket == null) if (webSocket == null)
{ {
@@ -144,9 +181,7 @@ public class WSClient extends WebSocketListener implements AutoCloseable
connect(); connect();
} }
final String json = gson.toJson(message, WebsocketMessage.class); webSocket.send(ByteString.of(message.toByteArray()));
webSocket.send(json);
log.debug("Sent: {}", json);
} }
@Override @Override
@@ -165,28 +200,55 @@ public class WSClient extends WebSocketListener implements AutoCloseable
} }
@Override @Override
public void onMessage(WebSocket webSocket, String text) public void onMessage(WebSocket webSocket, ByteString bytes)
{ {
final WebsocketMessage message; Party.S2C s2c;
try try
{ {
message = gson.fromJson(text, WebsocketMessage.class); s2c = Party.S2C.parseFrom(bytes.toByteArray());
} }
catch (JsonParseException e) catch (InvalidProtocolBufferException e)
{ {
log.debug("Failed to deserialize message", e); log.debug("Failed to deserialize message", e);
return; return;
} }
if (message.isParty() && !(message instanceof PartyMessage)) switch (s2c.getMsgCase())
{ {
// spoofed message? case JOIN:
return; Party.UserJoin join = s2c.getJoin();
} UserJoin userJoin = new UserJoin(join.getPartyId(), join.getMemberId());
log.debug("Got: {}", userJoin);
eventBus.post(userJoin);
break;
case PART:
Party.UserPart part = s2c.getPart();
UserPart userPart = new UserPart(part.getMemberId());
log.debug("Got: {}", userPart);
eventBus.post(userPart);
break;
case DATA:
Party.PartyData data = s2c.getData();
final WebsocketMessage message;
log.debug("Got: {}", text); try
eventBus.post(message); {
message = gson.fromJson(new InputStreamReader(data.getData().newInput()), WebsocketMessage.class);
}
catch (JsonParseException e)
{
log.debug("Failed to deserialize message", e);
return;
}
if (message instanceof PartyMemberMessage)
{
((PartyMemberMessage) message).setMemberId(data.getMemberId());
}
log.debug("Got: {}", message);
eventBus.post(message);
}
} }
@Override @Override

View File

@@ -29,29 +29,19 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import net.runelite.client.party.messages.Handshake;
import net.runelite.client.party.messages.Join;
import net.runelite.client.party.messages.Part;
import net.runelite.client.party.messages.PartyChatMessage; import net.runelite.client.party.messages.PartyChatMessage;
import net.runelite.client.party.messages.UserJoin;
import net.runelite.client.party.messages.UserPart;
import net.runelite.client.party.messages.UserSync; import net.runelite.client.party.messages.UserSync;
import net.runelite.client.party.messages.WebsocketMessage; import net.runelite.client.party.messages.WebsocketMessage;
import net.runelite.client.util.RuntimeTypeAdapterFactory; import net.runelite.client.util.RuntimeTypeAdapterFactory;
import net.runelite.http.api.RuneLiteAPI; import net.runelite.http.api.RuneLiteAPI;
public class WebsocketGsonFactory class WebsocketGsonFactory
{ {
private static final Collection<Class<? extends WebsocketMessage>> MESSAGES; private static final Collection<Class<? extends WebsocketMessage>> MESSAGES;
static static
{ {
final List<Class<? extends WebsocketMessage>> messages = new ArrayList<>(); final List<Class<? extends WebsocketMessage>> messages = new ArrayList<>();
messages.add(Handshake.class);
messages.add(Join.class);
messages.add(Part.class);
messages.add(UserJoin.class);
messages.add(UserPart.class);
messages.add(UserSync.class); messages.add(UserSync.class);
messages.add(PartyChatMessage.class); messages.add(PartyChatMessage.class);
MESSAGES = messages; MESSAGES = messages;

View File

@@ -22,17 +22,13 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package net.runelite.client.party.messages; package net.runelite.client.party.events;
import java.util.UUID;
import lombok.EqualsAndHashCode;
import lombok.Value; import lombok.Value;
@Value @Value
@EqualsAndHashCode(callSuper = true) public class UserJoin
public class UserJoin extends WebsocketMessage
{ {
private final UUID memberId; long partyId;
private final UUID partyId; long memberId;
private final String name;
} }

View File

@@ -22,15 +22,12 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package net.runelite.client.party.messages; package net.runelite.client.party.events;
import java.util.UUID;
import lombok.EqualsAndHashCode;
import lombok.Value; import lombok.Value;
@Value @Value
@EqualsAndHashCode(callSuper = true) public class UserPart
public class UserPart extends WebsocketMessage
{ {
private final UUID memberId; long memberId;
} }

View File

@@ -1,34 +0,0 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.client.party.messages;
import java.util.UUID;
import lombok.Data;
@Data
public class Handshake extends WebsocketMessage
{
private UUID session;
}

View File

@@ -1,37 +0,0 @@
/*
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.client.party.messages;
import java.util.UUID;
import lombok.EqualsAndHashCode;
import lombok.Value;
@Value
@EqualsAndHashCode(callSuper = true)
public class Join extends WebsocketMessage
{
private final UUID partyId;
private final String name;
}

View File

@@ -1,29 +0,0 @@
/*
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.client.party.messages;
public class Part extends WebsocketMessage
{
}

View File

@@ -1,6 +1,5 @@
package net.runelite.client.party.messages; package net.runelite.client.party.messages;
import java.util.UUID;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
@@ -8,5 +7,5 @@ import lombok.Setter;
@Setter @Setter
public abstract class PartyMemberMessage extends PartyMessage public abstract class PartyMemberMessage extends PartyMessage
{ {
private UUID memberId; private transient long memberId;
} }

View File

@@ -26,8 +26,4 @@ package net.runelite.client.party.messages;
public abstract class PartyMessage extends WebsocketMessage public abstract class PartyMessage extends WebsocketMessage
{ {
public PartyMessage()
{
_party = true;
}
} }

View File

@@ -24,11 +24,6 @@
*/ */
package net.runelite.client.party.messages; package net.runelite.client.party.messages;
import lombok.EqualsAndHashCode;
import lombok.Value;
@Value
@EqualsAndHashCode(callSuper = true)
public class UserSync extends PartyMemberMessage public class UserSync extends PartyMemberMessage
{ {
} }

View File

@@ -24,12 +24,6 @@
*/ */
package net.runelite.client.party.messages; package net.runelite.client.party.messages;
public class WebsocketMessage public abstract class WebsocketMessage
{ {
protected boolean _party;
public boolean isParty()
{
return _party;
}
} }

View File

@@ -280,7 +280,6 @@ public class DiscordPlugin extends Plugin
discordUser.discriminator, discordUser.discriminator,
discordUser.avatar discordUser.avatar
); );
userInfo.setMemberId(localMember.getMemberId());
partyService.send(userInfo); partyService.send(userInfo);
} }
} }

View File

@@ -198,7 +198,6 @@ public class DpsCounterPlugin extends Plugin
if (localMember != null) if (localMember != null)
{ {
final DpsUpdate dpsUpdate = new DpsUpdate(hit, isBoss); final DpsUpdate dpsUpdate = new DpsUpdate(hit, isBoss);
dpsUpdate.setMemberId(localMember.getMemberId());
partyService.send(dpsUpdate); partyService.send(dpsUpdate);
} }
@@ -235,7 +234,7 @@ public class DpsCounterPlugin extends Plugin
@Subscribe @Subscribe
public void onDpsUpdate(DpsUpdate dpsUpdate) public void onDpsUpdate(DpsUpdate dpsUpdate)
{ {
if (partyService.getLocalMember().getMemberId().equals(dpsUpdate.getMemberId())) if (partyService.getLocalMember().getMemberId() == dpsUpdate.getMemberId())
{ {
return; return;
} }

View File

@@ -33,7 +33,6 @@ import net.runelite.api.NPC;
import net.runelite.api.Player; import net.runelite.api.Player;
import net.runelite.api.Projectile; import net.runelite.api.Projectile;
import net.runelite.api.Renderable; import net.runelite.api.Renderable;
import net.runelite.api.Varbits;
import net.runelite.client.callback.Hooks; import net.runelite.client.callback.Hooks;
import net.runelite.client.config.ConfigManager; import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.Subscribe; import net.runelite.client.eventbus.Subscribe;
@@ -153,13 +152,6 @@ public class EntityHiderPlugin extends Plugin
return !(drawingUI ? hideLocalPlayer2D : hideLocalPlayer); return !(drawingUI ? hideLocalPlayer2D : hideLocalPlayer);
} }
final boolean inPvp = client.getVarbitValue(Varbits.PVP_SPEC_ORB) == 1;
if (inPvp)
{
// In PVP we only allow hiding everyone or no one
return !(drawingUI ? hideOthers2D : hideOthers);
}
if (hideAttackers && player.getInteracting() == local) if (hideAttackers && player.getInteracting() == local)
{ {
return false; // hide return false; // hide

View File

@@ -38,6 +38,8 @@ import javax.swing.border.Border;
import javax.swing.border.EmptyBorder; import javax.swing.border.EmptyBorder;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Getter; import lombok.Getter;
import net.runelite.client.party.PartyMember;
import net.runelite.client.party.PartyService;
import net.runelite.client.plugins.party.data.PartyData; import net.runelite.client.plugins.party.data.PartyData;
import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.DynamicGridLayout; import net.runelite.client.ui.DynamicGridLayout;
@@ -45,7 +47,6 @@ import net.runelite.client.ui.FontManager;
import net.runelite.client.ui.components.MouseDragEventForwarder; import net.runelite.client.ui.components.MouseDragEventForwarder;
import net.runelite.client.ui.components.ProgressBar; import net.runelite.client.ui.components.ProgressBar;
import net.runelite.client.util.ImageUtil; import net.runelite.client.util.ImageUtil;
import net.runelite.client.party.PartyMember;
class PartyMemberBox extends JPanel class PartyMemberBox extends JPanel
{ {
@@ -56,6 +57,7 @@ class PartyMemberBox extends JPanel
@Getter(AccessLevel.PACKAGE) @Getter(AccessLevel.PACKAGE)
private final PartyData memberPartyData; private final PartyData memberPartyData;
private final PartyService partyService;
private final ProgressBar hpBar = new ProgressBar(); private final ProgressBar hpBar = new ProgressBar();
private final ProgressBar prayerBar = new ProgressBar(); private final ProgressBar prayerBar = new ProgressBar();
@@ -67,10 +69,12 @@ class PartyMemberBox extends JPanel
private boolean avatarSet; private boolean avatarSet;
PartyMemberBox(final PartyConfig config, final JComponent panel, final PartyData memberPartyData) PartyMemberBox(final PartyConfig config, final JComponent panel, final PartyData memberPartyData,
final PartyService partyService)
{ {
this.config = config; this.config = config;
this.memberPartyData = memberPartyData; this.memberPartyData = memberPartyData;
this.partyService = partyService;
setLayout(new BorderLayout()); setLayout(new BorderLayout());
setBorder(new EmptyBorder(5, 0, 0, 0)); setBorder(new EmptyBorder(5, 0, 0, 0));
@@ -137,7 +141,7 @@ class PartyMemberBox extends JPanel
void update() void update()
{ {
final PartyMember member = memberPartyData.getMember(); final PartyMember member = partyService.getMemberById(memberPartyData.getMemberId());
// Avatar // Avatar
if (!avatarSet && member.getAvatar() != null) if (!avatarSet && member.getAvatar() != null)

View File

@@ -34,7 +34,6 @@ import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.StringSelection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.UUID;
import javax.swing.BoxLayout; import javax.swing.BoxLayout;
import javax.swing.JButton; import javax.swing.JButton;
import javax.swing.JComponent; import javax.swing.JComponent;
@@ -42,12 +41,12 @@ import javax.swing.JOptionPane;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.border.EmptyBorder; import javax.swing.border.EmptyBorder;
import net.runelite.client.callback.ClientThread; import net.runelite.client.callback.ClientThread;
import net.runelite.client.party.PartyService;
import net.runelite.client.plugins.party.data.PartyData; import net.runelite.client.plugins.party.data.PartyData;
import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.PluginPanel; import net.runelite.client.ui.PluginPanel;
import net.runelite.client.ui.components.DragAndDropReorderPane; import net.runelite.client.ui.components.DragAndDropReorderPane;
import net.runelite.client.ui.components.PluginErrorPanel; import net.runelite.client.ui.components.PluginErrorPanel;
import net.runelite.client.party.PartyService;
class PartyPanel extends PluginPanel class PartyPanel extends PluginPanel
{ {
@@ -58,7 +57,7 @@ class PartyPanel extends PluginPanel
private final PartyService party; private final PartyService party;
private final PartyConfig config; private final PartyConfig config;
private final Map<UUID, PartyMemberBox> memberBoxes = new HashMap<>(); private final Map<Long, PartyMemberBox> memberBoxes = new HashMap<>();
private final JButton startButton = new JButton(); private final JButton startButton = new JButton();
private final JButton joinPartyButton = new JButton(); private final JButton joinPartyButton = new JButton();
@@ -216,10 +215,10 @@ class PartyPanel extends PluginPanel
void addMember(PartyData partyData) void addMember(PartyData partyData)
{ {
if (!memberBoxes.containsKey(partyData.getMember().getMemberId())) if (!memberBoxes.containsKey(partyData.getMemberId()))
{ {
PartyMemberBox partyMemberBox = new PartyMemberBox(config, memberBoxPanel, partyData); PartyMemberBox partyMemberBox = new PartyMemberBox(config, memberBoxPanel, partyData, party);
memberBoxes.put(partyData.getMember().getMemberId(), partyMemberBox); memberBoxes.put(partyData.getMemberId(), partyMemberBox);
memberBoxPanel.add(partyMemberBox); memberBoxPanel.add(partyMemberBox);
memberBoxPanel.revalidate(); memberBoxPanel.revalidate();
} }
@@ -234,7 +233,7 @@ class PartyPanel extends PluginPanel
updateParty(); updateParty();
} }
void removeMember(UUID memberId) void removeMember(long memberId)
{ {
final PartyMemberBox memberBox = memberBoxes.remove(memberId); final PartyMemberBox memberBox = memberBoxes.remove(memberId);
@@ -247,7 +246,7 @@ class PartyPanel extends PluginPanel
updateParty(); updateParty();
} }
void updateMember(UUID userId) void updateMember(long userId)
{ {
final PartyMemberBox memberBox = memberBoxes.get(userId); final PartyMemberBox memberBox = memberBoxes.get(userId);

View File

@@ -36,7 +36,6 @@ import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
@@ -70,8 +69,8 @@ import net.runelite.client.input.KeyManager;
import net.runelite.client.party.PartyMember; import net.runelite.client.party.PartyMember;
import net.runelite.client.party.PartyService; import net.runelite.client.party.PartyService;
import net.runelite.client.party.WSClient; import net.runelite.client.party.WSClient;
import net.runelite.client.party.messages.UserJoin; import net.runelite.client.party.events.UserJoin;
import net.runelite.client.party.messages.UserPart; import net.runelite.client.party.events.UserPart;
import net.runelite.client.party.messages.UserSync; import net.runelite.client.party.messages.UserSync;
import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.plugins.PluginDescriptor;
@@ -138,7 +137,7 @@ public class PartyPlugin extends Plugin
boolean developerMode; boolean developerMode;
@Getter @Getter
private final Map<UUID, PartyData> partyDataMap = Collections.synchronizedMap(new HashMap<>()); private final Map<Long, PartyData> partyDataMap = Collections.synchronizedMap(new HashMap<>());
@Getter @Getter
private final List<PartyTilePingData> pendingTilePings = Collections.synchronizedList(new ArrayList<>()); private final List<PartyTilePingData> pendingTilePings = Collections.synchronizedList(new ArrayList<>());
@@ -295,7 +294,6 @@ public class PartyPlugin extends Plugin
event.consume(); event.consume();
final TilePing tilePing = new TilePing(selectedSceneTile.getWorldLocation()); final TilePing tilePing = new TilePing(selectedSceneTile.getWorldLocation());
tilePing.setMemberId(party.getLocalMember().getMemberId());
party.send(tilePing); party.send(tilePing);
} }
@@ -355,7 +353,6 @@ public class PartyPlugin extends Plugin
lastLocation = location; lastLocation = location;
final LocationUpdate locationUpdate = new LocationUpdate(location); final LocationUpdate locationUpdate = new LocationUpdate(location);
locationUpdate.setMemberId(localMember.getMemberId());
party.send(locationUpdate); party.send(locationUpdate);
} }
@@ -371,7 +368,6 @@ public class PartyPlugin extends Plugin
{ {
// Request sync // Request sync
final UserSync userSync = new UserSync(); final UserSync userSync = new UserSync();
userSync.setMemberId(party.getLocalMember().getMemberId());
party.send(userSync); party.send(userSync);
} }
} }
@@ -380,28 +376,29 @@ public class PartyPlugin extends Plugin
public void onCharacterNameUpdate(final CharacterNameUpdate event) public void onCharacterNameUpdate(final CharacterNameUpdate event)
{ {
final PartyData partyData = getPartyData(event.getMemberId()); final PartyData partyData = getPartyData(event.getMemberId());
if (partyData == null) if (partyData == null)
{ {
return; return;
} }
final String name = Text.removeTags(Text.toJagexName(event.getCharacterName())); final PartyMember member = party.getMemberById(event.getMemberId());
final PartyMember member = partyData.getMember(); if (member != null)
if (!name.isEmpty())
{ {
member.setDisplayName(name); final String name = Text.removeTags(Text.toJagexName(event.getCharacterName()));
member.setLoggedIn(true); if (!name.isEmpty())
partyData.setColor(ColorUtil.fromObject(name)); {
} member.setDisplayName(name);
else member.setLoggedIn(true);
{ partyData.setColor(ColorUtil.fromObject(name));
member.setLoggedIn(false); }
partyData.setColor(Color.WHITE); else
{
member.setLoggedIn(false);
partyData.setColor(Color.WHITE);
}
} }
SwingUtilities.invokeLater(() -> panel.updateMember(member.getMemberId())); SwingUtilities.invokeLater(() -> panel.updateMember(event.getMemberId()));
} }
@Subscribe @Subscribe
@@ -425,7 +422,7 @@ public class PartyPlugin extends Plugin
partyData.setMaxPrayer(event.getMax()); partyData.setMaxPrayer(event.getMax());
} }
SwingUtilities.invokeLater(() -> panel.updateMember(partyData.getMember().getMemberId())); SwingUtilities.invokeLater(() -> panel.updateMember(partyData.getMemberId()));
} }
@Subscribe @Subscribe
@@ -471,21 +468,18 @@ public class PartyPlugin extends Plugin
if (forceSend || currentHealth != lastHp) if (forceSend || currentHealth != lastHp)
{ {
final SkillUpdate update = new SkillUpdate(Skill.HITPOINTS, currentHealth, realHealth); final SkillUpdate update = new SkillUpdate(Skill.HITPOINTS, currentHealth, realHealth);
update.setMemberId(localMember.getMemberId());
party.send(update); party.send(update);
} }
if (forceSend || currentPrayer != lastPray) if (forceSend || currentPrayer != lastPray)
{ {
final SkillUpdate update = new SkillUpdate(Skill.PRAYER, currentPrayer, realPrayer); final SkillUpdate update = new SkillUpdate(Skill.PRAYER, currentPrayer, realPrayer);
update.setMemberId(localMember.getMemberId());
party.send(update); party.send(update);
} }
if (forceSend || !characterName.equals(lastCharacterName)) if (forceSend || !characterName.equals(lastCharacterName))
{ {
final CharacterNameUpdate update = new CharacterNameUpdate(characterName); final CharacterNameUpdate update = new CharacterNameUpdate(characterName);
update.setMemberId(localMember.getMemberId());
party.send(update); party.send(update);
} }
} }
@@ -536,7 +530,7 @@ public class PartyPlugin extends Plugin
chatMessageManager.queue(QueuedMessage.builder().type(ChatMessageType.GAMEMESSAGE).value("Local ID " + party.getLocalMember().getMemberId()).build()); chatMessageManager.queue(QueuedMessage.builder().type(ChatMessageType.GAMEMESSAGE).value("Local ID " + party.getLocalMember().getMemberId()).build());
for (PartyMember partyMember : party.getMembers()) for (PartyMember partyMember : party.getMembers())
{ {
chatMessageManager.queue(QueuedMessage.builder().type(ChatMessageType.GAMEMESSAGE).value("Member " + partyMember.getName() + " " + partyMember.getDisplayName() + " " + partyMember.getMemberId()).build()); chatMessageManager.queue(QueuedMessage.builder().type(ChatMessageType.GAMEMESSAGE).value("Member " + partyMember.getDisplayName() + " " + partyMember.getMemberId()).build());
} }
} }
@@ -547,7 +541,7 @@ public class PartyPlugin extends Plugin
} }
@Nullable @Nullable
PartyData getPartyData(final UUID uuid) PartyData getPartyData(final long uuid)
{ {
final PartyMember memberById = party.getMemberById(uuid); final PartyMember memberById = party.getMemberById(uuid);
@@ -573,7 +567,7 @@ public class PartyPlugin extends Plugin
worldMapManager.add(worldMapPoint); worldMapManager.add(worldMapPoint);
} }
PartyData partyData = new PartyData(memberById, worldMapPoint); PartyData partyData = new PartyData(uuid, worldMapPoint);
SwingUtilities.invokeLater(() -> panel.addMember(partyData)); SwingUtilities.invokeLater(() -> panel.addMember(partyData));
return partyData; return partyData;

View File

@@ -24,7 +24,6 @@
*/ */
package net.runelite.client.plugins.party; package net.runelite.client.plugins.party;
import java.util.UUID;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import net.runelite.client.plugins.party.data.PartyData; import net.runelite.client.plugins.party.data.PartyData;
@@ -36,5 +35,5 @@ public interface PartyPluginService
* @return party data for member * @return party data for member
*/ */
@Nullable @Nullable
PartyData getPartyData(UUID memberId); PartyData getPartyData(long memberId);
} }

View File

@@ -24,7 +24,6 @@
*/ */
package net.runelite.client.plugins.party; package net.runelite.client.plugins.party;
import java.util.UUID;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import net.runelite.client.plugins.party.data.PartyData; import net.runelite.client.plugins.party.data.PartyData;
@@ -42,7 +41,7 @@ public class PartyPluginServiceImpl implements PartyPluginService
} }
@Override @Override
public PartyData getPartyData(UUID memberId) public PartyData getPartyData(long memberId)
{ {
return plugin.getPartyData(memberId); return plugin.getPartyData(memberId);
} }

View File

@@ -31,14 +31,13 @@ import lombok.RequiredArgsConstructor;
import lombok.Setter; import lombok.Setter;
import net.runelite.client.ui.overlay.components.PanelComponent; import net.runelite.client.ui.overlay.components.PanelComponent;
import net.runelite.client.ui.overlay.worldmap.WorldMapPoint; import net.runelite.client.ui.overlay.worldmap.WorldMapPoint;
import net.runelite.client.party.PartyMember;
@Setter @Setter
@Getter @Getter
@RequiredArgsConstructor @RequiredArgsConstructor
public class PartyData public class PartyData
{ {
private final PartyMember member; private final long memberId;
private final WorldMapPoint worldMapPoint; private final WorldMapPoint worldMapPoint;
private final PanelComponent panel = new PanelComponent(); private final PanelComponent panel = new PanelComponent();
private Color color = Color.WHITE; private Color color = Color.WHITE;

View File

@@ -24,14 +24,27 @@
*/ */
package net.runelite.client.plugins.party.messages; package net.runelite.client.plugins.party.messages;
import lombok.EqualsAndHashCode; import lombok.ToString;
import lombok.Value;
import net.runelite.api.coords.WorldPoint; import net.runelite.api.coords.WorldPoint;
import net.runelite.client.party.messages.PartyMemberMessage; import net.runelite.client.party.messages.PartyMemberMessage;
@Value @ToString(onlyExplicitlyIncluded = true)
@EqualsAndHashCode(callSuper = true)
public class LocationUpdate extends PartyMemberMessage public class LocationUpdate extends PartyMemberMessage
{ {
private final WorldPoint worldPoint; private final int c;
public LocationUpdate(WorldPoint worldPoint)
{
c = (worldPoint.getPlane() << 28) | (worldPoint.getX() << 14) | (worldPoint.getY());
}
@ToString.Include
public WorldPoint getWorldPoint()
{
return new WorldPoint(
(c >> 14) & 0x3fff,
c & 0x3fff,
(c >> 28) & 3
);
}
} }

View File

@@ -314,7 +314,6 @@ public class SpecialCounterPlugin extends Plugin
if (!party.getMembers().isEmpty()) if (!party.getMembers().isEmpty())
{ {
final SpecialCounterUpdate specialCounterUpdate = new SpecialCounterUpdate(npcIndex, specialWeapon, hit, client.getWorld(), localPlayerId); final SpecialCounterUpdate specialCounterUpdate = new SpecialCounterUpdate(npcIndex, specialWeapon, hit, client.getWorld(), localPlayerId);
specialCounterUpdate.setMemberId(party.getLocalMember().getMemberId());
party.send(specialCounterUpdate); party.send(specialCounterUpdate);
} }
@@ -341,7 +340,7 @@ public class SpecialCounterPlugin extends Plugin
@Subscribe @Subscribe
public void onSpecialCounterUpdate(SpecialCounterUpdate event) public void onSpecialCounterUpdate(SpecialCounterUpdate event)
{ {
if (party.getLocalMember().getMemberId().equals(event.getMemberId()) if (party.getLocalMember().getMemberId() == event.getMemberId()
|| event.getWorld() != client.getWorld()) || event.getWorld() != client.getWorld())
{ {
return; return;

View File

@@ -17,7 +17,7 @@ runelite.imgur.client.id=30d71e5f6860809
runelite.api.base=https://api.runelite.net/runelite-@project.version@ runelite.api.base=https://api.runelite.net/runelite-@project.version@
runelite.session=https://session.openosrs.dev runelite.session=https://session.openosrs.dev
runelite.static.base=https://static.runelite.net runelite.static.base=https://static.runelite.net
runelite.ws=https://api.runelite.net/ws runelite.ws=https://api.runelite.net/ws2
runelite.config=https://static.runelite.net/config.json runelite.config=https://static.runelite.net/config.json
runelite.osrstwitter.link=https://twitter.com/OldSchoolRS runelite.osrstwitter.link=https://twitter.com/OldSchoolRS
runelite.oauth.redirect=https://runelite.net/logged-in runelite.oauth.redirect=https://runelite.net/logged-in