Merge remote-tracking branch 'runelite/master'

This commit is contained in:
Owain van Brakel
2022-06-12 01:48:45 +02:00
65 changed files with 1985 additions and 513 deletions

View File

@@ -360,6 +360,7 @@ public class RuneLite
// Client size must be set prior to init
applet.setSize(Constants.GAME_FIXED_SIZE);
System.setProperty("jagex.disableBouncyCastle", "true");
// Change user.home so the client places jagexcache in the .runelite directory
String oldHome = System.setProperty("user.home", RUNELITE_DIR.getAbsolutePath());
try

View File

@@ -57,12 +57,12 @@ public class AccountClient
this.apiBase = apiBase;
}
public OAuthResponse login() throws IOException
public OAuthResponse login(int port) throws IOException
{
HttpUrl url = apiBase.newBuilder()
.addPathSegment("account")
.addPathSegment("login")
.addQueryParameter("uuid", uuid.toString())
.addQueryParameter("port", Integer.toString(port))
.build();
log.debug("Built URI: {}", url);

View File

@@ -35,5 +35,5 @@ public class AccountSession
{
private final UUID uuid;
private final Instant created;
private String username;
private final String username;
}

View File

@@ -25,12 +25,14 @@
package net.runelite.client.account;
import com.google.gson.Gson;
import com.sun.net.httpserver.HttpServer;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.time.Instant;
@@ -42,13 +44,11 @@ import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.events.SessionClose;
import net.runelite.client.events.SessionOpen;
import net.runelite.client.util.LinkBrowser;
import net.runelite.client.ws.WSClient;
import net.runelite.http.api.account.OAuthResponse;
import net.runelite.http.api.ws.messages.LoginResponse;
import okhttp3.HttpUrl;
@Singleton
@Slf4j
@@ -59,26 +59,29 @@ public class SessionManager
private final EventBus eventBus;
private final ConfigManager configManager;
private final WSClient wsClient;
private final File sessionFile;
private final AccountClient accountClient;
private final Gson gson;
private final String oauthRedirect;
private HttpServer server;
@Inject
private SessionManager(
@Named("sessionfile") File sessionfile,
ConfigManager configManager,
EventBus eventBus,
WSClient wsClient,
AccountClient accountClient,
Gson gson)
Gson gson,
@Named("runelite.oauth.redirect") String oauthRedirect
)
{
this.configManager = configManager;
this.eventBus = eventBus;
this.wsClient = wsClient;
this.sessionFile = sessionfile;
this.accountClient = accountClient;
this.gson = gson;
this.oauthRedirect = oauthRedirect;
eventBus.register(this);
}
@@ -113,7 +116,7 @@ public class SessionManager
return;
}
openSession(session, false);
openSession(session);
}
private void saveSession()
@@ -141,19 +144,12 @@ public class SessionManager
}
/**
* Set the given session as the active session and open a socket to the
* server with the given session
* Set the given session as the active session
*
* @param session session
*/
private void openSession(AccountSession session, boolean openSocket)
private void openSession(AccountSession session)
{
// Change session on the websocket
if (openSocket)
{
wsClient.changeSession(session.getUuid());
}
accountSession = session;
if (session.getUsername() != null)
@@ -170,8 +166,6 @@ public class SessionManager
private void closeSession()
{
wsClient.changeSession(null);
if (accountSession == null)
{
return;
@@ -199,49 +193,85 @@ public class SessionManager
public void login()
{
// If a session is already open, use that id. Otherwise generate a new id.
UUID uuid = wsClient.getSessionId() != null ? wsClient.getSessionId() : UUID.randomUUID();
accountClient.setUuid(uuid);
if (server == null)
{
try
{
startServer();
}
catch (IOException ex)
{
log.error("Unable to start http server", ex);
return;
}
}
final OAuthResponse login;
try
{
login = accountClient.login();
login = accountClient.login(server.getAddress().getPort());
}
catch (IOException ex)
{
log.warn("Unable to get oauth url", ex);
log.error("Unable to get oauth url", ex);
return;
}
// Create new session
openSession(new AccountSession(login.getUid(), Instant.now()), true);
// Navigate to login link
LinkBrowser.browse(login.getOauthUrl());
}
@Subscribe
public void onLoginResponse(LoginResponse loginResponse)
{
log.debug("Now signed in as {}", loginResponse.getUsername());
AccountSession session = getAccountSession();
session.setUsername(loginResponse.getUsername());
// Open session, again, now that we have a username
// This triggers onSessionOpen
// The socket is already opened here anyway so we pass true for openSocket
openSession(session, true);
// Save session to disk
saveSession();
}
public void logout()
{
closeSession();
deleteSession();
}
private void startServer() throws IOException
{
server = HttpServer.create(new InetSocketAddress("localhost", 0), 1);
server.createContext("/oauth", req ->
{
try
{
final HttpUrl url = HttpUrl.get("http://localhost" + req.getRequestURI());
final String username = url.queryParameter("username");
final UUID sessionId = UUID.fromString(url.queryParameter("sessionId"));
log.debug("Now signed in as {}", username);
// open the session, which triggers the sessonopen event
AccountSession session = new AccountSession(sessionId, Instant.now(), username);
openSession(session);
// Save session to disk
saveSession();
req.getResponseHeaders().set("Location", oauthRedirect);
req.sendResponseHeaders(302, 0);
}
catch (Exception e)
{
log.warn("failure serving oauth response", e);
req.sendResponseHeaders(400, 0);
req.getResponseBody().write(e.getMessage().getBytes(StandardCharsets.UTF_8));
}
finally
{
req.close();
stopServer();
}
});
log.debug("Starting server {}", server);
server.start();
}
private void stopServer()
{
log.debug("Stopping server {}", server);
server.stop(0);
server = null;
}
}

View File

@@ -36,6 +36,7 @@ import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.image.BufferedImage;
import java.awt.image.VolatileImage;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -112,6 +113,14 @@ public class Hooks implements Callbacks
private static MainBufferProvider lastMainBufferProvider;
private static Graphics2D lastGraphics;
@FunctionalInterface
public interface RenderableDrawListener
{
boolean draw(Renderable renderable, boolean ui);
}
private final List<RenderableDrawListener> renderableDrawListeners = new ArrayList<>();
/**
* Get the Graphics2D for the MainBufferProvider image
* This caches the Graphics2D instance so it can be reused
@@ -219,7 +228,7 @@ public class Hooks implements Callbacks
}
catch (Exception ex)
{
log.warn("error during main loop tasks", ex);
log.error("error during main loop tasks", ex);
}
}
@@ -344,7 +353,7 @@ public class Hooks implements Callbacks
}
catch (Exception ex)
{
log.warn("Error during overlay rendering", ex);
log.error("Error during overlay rendering", ex);
}
notifier.processFlash(graphics2d);
@@ -439,7 +448,7 @@ public class Hooks implements Callbacks
}
catch (Exception ex)
{
log.warn("Error during overlay rendering", ex);
log.error("Error during overlay rendering", ex);
}
}
@@ -455,7 +464,7 @@ public class Hooks implements Callbacks
}
catch (Exception ex)
{
log.warn("Error during overlay rendering", ex);
log.error("Error during overlay rendering", ex);
}
}
@@ -507,7 +516,7 @@ public class Hooks implements Callbacks
}
catch (Exception ex)
{
log.warn("Error during overlay rendering", ex);
log.error("Error during overlay rendering", ex);
}
}
@@ -523,7 +532,7 @@ public class Hooks implements Callbacks
}
catch (Exception ex)
{
log.warn("Error during overlay rendering", ex);
log.error("Error during overlay rendering", ex);
}
}
@@ -549,7 +558,16 @@ public class Hooks implements Callbacks
eventBus.post(fakeXpDrop);
}
public void registerRenderableDrawListener(RenderableDrawListener listener)
{
renderableDrawListeners.add(listener);
}
public void unregisterRenderableDrawListener(RenderableDrawListener listener)
{
renderableDrawListeners.remove(listener);
}
public static void clearColorBuffer(int x, int y, int width, int height, int color)
{
BufferProvider bp = client.getBufferProvider();
@@ -588,4 +606,24 @@ public class Hooks implements Callbacks
client.getCallbacks().post(event);
return event.isConsumed();
}
@Override
public boolean draw(Renderable renderable, boolean drawingUi)
{
try
{
for (RenderableDrawListener renderableDrawListener : renderableDrawListeners)
{
if (!renderableDrawListener.draw(renderable, drawingUi))
{
return false;
}
}
}
catch (Exception ex)
{
log.error("exception from renderable draw listener", ex);
}
return true;
}
}

View File

@@ -22,7 +22,7 @@
* (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.ws;
package net.runelite.client.party;
import java.awt.image.BufferedImage;
import java.util.UUID;

View File

@@ -23,7 +23,7 @@
* (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.ws;
package net.runelite.client.party;
import com.google.common.base.CharMatcher;
import com.google.common.hash.Hashing;
@@ -44,22 +44,21 @@ import net.runelite.api.ChatMessageType;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.ItemComposition;
import net.runelite.client.account.AccountSession;
import net.runelite.client.account.SessionManager;
import net.runelite.client.chat.ChatMessageManager;
import net.runelite.client.chat.QueuedMessage;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.events.PartyChanged;
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.PartyMessage;
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.util.Text;
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;
import net.runelite.http.api.ws.messages.party.PartyChatMessage;
import net.runelite.http.api.ws.messages.party.UserJoin;
import net.runelite.http.api.ws.messages.party.UserPart;
import net.runelite.http.api.ws.messages.party.UserSync;
@Slf4j
@Singleton
@@ -72,7 +71,6 @@ public class PartyService
private final Client client;
private final WSClient wsClient;
private final SessionManager sessionManager;
private final EventBus eventBus;
private final ChatMessageManager chat;
private final List<PartyMember> members = new ArrayList<>();
@@ -83,11 +81,10 @@ public class PartyService
private String partyPassphrase;
@Inject
private PartyService(final Client client, final WSClient wsClient, final SessionManager sessionManager, final EventBus eventBus, final ChatMessageManager chat)
private PartyService(final Client client, final WSClient wsClient, final EventBus eventBus, final ChatMessageManager chat)
{
this.client = client;
this.wsClient = wsClient;
this.sessionManager = sessionManager;
this.eventBus = eventBus;
this.chat = chat;
eventBus.register(this);
@@ -169,12 +166,7 @@ public class PartyService
if (partyId == null)
{
// close the websocket if the session id isn't for an account
if (sessionManager.getAccountSession() == null)
{
wsClient.changeSession(null);
}
wsClient.changeSession(null);
eventBus.post(new PartyChanged(partyPassphrase, partyId));
return;
}
@@ -182,16 +174,29 @@ public class PartyService
// If there isn't already a session open, open one
if (!wsClient.sessionExists())
{
AccountSession accountSession = sessionManager.getAccountSession();
// Use the existing account session, if it exists, otherwise generate a new session id
UUID uuid = accountSession != null ? accountSession.getUuid() : UUID.randomUUID();
wsClient.changeSession(uuid);
wsClient.changeSession(UUID.randomUUID());
}
eventBus.post(new PartyChanged(partyPassphrase, partyId));
wsClient.send(new Join(partyId, USERNAME));
}
public <T extends PartyMessage> void send(T message)
{
if (!wsClient.isOpen())
{
log.debug("Reconnecting to server");
PartyMember local = getLocalMember();
members.removeIf(m -> m != local);
wsClient.connect();
wsClient.send(new Join(partyId, USERNAME));
}
wsClient.send(message);
}
@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)
{
@@ -202,13 +207,17 @@ public class PartyService
return;
}
final PartyMember partyMember = new PartyMember(message.getMemberId(), cleanUsername(message.getName()));
members.add(partyMember);
PartyMember partyMember = getMemberById(message.getMemberId());
if (partyMember == null)
{
partyMember = new PartyMember(message.getMemberId(), cleanUsername(message.getName()));
members.add(partyMember);
log.debug("User {} joins party, {} members", partyMember, members.size());
}
final PartyMember localMember = getLocalMember();
// Send info to other clients that this user successfully finished joining party
if (localMember != null && message.getMemberId().equals(localMember.getMemberId()))
if (localMember != null && localMember == partyMember)
{
final UserSync userSync = new UserSync();
userSync.setMemberId(message.getMemberId());
@@ -219,7 +228,10 @@ public class PartyService
@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()));
if (members.removeIf(member -> member.getMemberId().equals(message.getMemberId())))
{
log.debug("User {} leaves party, {} members", message.getMemberId(), members.size());
}
}
@Subscribe

View File

@@ -22,7 +22,7 @@
* (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.ws;
package net.runelite.client.party;
import com.google.gson.Gson;
import com.google.gson.JsonParseException;
@@ -37,10 +37,9 @@ import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.runelite.client.RuneLite;
import net.runelite.client.eventbus.EventBus;
import net.runelite.http.api.ws.WebsocketGsonFactory;
import net.runelite.http.api.ws.WebsocketMessage;
import net.runelite.http.api.ws.messages.Handshake;
import net.runelite.http.api.ws.messages.party.PartyMessage;
import net.runelite.client.party.messages.Handshake;
import net.runelite.client.party.messages.PartyMessage;
import net.runelite.client.party.messages.WebsocketMessage;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
@@ -97,7 +96,7 @@ public class WSClient extends WebSocketListener implements AutoCloseable
}
}
private void connect()
void connect()
{
if (sessionId == null)
{
@@ -116,6 +115,11 @@ public class WSClient extends WebSocketListener implements AutoCloseable
send(handshake);
}
boolean isOpen()
{
return webSocket != null;
}
public void registerMessage(final Class<? extends WebsocketMessage> message)
{
if (messages.add(message))

View File

@@ -0,0 +1,88 @@
/*
* 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;
import com.google.gson.Gson;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
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.UserJoin;
import net.runelite.client.party.messages.UserPart;
import net.runelite.client.party.messages.UserSync;
import net.runelite.client.party.messages.WebsocketMessage;
import net.runelite.client.util.RuntimeTypeAdapterFactory;
import net.runelite.http.api.RuneLiteAPI;
public class WebsocketGsonFactory
{
private static final Collection<Class<? extends WebsocketMessage>> MESSAGES;
static
{
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(PartyChatMessage.class);
MESSAGES = messages;
}
public static RuntimeTypeAdapterFactory<WebsocketMessage> factory(final Collection<Class<? extends WebsocketMessage>> messages)
{
final RuntimeTypeAdapterFactory<WebsocketMessage> factory = RuntimeTypeAdapterFactory.of(WebsocketMessage.class);
for (Class<? extends WebsocketMessage> message : MESSAGES)
{
factory.registerSubtype(message);
}
for (Class<? extends WebsocketMessage> message : messages)
{
factory.registerSubtype(message);
}
return factory;
}
public static Gson build(final RuntimeTypeAdapterFactory<WebsocketMessage> factory)
{
return RuneLiteAPI.GSON.newBuilder()
.registerTypeAdapterFactory(factory)
.create();
}
public static Gson build()
{
return build(factory(Collections.emptyList()));
}
}

View File

@@ -0,0 +1,34 @@
/*
* 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

@@ -0,0 +1,37 @@
/*
* 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

@@ -0,0 +1,29 @@
/*
* 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

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2019, 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 lombok.Value;
@Value
public class PartyChatMessage extends PartyMemberMessage
{
private final String value;
}

View File

@@ -0,0 +1,12 @@
package net.runelite.client.party.messages;
import java.util.UUID;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public abstract class PartyMemberMessage extends PartyMessage
{
private UUID memberId;
}

View File

@@ -0,0 +1,33 @@
/*
* 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 abstract class PartyMessage extends WebsocketMessage
{
public PartyMessage()
{
_party = true;
}
}

View File

@@ -0,0 +1,38 @@
/*
* 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 UserJoin extends WebsocketMessage
{
private final UUID memberId;
private final UUID partyId;
private final String name;
}

View File

@@ -0,0 +1,36 @@
/*
* 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 UserPart extends WebsocketMessage
{
private final UUID memberId;
}

View File

@@ -0,0 +1,34 @@
/*
* 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 lombok.EqualsAndHashCode;
import lombok.Value;
@Value
@EqualsAndHashCode(callSuper = true)
public class UserSync extends PartyMemberMessage
{
}

View File

@@ -0,0 +1,35 @@
/*
* 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;
public class WebsocketMessage
{
protected boolean _party;
public boolean isParty()
{
return _party;
}
}

View File

@@ -24,14 +24,10 @@
*/
package net.runelite.client.plugins.cannon;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Provides;
import java.awt.Color;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.inject.Inject;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
@@ -40,25 +36,22 @@ import net.runelite.api.ChatMessageType;
import net.runelite.api.Client;
import net.runelite.api.GameObject;
import net.runelite.api.GameState;
import net.runelite.api.GraphicID;
import net.runelite.api.InventoryID;
import net.runelite.api.Item;
import net.runelite.api.ItemContainer;
import net.runelite.api.ItemID;
import net.runelite.api.MenuAction;
import net.runelite.api.ObjectID;
import net.runelite.api.Player;
import net.runelite.api.Projectile;
import net.runelite.api.VarPlayer;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.coords.WorldArea;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.events.ChatMessage;
import net.runelite.api.events.GameObjectSpawned;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.GameTick;
import net.runelite.api.events.ItemContainerChanged;
import net.runelite.api.events.MenuOptionClicked;
import net.runelite.api.events.ProjectileMoved;
import net.runelite.api.events.VarbitChanged;
import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetInfo;
import net.runelite.client.Notifier;
@@ -80,17 +73,10 @@ import net.runelite.client.ui.overlay.infobox.InfoBoxManager;
@Slf4j
public class CannonPlugin extends Plugin
{
private static final Pattern NUMBER_PATTERN = Pattern.compile("([0-9]+)");
static final int MAX_OVERLAY_DISTANCE = 4100;
static final int MAX_CBALLS = 30;
private static final Set<Integer> CANNONBALL_PROJECTILE_IDS = ImmutableSet.of(
GraphicID.CANNONBALL, GraphicID.GRANITE_CANNONBALL,
GraphicID.CANNONBALL_OR, GraphicID.GRANITE_CANNONBALL_OR
);
private CannonCounter counter;
private boolean skipProjectileCheckThisTick;
private boolean cannonBallNotificationSent;
private WorldPoint clickedCannonLocation;
private boolean firstCannonLoad;
@@ -151,6 +137,7 @@ public class CannonPlugin extends Plugin
{
overlayManager.add(cannonOverlay);
overlayManager.add(cannonSpotOverlay);
clientThread.invoke(() -> cballsLeft = client.getVar(VarPlayer.CANNON_AMMO));
}
@Override
@@ -165,7 +152,6 @@ public class CannonPlugin extends Plugin
cannonBallNotificationSent = false;
cballsLeft = 0;
removeCounter();
skipProjectileCheckThisTick = false;
spotPoints.clear();
}
@@ -278,7 +264,7 @@ public class CannonPlugin extends Plugin
}
}
}
@Subscribe
public void onMenuOptionClicked(MenuOptionClicked event)
{
@@ -309,32 +295,16 @@ public class CannonPlugin extends Plugin
}
@Subscribe
public void onProjectileMoved(ProjectileMoved event)
public void onVarbitChanged(VarbitChanged varbitChanged)
{
Projectile projectile = event.getProjectile();
if (CANNONBALL_PROJECTILE_IDS.contains(projectile.getId()) && cannonPosition != null && cannonWorld == client.getWorld())
if (varbitChanged.getIndex() == VarPlayer.CANNON_AMMO.getId())
{
WorldPoint projectileLoc = WorldPoint.fromLocal(client, projectile.getX1(), projectile.getY1(), client.getPlane());
cballsLeft = client.getVar(VarPlayer.CANNON_AMMO);
//Check to see if projectile x,y is 0 else it will continuously decrease while ball is flying.
if (cannonPosition.contains(projectileLoc) && projectile.getX() == 0 && projectile.getY() == 0)
if (config.showCannonNotifications() && !cannonBallNotificationSent && cballsLeft > 0 && config.lowWarningThreshold() >= cballsLeft)
{
// When there's a chat message about cannon reloaded/unloaded/out of ammo,
// the message event runs before the projectile event. However they run
// in the opposite order on the server. So if both fires in the same tick,
// we don't want to update the cannonball counter if it was set to a specific
// amount.
if (!skipProjectileCheckThisTick)
{
cballsLeft--;
if (config.showCannonNotifications() && !cannonBallNotificationSent && cballsLeft > 0 && config.lowWarningThreshold() >= cballsLeft)
{
notifier.notify(String.format("Your cannon has %d cannon balls remaining!", cballsLeft));
cannonBallNotificationSent = true;
}
}
notifier.notify(String.format("Your cannon has %d cannon balls remaining!", cballsLeft));
cannonBallNotificationSent = true;
}
}
}
@@ -351,32 +321,13 @@ public class CannonPlugin extends Plugin
{
cannonPlaced = true;
addCounter();
cballsLeft = 0;
firstCannonLoad = true;
final ItemContainer inventory = client.getItemContainer(InventoryID.INVENTORY);
if (inventory != null)
{
int invCballs = inventory.count(ItemID.GRANITE_CANNONBALL) > 0
? inventory.count(ItemID.GRANITE_CANNONBALL)
: inventory.count(ItemID.CANNONBALL);
// Cannonballs are always forcibly loaded after the furnace is added. If the player has more than
// the max number of cannon balls in their inventory, the cannon will always be fully filled.
// This is preferable to using the proceeding "You load the cannon with x cannon balls" message
// since it will show a lower number of cannon balls if the cannon is already partially-filled
// prior to being placed.
if (invCballs >= MAX_CBALLS)
{
cballsLeft = MAX_CBALLS;
}
}
}
else if (event.getMessage().contains("You pick up the cannon")
|| event.getMessage().contains("Your cannon has decayed. Speak to Nulodion to get a new one!")
|| event.getMessage().contains("Your cannon has been destroyed!"))
{
cannonPlaced = false;
cballsLeft = 0;
removeCounter();
cannonPosition = null;
}
@@ -410,83 +361,21 @@ public class CannonPlugin extends Plugin
clickedCannonLocation = null;
}
Matcher m = NUMBER_PATTERN.matcher(event.getMessage());
if (m.find())
{
// The cannon will usually refill to MAX_CBALLS, but if the
// player didn't have enough cannonballs in their inventory,
// it could fill up less than that. Filling the cannon to
// cballsLeft + amt is not always accurate though because our
// counter doesn't decrease if the player has been too far away
// from the cannon due to the projectiels not being in memory,
// so our counter can be higher than it is supposed to be.
int amt = Integer.valueOf(m.group());
if (cballsLeft + amt >= MAX_CBALLS)
{
skipProjectileCheckThisTick = true;
cballsLeft = MAX_CBALLS;
}
else
{
cballsLeft += amt;
}
}
else if (event.getMessage().equals("You load the cannon with one cannonball."))
{
if (cballsLeft + 1 >= MAX_CBALLS)
{
skipProjectileCheckThisTick = true;
cballsLeft = MAX_CBALLS;
}
else
{
cballsLeft++;
}
}
cannonBallNotificationSent = false;
}
else if (event.getMessage().contains("Your cannon is out of ammo!"))
{
skipProjectileCheckThisTick = true;
// If the player was out of range of the cannon, some cannonballs
// may have been used without the client knowing, so having this
// extra check is a good idea.
cballsLeft = 0;
if (config.showCannonNotifications())
{
notifier.notify("Your cannon is out of ammo!");
}
}
else if (event.getMessage().startsWith("Your cannon contains"))
{
Matcher m = NUMBER_PATTERN.matcher(event.getMessage());
if (m.find())
{
cballsLeft = Integer.parseInt(m.group());
}
}
else if (event.getMessage().startsWith("You unload your cannon and receive Cannonball")
|| event.getMessage().startsWith("You unload your cannon and receive Granite cannonball"))
{
skipProjectileCheckThisTick = true;
cballsLeft = 0;
}
else if (event.getMessage().equals("This isn't your cannon!") || event.getMessage().equals("This is not your cannon."))
{
clickedCannonLocation = null;
}
}
@Subscribe
public void onGameTick(GameTick event)
{
skipProjectileCheckThisTick = false;
}
Color getStateColor()
{
if (cballsLeft > 15)

View File

@@ -608,7 +608,7 @@ public class ChatChannelPlugin extends Plugin
Widget chatTitle = client.getWidget(WidgetInfo.FRIENDS_CHAT_TITLE);
if (friendsChatManager != null && friendsChatManager.getCount() > 0 && chatTitle != null)
{
chatTitle.setText(chatTitle.getText() + " (" + friendsChatManager.getCount() + "/100)");
chatTitle.setText(chatTitle.getText() + " (" + friendsChatManager.getCount() + "/" + friendsChatManager.getSize() + ")");
}
}
else if (event.getScriptId() == ScriptID.CLAN_SIDEPANEL_DRAW)

View File

@@ -320,7 +320,9 @@ public class ChatFilterPlugin extends Plugin
String censorMessage(final String username, final String message)
{
String strippedMessage = jagexPrintableCharMatcher.retainFrom(message)
.replace('\u00A0', ' ');
.replace('\u00A0', ' ')
.replaceAll("<lt>", "<")
.replaceAll("<gt>", ">");
String strippedAccents = stripAccents(strippedMessage);
assert strippedMessage.length() == strippedAccents.length();

View File

@@ -51,6 +51,10 @@ import net.runelite.client.config.ConfigManager;
import net.runelite.client.discord.DiscordService;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.events.ConfigChanged;
import net.runelite.client.party.PartyMember;
import net.runelite.client.party.PartyService;
import net.runelite.client.party.WSClient;
import net.runelite.client.party.messages.UserSync;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.task.Schedule;
@@ -58,11 +62,7 @@ import net.runelite.client.ui.ClientToolbar;
import net.runelite.client.ui.NavigationButton;
import net.runelite.client.util.ImageUtil;
import net.runelite.client.util.LinkBrowser;
import net.runelite.client.ws.PartyMember;
import net.runelite.client.ws.PartyService;
import net.runelite.client.ws.WSClient;
import net.runelite.discord.DiscordUser;
import net.runelite.http.api.ws.messages.party.UserSync;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
@@ -281,7 +281,7 @@ public class DiscordPlugin extends Plugin
discordUser.avatar
);
userInfo.setMemberId(localMember.getMemberId());
wsClient.send(userInfo);
partyService.send(userInfo);
}
}
}

View File

@@ -26,7 +26,7 @@ package net.runelite.client.plugins.discord;
import lombok.EqualsAndHashCode;
import lombok.Value;
import net.runelite.http.api.ws.messages.party.PartyMemberMessage;
import net.runelite.client.party.messages.PartyMemberMessage;
@Value
@EqualsAndHashCode(callSuper = true)

View File

@@ -47,9 +47,9 @@ import net.runelite.client.events.PartyChanged;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.ui.overlay.OverlayManager;
import net.runelite.client.ws.PartyMember;
import net.runelite.client.ws.PartyService;
import net.runelite.client.ws.WSClient;
import net.runelite.client.party.PartyMember;
import net.runelite.client.party.PartyService;
import net.runelite.client.party.WSClient;
@PluginDescriptor(
name = "DPS Counter",
@@ -199,7 +199,7 @@ public class DpsCounterPlugin extends Plugin
{
final DpsUpdate dpsUpdate = new DpsUpdate(hit, isBoss);
dpsUpdate.setMemberId(localMember.getMemberId());
wsClient.send(dpsUpdate);
partyService.send(dpsUpdate);
}
if (dpsConfig.bossDamage() && !isBoss)

View File

@@ -42,7 +42,7 @@ import net.runelite.client.ui.overlay.components.TitleComponent;
import net.runelite.client.ui.overlay.tooltip.Tooltip;
import net.runelite.client.ui.overlay.tooltip.TooltipManager;
import net.runelite.client.util.QuantityFormatter;
import net.runelite.client.ws.PartyService;
import net.runelite.client.party.PartyService;
class DpsOverlay extends OverlayPanel
{

View File

@@ -27,7 +27,7 @@ package net.runelite.client.plugins.dpscounter;
import lombok.EqualsAndHashCode;
import lombok.Value;
import net.runelite.http.api.ws.messages.party.PartyMemberMessage;
import net.runelite.client.party.messages.PartyMemberMessage;
@Value
@EqualsAndHashCode(callSuper = true)

View File

@@ -25,9 +25,16 @@
*/
package net.runelite.client.plugins.entityhider;
import com.google.common.annotations.VisibleForTesting;
import com.google.inject.Provides;
import javax.inject.Inject;
import net.runelite.api.Client;
import net.runelite.api.NPC;
import net.runelite.api.Player;
import net.runelite.api.Projectile;
import net.runelite.api.Renderable;
import net.runelite.api.Varbits;
import net.runelite.client.callback.Hooks;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.events.ConfigChanged;
@@ -48,6 +55,25 @@ public class EntityHiderPlugin extends Plugin
@Inject
private EntityHiderConfig config;
@Inject
private Hooks hooks;
private boolean hideOthers;
private boolean hideOthers2D;
private boolean hideFriends;
private boolean hideFriendsChatMembers;
private boolean hideClanMembers;
private boolean hideIgnoredPlayers;
private boolean hideLocalPlayer;
private boolean hideLocalPlayer2D;
private boolean hideNPCs;
private boolean hideNPCs2D;
private boolean hidePets;
private boolean hideAttackers;
private boolean hideProjectiles;
private final Hooks.RenderableDrawListener drawListener = this::shouldDraw;
@Provides
EntityHiderConfig provideConfig(ConfigManager configManager)
{
@@ -58,6 +84,14 @@ public class EntityHiderPlugin extends Plugin
protected void startUp()
{
updateConfig();
hooks.registerRenderableDrawListener(drawListener);
}
@Override
protected void shutDown()
{
hooks.unregisterRenderableDrawListener(drawListener);
}
@Subscribe
@@ -71,52 +105,107 @@ public class EntityHiderPlugin extends Plugin
private void updateConfig()
{
client.setIsHidingEntities(true);
hideOthers = config.hideOthers();
hideOthers2D = config.hideOthers2D();
client.setOthersHidden(config.hideOthers());
client.setOthersHidden2D(config.hideOthers2D());
hideFriends = config.hideFriends();
hideFriendsChatMembers = config.hideFriendsChatMembers();
hideClanMembers = config.hideClanChatMembers();
hideIgnoredPlayers = config.hideIgnores();
client.setFriendsHidden(config.hideFriends());
client.setFriendsChatMembersHidden(config.hideFriendsChatMembers());
client.setClanChatMembersHidden(config.hideClanChatMembers());
client.setIgnoresHidden(config.hideIgnores());
hideLocalPlayer = config.hideLocalPlayer();
hideLocalPlayer2D = config.hideLocalPlayer2D();
client.setLocalPlayerHidden(config.hideLocalPlayer());
client.setLocalPlayerHidden2D(config.hideLocalPlayer2D());
hideNPCs = config.hideNPCs();
hideNPCs2D = config.hideNPCs2D();
client.setNPCsHidden(config.hideNPCs());
client.setNPCsHidden2D(config.hideNPCs2D());
hidePets = config.hidePets();
client.setPetsHidden(config.hidePets());
hideAttackers = config.hideAttackers();
client.setAttackersHidden(config.hideAttackers());
client.setProjectilesHidden(config.hideProjectiles());
hideProjectiles = config.hideProjectiles();
}
@Override
protected void shutDown() throws Exception
@VisibleForTesting
boolean shouldDraw(Renderable renderable, boolean drawingUI)
{
client.setIsHidingEntities(false);
if (renderable instanceof Player)
{
Player player = (Player) renderable;
Player local = client.getLocalPlayer();
client.setOthersHidden(false);
client.setOthersHidden2D(false);
if (player.getName() == null)
{
// player.isFriend() and player.isFriendsChatMember() npe when the player has a null name
return true;
}
client.setFriendsHidden(false);
client.setFriendsChatMembersHidden(false);
client.setClanChatMembersHidden(false);
client.setIgnoresHidden(false);
// Allow hiding local self in pvp, which is an established meta.
// It is more advantageous than renderself due to being able to still render local player 2d
if (player == local)
{
return !(drawingUI ? hideLocalPlayer2D : hideLocalPlayer);
}
client.setLocalPlayerHidden(false);
client.setLocalPlayerHidden2D(false);
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);
}
client.setNPCsHidden(false);
client.setNPCsHidden2D(false);
if (hideAttackers && player.getInteracting() == local)
{
return false; // hide
}
client.setPetsHidden(false);
if (player.isFriend())
{
return !hideFriends;
}
if (player.isFriendsChatMember())
{
return !hideFriendsChatMembers;
}
if (player.isClanMember())
{
return !hideClanMembers;
}
if (client.getIgnoreContainer().findByName(player.getName()) != null)
{
return !hideIgnoredPlayers;
}
client.setAttackersHidden(false);
return !(drawingUI ? hideOthers2D : hideOthers);
}
else if (renderable instanceof NPC)
{
NPC npc = (NPC) renderable;
client.setProjectilesHidden(false);
if (npc.getComposition().isFollower() && npc != client.getFollower())
{
return !hidePets;
}
if (npc.getInteracting() == client.getLocalPlayer())
{
boolean b = hideAttackers;
// Kludge to make hide attackers only affect 2d or 3d if the 2d or 3d hide is on
// This allows hiding 2d for all npcs, including attackers.
if (hideNPCs2D || hideNPCs)
{
b &= drawingUI ? hideNPCs2D : hideNPCs;
}
return !b;
}
return !(drawingUI ? hideNPCs2D : hideNPCs);
}
else if (renderable instanceof Projectile)
{
return !hideProjectiles;
}
return true;
}
}

View File

@@ -41,11 +41,15 @@ import net.runelite.api.GameObject;
import net.runelite.api.GameState;
import static net.runelite.api.HintArrowType.WORLD_POSITION;
import net.runelite.api.MenuAction;
import static net.runelite.api.ObjectID.DEPLETED_VEIN;
import static net.runelite.api.ObjectID.DEPLETED_VEIN_26665;
import static net.runelite.api.ObjectID.DEPLETED_VEIN_26666;
import static net.runelite.api.ObjectID.DEPLETED_VEIN_26667;
import static net.runelite.api.ObjectID.DEPLETED_VEIN_26668;
import static net.runelite.api.ObjectID.EMPTY_WALL;
import static net.runelite.api.ObjectID.MINERAL_VEIN;
import static net.runelite.api.ObjectID.MINERAL_VEIN_5990;
import static net.runelite.api.ObjectID.MINERAL_VEIN_5991;
import static net.runelite.api.ObjectID.ORE_VEIN_26661;
import static net.runelite.api.ObjectID.ORE_VEIN_26662;
import static net.runelite.api.ObjectID.ORE_VEIN_26663;
@@ -355,12 +359,22 @@ public class MiningPlugin extends Plugin
respawns.add(rockRespawn);
break;
}
case DEPLETED_VEIN: // Depleted gold vein
{
Rock rock = Rock.MINERAL_VEIN;
RockRespawn rockRespawn = new RockRespawn(rock, object.getWorldLocation(), Instant.now(), (int) rock.getRespawnTime(region).toMillis(), rock.getZOffset());
respawns.add(rockRespawn);
break;
}
case ORE_VEIN_26661: // Motherlode vein
case ORE_VEIN_26662: // Motherlode vein
case ORE_VEIN_26663: // Motherlode vein
case ORE_VEIN_26664: // Motherlode vein
case ROCKS_41547: // Barronite vein
case ROCKS_41548: // Barronite vein
case MINERAL_VEIN: // Arzinian gold vein
case MINERAL_VEIN_5990: // Gold vein
case MINERAL_VEIN_5991: // Gold vein
{
// If the vein respawns before the timer is up, remove it
final WorldPoint point = object.getWorldLocation();

View File

@@ -98,7 +98,8 @@ enum Rock
TE_SALT(Duration.of(9, GAME_TICKS), 0, ROCKS_33256),
BASALT(Duration.of(9, GAME_TICKS), 0, ROCKS_33257),
DAEYALT_ESSENCE(Duration.of(MiningRocksOverlay.DAEYALT_MAX_RESPAWN_TIME, GAME_TICKS), 0, DAEYALT_ESSENCE_39095),
BARRONITE(Duration.of(89, GAME_TICKS), 140);
BARRONITE(Duration.of(89, GAME_TICKS), 140),
MINERAL_VEIN(Duration.of(100, GAME_TICKS), 150);
private static final int WILDERNESS_RESOURCE_AREA = 12605;
private static final int MISCELLANIA = 10044;

View File

@@ -45,7 +45,7 @@ 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;
import net.runelite.client.ws.PartyMember;
import net.runelite.client.party.PartyMember;
class PartyMemberBox extends JPanel
{

View File

@@ -47,7 +47,7 @@ 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;
import net.runelite.client.party.PartyService;
class PartyPanel extends PluginPanel
{

View File

@@ -66,6 +66,12 @@ import net.runelite.client.events.ConfigChanged;
import net.runelite.client.events.OverlayMenuClicked;
import net.runelite.client.events.PartyChanged;
import net.runelite.client.events.PartyMemberAvatar;
import net.runelite.client.party.PartyMember;
import net.runelite.client.party.PartyService;
import net.runelite.client.party.WSClient;
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.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.plugins.party.data.PartyData;
@@ -83,12 +89,6 @@ 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;
import net.runelite.http.api.ws.messages.party.UserJoin;
import net.runelite.http.api.ws.messages.party.UserPart;
import net.runelite.http.api.ws.messages.party.UserSync;
@PluginDescriptor(
name = "Party",
@@ -104,9 +104,6 @@ public class PartyPlugin extends Plugin
@Inject
private PartyService party;
@Inject
private WSClient ws;
@Inject
private OverlayManager overlayManager;
@@ -266,7 +263,7 @@ public class PartyPlugin extends Plugin
event.consume();
final TilePing tilePing = new TilePing(selectedSceneTile.getWorldLocation());
tilePing.setMemberId(party.getLocalMember().getMemberId());
wsClient.send(tilePing);
party.send(tilePing);
}
@Subscribe
@@ -326,7 +323,7 @@ public class PartyPlugin extends Plugin
final LocationUpdate locationUpdate = new LocationUpdate(location);
locationUpdate.setMemberId(localMember.getMemberId());
wsClient.send(locationUpdate);
party.send(locationUpdate);
}
@Subscribe
@@ -342,7 +339,7 @@ public class PartyPlugin extends Plugin
// Request sync
final UserSync userSync = new UserSync();
userSync.setMemberId(party.getLocalMember().getMemberId());
ws.send(userSync);
party.send(userSync);
}
}
@@ -442,21 +439,21 @@ public class PartyPlugin extends Plugin
{
final SkillUpdate update = new SkillUpdate(Skill.HITPOINTS, currentHealth, realHealth);
update.setMemberId(localMember.getMemberId());
ws.send(update);
party.send(update);
}
if (forceSend || currentPrayer != lastPray)
{
final SkillUpdate update = new SkillUpdate(Skill.PRAYER, currentPrayer, realPrayer);
update.setMemberId(localMember.getMemberId());
ws.send(update);
party.send(update);
}
if (forceSend || !characterName.equals(lastCharacterName))
{
final CharacterNameUpdate update = new CharacterNameUpdate(characterName);
update.setMemberId(localMember.getMemberId());
ws.send(update);
party.send(update);
}
}

View File

@@ -30,7 +30,7 @@ import net.runelite.api.Point;
import net.runelite.api.coords.WorldPoint;
import net.runelite.client.ui.overlay.worldmap.WorldMapPoint;
import net.runelite.client.util.ImageUtil;
import net.runelite.client.ws.PartyMember;
import net.runelite.client.party.PartyMember;
class PartyWorldMapPoint extends WorldMapPoint
{

View File

@@ -31,7 +31,7 @@ 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;
import net.runelite.client.party.PartyMember;
@Setter
@Getter

View File

@@ -25,7 +25,7 @@
package net.runelite.client.plugins.party.messages;
import lombok.Data;
import net.runelite.http.api.ws.messages.party.PartyMemberMessage;
import net.runelite.client.party.messages.PartyMemberMessage;
@Data
public class CharacterNameUpdate extends PartyMemberMessage

View File

@@ -27,7 +27,7 @@ package net.runelite.client.plugins.party.messages;
import lombok.EqualsAndHashCode;
import lombok.Value;
import net.runelite.api.coords.WorldPoint;
import net.runelite.http.api.ws.messages.party.PartyMemberMessage;
import net.runelite.client.party.messages.PartyMemberMessage;
@Value
@EqualsAndHashCode(callSuper = true)

View File

@@ -27,7 +27,7 @@ package net.runelite.client.plugins.party.messages;
import lombok.AllArgsConstructor;
import lombok.Getter;
import net.runelite.api.Skill;
import net.runelite.http.api.ws.messages.party.PartyMemberMessage;
import net.runelite.client.party.messages.PartyMemberMessage;
@AllArgsConstructor
@Getter

View File

@@ -27,7 +27,7 @@ package net.runelite.client.plugins.party.messages;
import lombok.EqualsAndHashCode;
import lombok.Value;
import net.runelite.api.coords.WorldPoint;
import net.runelite.http.api.ws.messages.party.PartyMemberMessage;
import net.runelite.client.party.messages.PartyMemberMessage;
@Value
@EqualsAndHashCode(callSuper = true)

View File

@@ -82,6 +82,9 @@ import net.runelite.client.events.ConfigChanged;
import net.runelite.client.events.OverlayMenuClicked;
import net.runelite.client.game.SpriteManager;
import net.runelite.client.input.KeyManager;
import net.runelite.client.party.PartyMember;
import net.runelite.client.party.PartyService;
import net.runelite.client.party.messages.PartyChatMessage;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.plugins.raids.events.RaidReset;
@@ -94,11 +97,7 @@ import net.runelite.client.util.HotkeyListener;
import net.runelite.client.util.ImageCapture;
import net.runelite.client.util.Text;
import static net.runelite.client.util.Text.sanitize;
import net.runelite.client.ws.PartyMember;
import net.runelite.client.ws.PartyService;
import net.runelite.client.ws.WSClient;
import net.runelite.http.api.chat.LayoutRoom;
import net.runelite.http.api.ws.messages.party.PartyChatMessage;
@PluginDescriptor(
name = "Chambers Of Xeric",
@@ -156,9 +155,6 @@ public class RaidsPlugin extends Plugin
@Inject
private PartyService party;
@Inject
private WSClient ws;
@Inject
private ChatCommandManager chatCommandManager;
@@ -494,7 +490,7 @@ public class RaidsPlugin extends Plugin
{
final PartyChatMessage message = new PartyChatMessage(layoutMessage);
message.setMemberId(localMember.getMemberId());
ws.send(message);
party.send(message);
}
}

View File

@@ -66,6 +66,17 @@ public interface SpecialCounterConfig extends Config
return Color.WHITE;
}
@ConfigItem(
position = 3,
keyName = "infobox",
name = "Infobox",
description = "Adds an infobox counting special attacks"
)
default boolean infobox()
{
return true;
}
@ConfigItem(
position = 10,
keyName = "dragonWarhammerThreshold",

View File

@@ -67,8 +67,8 @@ import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.ui.overlay.OverlayManager;
import net.runelite.client.ui.overlay.infobox.InfoBoxManager;
import net.runelite.client.util.ImageUtil;
import net.runelite.client.ws.PartyService;
import net.runelite.client.ws.WSClient;
import net.runelite.client.party.PartyService;
import net.runelite.client.party.WSClient;
@PluginDescriptor(
name = "Special Attack Counter",
@@ -293,13 +293,16 @@ public class SpecialCounterPlugin extends Plugin
int hit = getHit(specialWeapon, hitsplat);
int localPlayerId = client.getLocalPlayer().getId();
updateCounter(specialWeapon, null, hit);
if (config.infobox())
{
updateCounter(specialWeapon, null, hit);
}
if (!party.getMembers().isEmpty())
{
final SpecialCounterUpdate specialCounterUpdate = new SpecialCounterUpdate(interactingId, specialWeapon, hit, client.getWorld(), localPlayerId);
specialCounterUpdate.setMemberId(party.getLocalMember().getMemberId());
wsClient.send(specialCounterUpdate);
party.send(specialCounterUpdate);
}
playerInfoDrops.add(createSpecInfoDrop(specialWeapon, hit, localPlayerId));
@@ -359,7 +362,10 @@ public class SpecialCounterPlugin extends Plugin
// Otherwise we only add the count if it is against a npc we are already tracking
if (interactedNpcIds.contains(event.getNpcId()))
{
updateCounter(event.getWeapon(), name, event.getHit());
if (config.infobox())
{
updateCounter(event.getWeapon(), name, event.getHit());
}
}
if (event.getWorld() == client.getWorld())

View File

@@ -26,7 +26,7 @@ package net.runelite.client.plugins.specialcounter;
import lombok.EqualsAndHashCode;
import lombok.Value;
import net.runelite.http.api.ws.messages.party.PartyMemberMessage;
import net.runelite.client.party.messages.PartyMemberMessage;
@Value
@EqualsAndHashCode(callSuper = true)

View File

@@ -40,7 +40,7 @@ import static net.runelite.client.util.RSTimeUnit.GAME_TICKS;
@Getter(AccessLevel.PACKAGE)
enum GameTimer
{
STAMINA(ItemID.STAMINA_POTION4, GameTimerImageType.ITEM, "Stamina", true),
STAMINA(ItemID.STAMINA_POTION4, GameTimerImageType.ITEM, "Stamina", false),
ANTIFIRE(ItemID.ANTIFIRE_POTION4, GameTimerImageType.ITEM, "Antifire", 6, ChronoUnit.MINUTES),
EXANTIFIRE(ItemID.EXTENDED_ANTIFIRE4, GameTimerImageType.ITEM, "Extended antifire", 12, ChronoUnit.MINUTES),
OVERLOAD(ItemID.OVERLOAD_4, GameTimerImageType.ITEM, "Overload", 5, ChronoUnit.MINUTES, true),

View File

@@ -0,0 +1,239 @@
/*
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.runelite.client.util;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.internal.Streams;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Adapts values whose runtime type may differ from their declaration type. This
* is necessary when a field's type is not the same type that GSON should create
* when deserializing that field. For example, consider these types:
* <pre> {@code
* abstract class Shape {
* int x;
* int y;
* }
* class Circle extends Shape {
* int radius;
* }
* class Rectangle extends Shape {
* int width;
* int height;
* }
* class Diamond extends Shape {
* int width;
* int height;
* }
* class Drawing {
* Shape bottomShape;
* Shape topShape;
* }
* }</pre>
* <p>Without additional type information, the serialized JSON is ambiguous. Is
* the bottom shape in this drawing a rectangle or a diamond? <pre> {@code
* {
* "bottomShape": {
* "width": 10,
* "height": 5,
* "x": 0,
* "y": 0
* },
* "topShape": {
* "radius": 2,
* "x": 4,
* "y": 1
* }
* }}</pre>
* This class addresses this problem by adding type information to the
* serialized JSON and honoring that type information when the JSON is
* deserialized: <pre> {@code
* {
* "bottomShape": {
* "type": "Diamond",
* "width": 10,
* "height": 5,
* "x": 0,
* "y": 0
* },
* "topShape": {
* "type": "Circle",
* "radius": 2,
* "x": 4,
* "y": 1
* }
* }}</pre>
* Both the type field name ({@code "type"}) and the type labels ({@code
* "Rectangle"}) are configurable.
*
* <h3>Registering Types</h3>
* Create a {@code RuntimeTypeAdapterFactory} by passing the base type and type field
* name to the {@link #of} factory method. If you don't supply an explicit type
* field name, {@code "type"} will be used. <pre> {@code
* RuntimeTypeAdapterFactory<Shape> shapeAdapterFactory
* = RuntimeTypeAdapterFactory.of(Shape.class, "type");
* }</pre>
* Next register all of your subtypes. Every subtype must be explicitly
* registered. This protects your application from injection attacks. If you
* don't supply an explicit type label, the type's simple name will be used.
* <pre> {@code
* shapeAdapter.registerSubtype(Rectangle.class, "Rectangle");
* shapeAdapter.registerSubtype(Circle.class, "Circle");
* shapeAdapter.registerSubtype(Diamond.class, "Diamond");
* }</pre>
* Finally, register the type adapter factory in your application's GSON builder:
* <pre> {@code
* Gson gson = new GsonBuilder()
* .registerTypeAdapterFactory(shapeAdapterFactory)
* .create();
* }</pre>
* Like {@code GsonBuilder}, this API supports chaining: <pre> {@code
* RuntimeTypeAdapterFactory<Shape> shapeAdapterFactory = RuntimeTypeAdapterFactory.of(Shape.class)
* .registerSubtype(Rectangle.class)
* .registerSubtype(Circle.class)
* .registerSubtype(Diamond.class);
* }</pre>
*/
public final class RuntimeTypeAdapterFactory<T> implements TypeAdapterFactory {
private final Class<?> baseType;
private final String typeFieldName;
private final Map<String, Class<?>> labelToSubtype = new LinkedHashMap<String, Class<?>>();
private final Map<Class<?>, String> subtypeToLabel = new LinkedHashMap<Class<?>, String>();
private RuntimeTypeAdapterFactory(Class<?> baseType, String typeFieldName) {
if (typeFieldName == null || baseType == null) {
throw new NullPointerException();
}
this.baseType = baseType;
this.typeFieldName = typeFieldName;
}
/**
* Creates a new runtime type adapter using for {@code baseType} using {@code
* typeFieldName} as the type field name. Type field names are case sensitive.
*/
public static <T> RuntimeTypeAdapterFactory<T> of(Class<T> baseType, String typeFieldName) {
return new RuntimeTypeAdapterFactory<T>(baseType, typeFieldName);
}
/**
* Creates a new runtime type adapter for {@code baseType} using {@code "type"} as
* the type field name.
*/
public static <T> RuntimeTypeAdapterFactory<T> of(Class<T> baseType) {
return new RuntimeTypeAdapterFactory<T>(baseType, "type");
}
/**
* Registers {@code type} identified by {@code label}. Labels are case
* sensitive.
*
* @throws IllegalArgumentException if either {@code type} or {@code label}
* have already been registered on this type adapter.
*/
public RuntimeTypeAdapterFactory<T> registerSubtype(Class<? extends T> type, String label) {
if (type == null || label == null) {
throw new NullPointerException();
}
if (subtypeToLabel.containsKey(type) || labelToSubtype.containsKey(label)) {
throw new IllegalArgumentException("types and labels must be unique");
}
labelToSubtype.put(label, type);
subtypeToLabel.put(type, label);
return this;
}
/**
* Registers {@code type} identified by its {@link Class#getSimpleName simple
* name}. Labels are case sensitive.
*
* @throws IllegalArgumentException if either {@code type} or its simple name
* have already been registered on this type adapter.
*/
public RuntimeTypeAdapterFactory<T> registerSubtype(Class<? extends T> type) {
return registerSubtype(type, type.getSimpleName());
}
public <R> TypeAdapter<R> create(Gson gson, TypeToken<R> type) {
if (type.getRawType() != baseType) {
return null;
}
final Map<String, TypeAdapter<?>> labelToDelegate
= new LinkedHashMap<String, TypeAdapter<?>>();
final Map<Class<?>, TypeAdapter<?>> subtypeToDelegate
= new LinkedHashMap<Class<?>, TypeAdapter<?>>();
for (Map.Entry<String, Class<?>> entry : labelToSubtype.entrySet()) {
TypeAdapter<?> delegate = gson.getDelegateAdapter(this, TypeToken.get(entry.getValue()));
labelToDelegate.put(entry.getKey(), delegate);
subtypeToDelegate.put(entry.getValue(), delegate);
}
return new TypeAdapter<R>() {
@Override public R read(JsonReader in) throws IOException {
JsonElement jsonElement = Streams.parse(in);
JsonElement labelJsonElement = jsonElement.getAsJsonObject().remove(typeFieldName);
if (labelJsonElement == null) {
throw new JsonParseException("cannot deserialize " + baseType
+ " because it does not define a field named " + typeFieldName);
}
String label = labelJsonElement.getAsString();
@SuppressWarnings("unchecked") // registration requires that subtype extends T
TypeAdapter<R> delegate = (TypeAdapter<R>) labelToDelegate.get(label);
if (delegate == null) {
throw new JsonParseException("cannot deserialize " + baseType + " subtype named "
+ label + "; did you forget to register a subtype?");
}
return delegate.fromJsonTree(jsonElement);
}
@Override public void write(JsonWriter out, R value) throws IOException {
Class<?> srcType = value.getClass();
String label = subtypeToLabel.get(srcType);
@SuppressWarnings("unchecked") // registration requires that subtype extends T
TypeAdapter<R> delegate = (TypeAdapter<R>) subtypeToDelegate.get(srcType);
if (delegate == null) {
throw new JsonParseException("cannot serialize " + srcType.getName()
+ "; did you forget to register a subtype?");
}
JsonObject jsonObject = delegate.toJsonTree(value).getAsJsonObject();
if (jsonObject.has(typeFieldName)) {
throw new JsonParseException("cannot serialize " + srcType.getName()
+ " because it already defines a field named " + typeFieldName);
}
JsonObject clone = new JsonObject();
clone.add(typeFieldName, new JsonPrimitive(label));
for (Map.Entry<String, JsonElement> e : jsonObject.entrySet()) {
clone.add(e.getKey(), e.getValue());
}
Streams.write(clone, out);
}
}.nullSafe();
}
}

View File

@@ -10687,5 +10687,9 @@
"ensouled hellhound head": [
26996,
26997
],
"smiths gloves": [
27029,
27031
]
}

View File

@@ -19,4 +19,5 @@ runelite.session=https://session.openosrs.dev
runelite.static.base=https://static.runelite.net
runelite.ws=https://api.runelite.net/ws
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

View File

@@ -0,0 +1,152 @@
/*
* 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.config;
import com.google.inject.Guice;
import com.google.inject.testing.fieldbinder.Bind;
import com.google.inject.testing.fieldbinder.BoundFieldModule;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.ScheduledExecutorService;
import javax.inject.Inject;
import javax.inject.Named;
import net.runelite.api.Client;
import net.runelite.client.RuneLite;
import net.runelite.client.eventbus.EventBus;
import org.junit.Assert;
import static org.junit.Assert.assertNotNull;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class ConfigManagerTest
{
@Mock
@Bind
EventBus eventBus;
@Mock
@Bind
ScheduledExecutorService executor;
@Mock
@Bind
RuneLiteConfig runeliteConfig;
@Bind
@Named("sessionfile")
File sessionfile = RuneLite.DEFAULT_SESSION_FILE;
@Bind
@Named("config")
File config = RuneLite.DEFAULT_CONFIG_FILE;
@Mock
@Bind
Client client;
@Mock
@Bind
ConfigClient configClient;
@Inject
ConfigManager manager;
@Before
public void before()
{
Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this);
}
@Test
public void testGetConfig() throws IOException
{
manager.setConfiguration("test", "key", "moo");
TestConfig conf = manager.getConfig(TestConfig.class);
Assert.assertEquals("moo", conf.key());
}
@Test
public void testGetConfigDefault() throws IOException
{
TestConfig conf = manager.getConfig(TestConfig.class);
Assert.assertEquals("default", conf.key());
}
@Test
public void testSetConfig() throws IOException
{
TestConfig conf = manager.getConfig(TestConfig.class);
conf.key("new value");
Assert.assertEquals("new value", conf.key());
}
@Test
public void testGetConfigDescriptor() throws IOException
{
TestConfig conf = manager.getConfig(TestConfig.class);
ConfigDescriptor descriptor = manager.getConfigDescriptor(conf);
Assert.assertEquals(2, descriptor.getItems().size());
}
@Test
public void testResetNullDefaultConfig()
{
TestConfig conf = manager.getConfig(TestConfig.class);
ConfigDescriptor descriptor = manager.getConfigDescriptor(conf);
conf.nullDefaultKey("new value");
manager.unsetConfiguration(descriptor.getGroup().value(), "nullDefaultKey");
manager.setDefaultConfiguration(conf, false);
Assert.assertNull(conf.nullDefaultKey());
}
@Test
public void testKeySplitter()
{
for (String[] test : new String[][]
{
{"rsprofile", "rsprofile.123", "rsprofileThing"},
{"rsprofile", null, "rsprofileThing"},
{"foo", "rsprofile.123", "big.bad"},
{"foo", null, "big.bad"},
{"foo", "rsprofile.123", "456"},
{"foo", null, "file.256"},
})
{
String whole = ConfigManager.getWholeKey(test[0], test[1], test[2]);
String[] split = ConfigManager.splitKey(whole);
assertNotNull(split);
Assert.assertEquals(split[0], test[0]);
Assert.assertEquals(split[1], test[1]);
Assert.assertEquals(split[2], test[2]);
}
}
}

View File

@@ -0,0 +1,63 @@
/*
* 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.config;
@ConfigGroup("test")
public interface TestConfig extends Config
{
@ConfigItem(
keyName = "key",
name = "Key Name",
description = "value"
)
default String key()
{
return "default";
}
@ConfigItem(
keyName = "key",
name = "Key Name",
description = "value"
)
void key(String key);
@ConfigItem(
keyName = "nullDefaultKey",
name = "Key Name",
description = "value"
)
void nullDefaultKey(String key);
@ConfigItem(
keyName = "nullDefaultKey",
name = "Key Name",
description = "value"
)
default String nullDefaultKey()
{
return null;
}
}

View File

@@ -31,10 +31,9 @@ import com.google.inject.testing.fieldbinder.BoundFieldModule;
import javax.inject.Inject;
import net.runelite.api.ChatMessageType;
import net.runelite.api.Client;
import net.runelite.api.InventoryID;
import net.runelite.api.ItemContainer;
import net.runelite.api.ItemID;
import net.runelite.api.VarPlayer;
import net.runelite.api.events.ChatMessage;
import net.runelite.api.events.VarbitChanged;
import net.runelite.client.Notifier;
import net.runelite.client.game.ItemManager;
import net.runelite.client.ui.overlay.OverlayManager;
@@ -45,8 +44,11 @@ import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.mockito.ArgumentMatchers.any;
import org.mockito.Mock;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.mockito.junit.MockitoJUnitRunner;
@@ -89,13 +91,12 @@ public class CannonPluginTest
@Bind
private OverlayManager overlayManager;
private static final ChatMessage ADD_FURNACE = new ChatMessage();
private static final VarbitChanged cannonAmmoChanged = new VarbitChanged();
@BeforeClass
public static void chatMessageSetup()
public static void cannonVarpSetup()
{
ADD_FURNACE.setType(ChatMessageType.SPAM);
ADD_FURNACE.setMessage("You add the furnace.");
cannonAmmoChanged.setIndex(VarPlayer.CANNON_AMMO.getId());
}
@Before
@@ -105,82 +106,92 @@ public class CannonPluginTest
}
@Test
public void addWrongTypeOfCannonballs()
public void testAmmoCountOnPlace()
{
final ChatMessage message = new ChatMessage();
message.setType(ChatMessageType.GAMEMESSAGE);
message.setMessage("Your cannon contains 20 x Cannonball.<br>You can only add cannonballs of the same kind.");
ChatMessage chatMessage = new ChatMessage();
chatMessage.setType(ChatMessageType.SPAM);
chatMessage.setMessage("You add the furnace.");
plugin.onChatMessage(message);
assertEquals(20, plugin.getCballsLeft());
}
@Test
public void addMaxCannonballs()
{
final ItemContainer inventory = mock(ItemContainer.class);
when(client.getItemContainer(InventoryID.INVENTORY)).thenReturn(inventory);
when(inventory.count(ItemID.CANNONBALL)).thenReturn(100);
when(inventory.count(ItemID.GRANITE_CANNONBALL)).thenReturn(0);
plugin.onChatMessage(ADD_FURNACE);
plugin.onChatMessage(chatMessage);
assertTrue(plugin.isCannonPlaced());
assertEquals(30, plugin.getCballsLeft());
plugin.onVarbitChanged(cannonAmmoChanged);
assertEquals(0, plugin.getCballsLeft());
plugin.onChatMessage(loadCannonballs(30));
// Some time passes...
when(client.getVar(VarPlayer.CANNON_AMMO)).thenReturn(30);
plugin.onVarbitChanged(cannonAmmoChanged);
assertEquals(30, plugin.getCballsLeft());
}
@Test
public void addNotMaxCannonballs()
public void testCannonInfoBox()
{
final ItemContainer inventory = mock(ItemContainer.class);
when(client.getItemContainer(InventoryID.INVENTORY)).thenReturn(inventory);
when(inventory.count(ItemID.GRANITE_CANNONBALL)).thenReturn(12);
when(config.showInfobox()).thenReturn(true);
plugin.onChatMessage(ADD_FURNACE);
ChatMessage chatMessage = new ChatMessage();
chatMessage.setType(ChatMessageType.SPAM);
chatMessage.setMessage("You add the furnace.");
plugin.onChatMessage(chatMessage);
assertTrue(plugin.isCannonPlaced());
assertEquals(0, plugin.getCballsLeft());
plugin.onChatMessage(loadCannonballs(12));
assertEquals(12, plugin.getCballsLeft());
verify(infoBoxManager).addInfoBox(any(CannonCounter.class));
}
@Test
public void addReclaimedCannonballs()
public void testThresholdNotificationShouldNotify()
{
final ItemContainer inventory = mock(ItemContainer.class);
when(client.getItemContainer(InventoryID.INVENTORY)).thenReturn(inventory);
when(inventory.count(ItemID.CANNONBALL)).thenReturn(1250);
when(config.showCannonNotifications()).thenReturn(true);
when(config.lowWarningThreshold()).thenReturn(10);
plugin.onChatMessage(ADD_FURNACE);
assertTrue(plugin.isCannonPlaced());
when(client.getVar(VarPlayer.CANNON_AMMO)).thenReturn(30);
plugin.onVarbitChanged(cannonAmmoChanged);
when(client.getVar(VarPlayer.CANNON_AMMO)).thenReturn(10);
plugin.onVarbitChanged(cannonAmmoChanged);
assertEquals(30, plugin.getCballsLeft());
plugin.onChatMessage(loadCannonballs(18));
assertEquals(30, plugin.getCballsLeft());
verify(notifier, times(1)).notify("Your cannon has 10 cannon balls remaining!");
}
private static ChatMessage loadCannonballs(final int numCannonballs)
@Test
public void testThresholdNotificationShouldNotifyOnce()
{
final ChatMessage message = new ChatMessage();
message.setType(ChatMessageType.GAMEMESSAGE);
when(config.showCannonNotifications()).thenReturn(true);
when(config.lowWarningThreshold()).thenReturn(10);
// Cannons use the same chat message for loading cannonballs regardless of whether they're normal or granite.
if (numCannonballs == 1)
for (int cballs = 15; cballs >= 8; --cballs)
{
message.setMessage("You load the cannon with one cannonball.");
}
else
{
message.setMessage(String.format("You load the cannon with %s cannonballs.", numCannonballs));
when(client.getVar(VarPlayer.CANNON_AMMO)).thenReturn(cballs);
plugin.onVarbitChanged(cannonAmmoChanged);
}
return message;
verify(notifier, times(1)).notify("Your cannon has 10 cannon balls remaining!");
}
@Test
public void testThresholdNotificationsShouldNotNotify()
{
when(config.showCannonNotifications()).thenReturn(true);
when(config.lowWarningThreshold()).thenReturn(0);
when(client.getVar(VarPlayer.CANNON_AMMO)).thenReturn(30);
plugin.onVarbitChanged(cannonAmmoChanged);
when(client.getVar(VarPlayer.CANNON_AMMO)).thenReturn(10);
plugin.onVarbitChanged(cannonAmmoChanged);
verify(notifier, never()).notify("Your cannon has 10 cannon balls remaining!");
}
@Test
public void testCannonOutOfAmmo()
{
when(config.showCannonNotifications()).thenReturn(true);
ChatMessage cannonOutOfAmmo = new ChatMessage(null, ChatMessageType.GAMEMESSAGE, "", "Your cannon is out of ammo!", "", 0);
plugin.onChatMessage(cannonOutOfAmmo);
verify(notifier, times(1)).notify("Your cannon is out of ammo!");
}
}

View File

@@ -453,4 +453,15 @@ public class ChatFilterPluginTest
chatFilterPlugin.onScriptCallbackEvent(event);
assertEquals(1, client.getIntStack()[client.getIntStackSize() - 3]); // not filtered
}
@Test
public void testLtGt()
{
when(chatFilterConfig.filteredWords()).thenReturn("f<ilte>r");
chatFilterPlugin.updateFilteredPatterns();
String message = chatFilterPlugin.censorMessage("Adam", "start f<lt>ilte<gt>r end");
assertEquals("start ******** end", message);
}
}

View File

@@ -32,7 +32,7 @@ import javax.inject.Named;
import net.runelite.api.Client;
import net.runelite.client.discord.DiscordPresence;
import net.runelite.client.discord.DiscordService;
import net.runelite.client.ws.PartyService;
import net.runelite.client.party.PartyService;
import static org.junit.Assert.assertEquals;
import org.junit.Before;
import org.junit.Test;

View File

@@ -0,0 +1,242 @@
/*
* Copyright (c) 2022, 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.plugins.entityhider;
import com.google.inject.Guice;
import com.google.inject.testing.fieldbinder.Bind;
import com.google.inject.testing.fieldbinder.BoundFieldModule;
import javax.inject.Inject;
import net.runelite.api.Client;
import net.runelite.api.Ignore;
import net.runelite.api.NPC;
import net.runelite.api.NPCComposition;
import net.runelite.api.NameableContainer;
import net.runelite.api.Player;
import net.runelite.client.callback.Hooks;
import net.runelite.client.events.ConfigChanged;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class EntityHiderPluginTest
{
@Inject
EntityHiderPlugin plugin;
@Mock
@Bind
Client client;
@Mock
@Bind
EntityHiderConfig config;
@Mock
@Bind
Hooks hooks;
@Mock
NameableContainer<Ignore> ignoreNameableContainer;
@Before
public void before()
{
Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this);
when(client.getIgnoreContainer()).thenReturn(ignoreNameableContainer);
}
@Test
public void testHideFriendsPositive()
{
when(config.hideOthers()).thenReturn(true);
when(config.hideFriends()).thenReturn(true);
ConfigChanged configChanged = new ConfigChanged();
configChanged.setGroup(EntityHiderConfig.GROUP);
plugin.onConfigChanged(configChanged);
Player player = mock(Player.class);
when(player.getName()).thenReturn("Adam");
when(player.isFriend()).thenReturn(true);
assertFalse(plugin.shouldDraw(player, false));
player = mock(Player.class);
when(player.getName()).thenReturn("Adam");
when(player.isFriend()).thenReturn(false);
assertFalse(plugin.shouldDraw(player, false));
}
@Test
public void testHideFriendsNegative()
{
when(config.hideOthers()).thenReturn(true);
ConfigChanged configChanged = new ConfigChanged();
configChanged.setGroup(EntityHiderConfig.GROUP);
plugin.onConfigChanged(configChanged);
Player player = mock(Player.class);
when(player.getName()).thenReturn("Adam");
when(player.isFriend()).thenReturn(false);
assertFalse(plugin.shouldDraw(player, false));
player = mock(Player.class);
when(player.getName()).thenReturn("Adam");
when(player.isFriend()).thenReturn(true);
assertTrue(plugin.shouldDraw(player, false));
}
@Test
public void testHideClansPositivie()
{
when(config.hideOthers()).thenReturn(true);
when(config.hideFriendsChatMembers()).thenReturn(true);
ConfigChanged configChanged = new ConfigChanged();
configChanged.setGroup(EntityHiderConfig.GROUP);
plugin.onConfigChanged(configChanged);
Player player = mock(Player.class);
when(player.getName()).thenReturn("Adam");
when(player.isFriendsChatMember()).thenReturn(true);
assertFalse(plugin.shouldDraw(player, false));
player = mock(Player.class);
when(player.getName()).thenReturn("Adam");
when(player.isFriendsChatMember()).thenReturn(false);
assertFalse(plugin.shouldDraw(player, false));
}
@Test
public void testHideClansNegative()
{
when(config.hideOthers()).thenReturn(true);
ConfigChanged configChanged = new ConfigChanged();
configChanged.setGroup(EntityHiderConfig.GROUP);
plugin.onConfigChanged(configChanged);
Player player = mock(Player.class);
when(player.getName()).thenReturn("Adam");
when(player.isFriendsChatMember()).thenReturn(false);
assertFalse(plugin.shouldDraw(player, false));
player = mock(Player.class);
when(player.getName()).thenReturn("Adam");
when(player.isFriendsChatMember()).thenReturn(true);
assertTrue(plugin.shouldDraw(player, false));
}
// hidenpc hideattacker hidden?
// t t t iif attacker would be hidden
// t f f
// f t t
// f f f
@Test
public void testHideAndAttacker()
{
when(config.hideNPCs2D()).thenReturn(true);
when(config.hideAttackers()).thenReturn(true);
ConfigChanged configChanged = new ConfigChanged();
configChanged.setGroup(EntityHiderConfig.GROUP);
plugin.onConfigChanged(configChanged);
NPCComposition composition = mock(NPCComposition.class);
NPC npc = mock(NPC.class);
when(npc.getComposition()).thenReturn(composition);
Player player = mock(Player.class);
when(client.getLocalPlayer()).thenReturn(player);
when(npc.getInteracting()).thenReturn(player);
assertFalse(plugin.shouldDraw(npc, true));
assertTrue(plugin.shouldDraw(npc, false));
}
@Test
public void testHideAndNoAttacker()
{
when(config.hideNPCs2D()).thenReturn(true);
ConfigChanged configChanged = new ConfigChanged();
configChanged.setGroup(EntityHiderConfig.GROUP);
plugin.onConfigChanged(configChanged);
NPCComposition composition = mock(NPCComposition.class);
NPC npc = mock(NPC.class);
when(npc.getComposition()).thenReturn(composition);
Player player = mock(Player.class);
when(client.getLocalPlayer()).thenReturn(player);
when(npc.getInteracting()).thenReturn(player);
assertTrue(plugin.shouldDraw(npc, true));
assertTrue(plugin.shouldDraw(npc, false));
}
@Test
public void testHideAttacker()
{
when(config.hideAttackers()).thenReturn(true);
ConfigChanged configChanged = new ConfigChanged();
configChanged.setGroup(EntityHiderConfig.GROUP);
plugin.onConfigChanged(configChanged);
NPCComposition composition = mock(NPCComposition.class);
NPC npc = mock(NPC.class);
when(npc.getComposition()).thenReturn(composition);
Player player = mock(Player.class);
when(client.getLocalPlayer()).thenReturn(player);
when(npc.getInteracting()).thenReturn(player);
assertFalse(plugin.shouldDraw(npc, true));
assertFalse(plugin.shouldDraw(npc, false));
}
}

View File

@@ -0,0 +1,156 @@
/*
* Copyright (c) 2019, Alexsuperfly <github.com/Alexsuperfly>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.client.plugins.raids;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.testing.fieldbinder.Bind;
import com.google.inject.testing.fieldbinder.BoundFieldModule;
import java.util.concurrent.ScheduledExecutorService;
import net.runelite.api.Client;
import net.runelite.client.Notifier;
import net.runelite.client.chat.ChatClient;
import net.runelite.client.config.ChatColorConfig;
import net.runelite.client.config.RuneLiteConfig;
import net.runelite.client.party.PartyService;
import net.runelite.client.ui.overlay.OverlayManager;
import net.runelite.client.ui.overlay.infobox.InfoBoxManager;
import net.runelite.client.util.ImageCapture;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class RaidsPluginTest
{
@Mock
@Bind
Client client;
@Mock
@Bind
ScheduledExecutorService executor;
@Mock
@Bind
ChatColorConfig chatColorConfig;
@Mock
@Bind
RuneLiteConfig runeliteConfig;
@Mock
@Bind
ImageCapture imageCapture;
@Mock
@Bind
Notifier notifier;
@Mock
@Bind
ChatClient chatClient;
@Mock
@Bind
InfoBoxManager infoBoxManager;
@Mock
@Bind
PartyService partyService;
@Mock
@Bind
OverlayManager overlayManager;
@Mock
@Bind
RaidsConfig raidsConfig;
@Mock
@Bind
RaidsOverlay raidsOverlay;
@Inject
RaidsPlugin raidsPlugin;
@Before
public void before()
{
Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this);
when(raidsConfig.whitelistedRooms()).thenReturn("");
when(raidsConfig.blacklistedRooms()).thenReturn("");
when(raidsConfig.whitelistedRotations()).thenReturn("");
when(raidsConfig.whitelistedLayouts()).thenReturn("");
}
@Test
public void testRotationWhitelist()
{
when(raidsConfig.whitelistedRotations()).thenReturn("Muttadiles, Tekton, Mystics");
raidsPlugin.updateLists();
final RaidRoom[] raidRooms = new RaidRoom[]{RaidRoom.MUTTADILES, RaidRoom.TEKTON, RaidRoom.MYSTICS};
Raid raid = mock(Raid.class);
when(raid.getCombatRooms()).thenReturn(raidRooms);
raidsPlugin.setRaid(raid);
assertTrue(raidsPlugin.getRotationMatches());
}
@Test
public void testRotationWhitelistMultiple()
{
when(raidsConfig.whitelistedRotations()).thenReturn("Vanguards, Vespula, Vasa \nMuttadiles, Tekton, Mystics");
raidsPlugin.updateLists();
final RaidRoom[] raidRooms = new RaidRoom[]{RaidRoom.MUTTADILES, RaidRoom.TEKTON, RaidRoom.MYSTICS};
Raid raid = mock(Raid.class);
when(raid.getCombatRooms()).thenReturn(raidRooms);
raidsPlugin.setRaid(raid);
assertTrue(raidsPlugin.getRotationMatches());
}
@Test
public void testRotationWhitelistBackwards()
{
when(raidsConfig.whitelistedRotations()).thenReturn("muttadiles, tekton, mystics");
raidsPlugin.updateLists();
final RaidRoom[] raidRooms = new RaidRoom[]{RaidRoom.MYSTICS, RaidRoom.TEKTON, RaidRoom.MUTTADILES};
Raid raid = mock(Raid.class);
when(raid.getCombatRooms()).thenReturn(raidRooms);
raidsPlugin.setRaid(raid);
assertFalse(raidsPlugin.getRotationMatches());
}
}

View File

@@ -49,8 +49,8 @@ import net.runelite.client.game.ItemManager;
import net.runelite.client.ui.overlay.OverlayManager;
import net.runelite.client.ui.overlay.infobox.InfoBoxManager;
import net.runelite.client.util.AsyncBufferedImage;
import net.runelite.client.ws.PartyService;
import net.runelite.client.ws.WSClient;
import net.runelite.client.party.PartyService;
import net.runelite.client.party.WSClient;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -115,6 +115,8 @@ public class SpecialCounterPluginTest
{
Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this);
when(specialCounterConfig.infobox()).thenReturn(true);
// Set up spec weapon
ItemContainer equipment = mock(ItemContainer.class);
when(equipment.getItem(EquipmentInventorySlot.WEAPON.getSlotIdx())).thenReturn(new Item(ItemID.BANDOS_GODSWORD, 1));