client: Async-ify or improve data loading methods (#1782)

client: Async-ify or improve data loading methods
This commit is contained in:
Owain van Brakel
2019-10-15 18:33:12 +02:00
committed by GitHub
33 changed files with 750 additions and 149992 deletions

File diff suppressed because one or more lines are too long

View File

@@ -9,6 +9,7 @@ dependencies {
compileOnly group: 'org.projectlombok', name: 'lombok', version: lombok
implementation group: 'com.google.code.gson', name: 'gson', version: gson
implementation group: 'com.google.guava', name: 'guava', version: guava
implementation group: 'com.squareup.okhttp3', name: 'okhttp', version: okhttp3
implementation group: 'io.reactivex.rxjava2', name: 'rxjava', version: rxjava
implementation group: 'org.apache.commons', name: 'commons-csv', version: apacheCommonsCsv

View File

@@ -26,6 +26,10 @@ package net.runelite.http.api;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import net.runelite.http.api.item.ItemEquipmentStats;
import net.runelite.http.api.item.ItemPrice;
import net.runelite.http.api.item.ItemStats;
import net.runelite.http.api.util.TypeAdapters;
import okhttp3.HttpUrl;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
@@ -37,7 +41,6 @@ import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
@@ -52,26 +55,33 @@ import java.util.concurrent.TimeUnit;
public class RuneLiteAPI
{
private static final Logger logger = LoggerFactory.getLogger(RuneLiteAPI.class);
public static final String RUNELITE_AUTH = "RUNELITE-AUTH";
public static final OkHttpClient CLIENT;
public static final Gson GSON = new GsonBuilder()
.setPrettyPrinting()
.registerTypeAdapter(ItemStats.class, TypeAdapters.ITEMSTATS)
.registerTypeAdapter(ItemEquipmentStats.class, TypeAdapters.EQUIPMENTSTATS)
.registerTypeAdapter(ItemPrice.class, TypeAdapters.ITEMPRICE)
.create();
private static final String BASE = "https://api.runelite.net";
private static final String WSBASE = "https://api.runelite.net/ws";
private static final String STATICBASE = "https://static.runelite.net";
private static final String OPENOSRS_BASE = /*"https://api.openosrs.com*/ "https://api.runelitepl.us";
private static final String OPENOSRS_SESSION = "https://session.openosrs.com";
private static final String MAVEN_METADATA = "http://repo.runelite.net/net/runelite/runelite-parent/maven-metadata.xml";
private static final Properties properties = new Properties();
private static String userAgent;
private static String version;
private static String upstreamVersion;
private static int rsVersion;
public static final String RUNELITE_AUTH = "RUNELITE-AUTH";
public static final OkHttpClient CLIENT;
public static final OkHttpClient RLP_CLIENT;
public static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
private static final Logger logger = LoggerFactory.getLogger(RuneLiteAPI.class);
private static final String BASE = "https://api.runelite.net";
private static final String OPENOSRS_BASE = /*"https://api.openosrs.com*/ "https://api.runelitepl.us";
private static final String OPENOSRS_SESSION = "https://session.openosrs.com";
private static final String WSBASE = "https://api.runelite.net/ws";
private static final String STATICBASE = "https://static.runelite.net";
private static final String MAVEN_METADATA =
"http://repo.runelite.net/net/runelite/runelite-parent/maven-metadata.xml";
private static final Properties properties = new Properties();
private static String rlUserAgent;
private static String openosrsUserAgent;
static
{
try
@@ -80,11 +90,11 @@ public class RuneLiteAPI
properties.load(in);
version = properties.getProperty("runelite.version");
String rlpCommit = properties.getProperty("runelite.commit");
String commit = properties.getProperty("runelite.commit");
boolean dirty = Boolean.parseBoolean(properties.getProperty("runelite.dirty"));
openosrsUserAgent = "openosrs/" + version + "-" + rlpCommit + (dirty ? "+" : "");
rlUserAgent = "openosrs/" + version;
userAgent = "OpenOSRS/" + version + "-" + commit + (dirty ? "+" : "");
rsVersion = Integer.parseInt(properties.getProperty("rs.version"));
parseMavenVersion();
@@ -101,8 +111,6 @@ public class RuneLiteAPI
CLIENT = new OkHttpClient.Builder()
.pingInterval(30, TimeUnit.SECONDS)
.connectTimeout(8655, TimeUnit.MILLISECONDS)
.writeTimeout(8655, TimeUnit.MILLISECONDS)
.addNetworkInterceptor(new Interceptor()
{
@Override
@@ -110,25 +118,7 @@ public class RuneLiteAPI
{
Request userAgentRequest = chain.request()
.newBuilder()
.header("User-Agent", rlUserAgent)
.build();
return chain.proceed(userAgentRequest);
}
})
.build();
RLP_CLIENT = new OkHttpClient.Builder()
.pingInterval(30, TimeUnit.SECONDS)
.writeTimeout(5655, TimeUnit.MILLISECONDS)
.connectTimeout(2655, TimeUnit.MILLISECONDS)
.addNetworkInterceptor(new Interceptor()
{
@Override
public Response intercept(Chain chain) throws IOException
{
Request userAgentRequest = chain.request()
.newBuilder()
.header("User-Agent", openosrsUserAgent)
.header("User-Agent", userAgent)
.build();
return chain.proceed(userAgentRequest);
}
@@ -136,7 +126,7 @@ public class RuneLiteAPI
.build();
}
public static HttpUrl getopenosrsSessionBase()
public static HttpUrl getSessionBase()
{
return HttpUrl.parse(OPENOSRS_SESSION);
}
@@ -153,7 +143,7 @@ public class RuneLiteAPI
return HttpUrl.parse(BASE + "/runelite-" + getVersion());
}
public static HttpUrl getPlusApiBase()
public static HttpUrl getOpenOSRSApiBase()
{
return HttpUrl.parse(OPENOSRS_BASE + "/http-service-" + getRlpVersion());
}
@@ -206,7 +196,7 @@ public class RuneLiteAPI
byte[] chunk = new byte[4096];
int bytesRead;
URLConnection conn = toDownload.openConnection();
conn.setRequestProperty("User-Agent", openosrsUserAgent);
conn.setRequestProperty("User-Agent", userAgent);
stream = conn.getInputStream();
while ((bytesRead = stream.read(chunk)) > 0)

View File

@@ -25,6 +25,7 @@
package net.runelite.http.api.account;
import com.google.gson.JsonParseException;
import io.reactivex.Observable;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -96,7 +97,7 @@ public class AccountClient
}
}
public boolean sessionCheck()
public Observable<Boolean> sessionCheck()
{
HttpUrl url = RuneLiteAPI.getApiBase().newBuilder()
.addPathSegment("account")
@@ -105,6 +106,8 @@ public class AccountClient
logger.debug("Built URI: {}", url);
return Observable.fromCallable(() ->
{
Request request = new Request.Builder()
.header(RuneLiteAPI.RUNELITE_AUTH, uuid.toString())
.url(url)
@@ -119,5 +122,6 @@ public class AccountClient
logger.debug("Unable to verify session", ex);
return true; // assume it is still valid if the server is unreachable
}
});
}
}

View File

@@ -51,7 +51,7 @@ public class AnimationClient
{
String json = RuneLiteAPI.GSON.toJson(animationRequest);
HttpUrl url = RuneLiteAPI.getPlusApiBase().newBuilder()
HttpUrl url = RuneLiteAPI.getOpenOSRSApiBase().newBuilder()
.addPathSegment("animation")
.build();
@@ -65,7 +65,7 @@ public class AnimationClient
try
{
try (Response response = RuneLiteAPI.RLP_CLIENT.newCall(request).execute())
try (Response response = RuneLiteAPI.CLIENT.newCall(request).execute())
{
logger.debug("animation response " + response.code());
}
@@ -75,7 +75,7 @@ public class AnimationClient
e.printStackTrace();
}
RuneLiteAPI.RLP_CLIENT.newCall(request).enqueue(new Callback()
RuneLiteAPI.CLIENT.newCall(request).enqueue(new Callback()
{
@Override
public void onFailure(Call call, IOException e)
@@ -103,7 +103,7 @@ public class AnimationClient
public List<AnimationKey> get() throws IOException
{
HttpUrl url = RuneLiteAPI.getPlusApiBase().newBuilder()
HttpUrl url = RuneLiteAPI.getOpenOSRSApiBase().newBuilder()
.addPathSegment("animation")
.build();
@@ -111,7 +111,7 @@ public class AnimationClient
.url(url)
.build();
try (Response response = RuneLiteAPI.RLP_CLIENT.newCall(request).execute())
try (Response response = RuneLiteAPI.CLIENT.newCall(request).execute())
{
InputStream in = response.body().byteStream();
// CHECKSTYLE:OFF
@@ -128,7 +128,7 @@ public class AnimationClient
public AnimationKey get(int npcid) throws IOException
{
HttpUrl url = RuneLiteAPI.getPlusApiBase().newBuilder()
HttpUrl url = RuneLiteAPI.getOpenOSRSApiBase().newBuilder()
.addPathSegment("animation")
.addPathSegment(Integer.toString(npcid))
.build();
@@ -137,7 +137,7 @@ public class AnimationClient
.url(url)
.build();
try (Response response = RuneLiteAPI.RLP_CLIENT.newCall(request).execute())
try (Response response = RuneLiteAPI.CLIENT.newCall(request).execute())
{
InputStream in = response.body().byteStream();
return RuneLiteAPI.GSON.fromJson(new InputStreamReader(in), AnimationKey.class);

View File

@@ -275,7 +275,7 @@ public class ChatClient
throw new IOException("Layout " + layout + " is not valid!");
}
HttpUrl url = RuneLiteAPI.getPlusApiBase().newBuilder()
HttpUrl url = RuneLiteAPI.getOpenOSRSApiBase().newBuilder()
.addPathSegment("chat")
.addPathSegment("layout")
.addQueryParameter("name", username)
@@ -287,7 +287,7 @@ public class ChatClient
.url(url)
.build();
try (Response response = RuneLiteAPI.RLP_CLIENT.newCall(request).execute())
try (Response response = RuneLiteAPI.CLIENT.newCall(request).execute())
{
return response.isSuccessful();
}
@@ -318,7 +318,7 @@ public class ChatClient
public String getLayout(String username) throws IOException
{
HttpUrl url = RuneLiteAPI.getPlusApiBase().newBuilder()
HttpUrl url = RuneLiteAPI.getOpenOSRSApiBase().newBuilder()
.addPathSegment("chat")
.addPathSegment("layout")
.addQueryParameter("name", username)
@@ -328,7 +328,7 @@ public class ChatClient
.url(url)
.build();
try (Response response = RuneLiteAPI.RLP_CLIENT.newCall(request).execute())
try (Response response = RuneLiteAPI.CLIENT.newCall(request).execute())
{
if (!response.isSuccessful())
{
@@ -353,7 +353,7 @@ public class ChatClient
public House[] getHosts(int world, String location) throws IOException
{
HttpUrl url = RuneLiteAPI.getPlusApiBase().newBuilder()
HttpUrl url = RuneLiteAPI.getOpenOSRSApiBase().newBuilder()
.addPathSegment("chat")
.addPathSegment("hosts")
.addQueryParameter("world", Integer.toString(world))
@@ -364,7 +364,7 @@ public class ChatClient
.url(url)
.build();
try (Response response = RuneLiteAPI.RLP_CLIENT.newCall(request).execute())
try (Response response = RuneLiteAPI.CLIENT.newCall(request).execute())
{
if (!response.isSuccessful())
{
@@ -411,7 +411,7 @@ public class ChatClient
public boolean submitHost(int world, String location, House house) throws IOException
{
HttpUrl url = RuneLiteAPI.getPlusApiBase().newBuilder()
HttpUrl url = RuneLiteAPI.getOpenOSRSApiBase().newBuilder()
.addPathSegment("chat")
.addPathSegment("hosts")
.addQueryParameter("world", Integer.toString(world))
@@ -431,7 +431,7 @@ public class ChatClient
.url(url)
.build();
try (Response response = RuneLiteAPI.RLP_CLIENT.newCall(request).execute())
try (Response response = RuneLiteAPI.CLIENT.newCall(request).execute())
{
return response.isSuccessful();
}
@@ -439,7 +439,7 @@ public class ChatClient
public boolean removeHost(int world, String location, House house) throws IOException
{
HttpUrl url = RuneLiteAPI.getPlusApiBase().newBuilder()
HttpUrl url = RuneLiteAPI.getOpenOSRSApiBase().newBuilder()
.addPathSegment("chat")
.addPathSegment("hosts")
.addQueryParameter("world", Integer.toString(world))
@@ -460,7 +460,7 @@ public class ChatClient
.url(url)
.build();
try (Response response = RuneLiteAPI.RLP_CLIENT.newCall(request).execute())
try (Response response = RuneLiteAPI.CLIENT.newCall(request).execute())
{
return response.isSuccessful();
}

View File

@@ -24,16 +24,15 @@
*/
package net.runelite.http.api.item;
import com.google.common.collect.ImmutableMap;
import com.google.gson.JsonParseException;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import io.reactivex.Observable;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Map;
import javax.imageio.ImageIO;
import javax.inject.Inject;
import net.runelite.http.api.RuneLiteAPI;
@@ -190,7 +189,7 @@ public class ItemClient
});
}
public Observable<ItemPrice[]> getPrices()
public Observable<ImmutableMap<Integer, ItemPrice>> getPrices()
{
HttpUrl.Builder urlBuilder = RuneLiteAPI.getApiBase().newBuilder()
.addPathSegment("item")
@@ -200,66 +199,64 @@ public class ItemClient
logger.debug("Built URI: {}", url);
return Observable.defer(() ->
return Observable.fromCallable(() ->
{
Request request = new Request.Builder()
.url(url)
.build();
try (Response response = client.newCall(request).execute())
try (JsonReader reader = new JsonReader(client.newCall(request).execute().body().charStream()))
{
if (!response.isSuccessful())
ImmutableMap.Builder<Integer, ItemPrice> builder = ImmutableMap.builderWithExpectedSize(3666);
reader.beginArray();
while (reader.hasNext())
{
logger.warn("Error looking up prices: {}", response);
return Observable.just(null);
ItemPrice price = RuneLiteAPI.GSON.fromJson(reader, ItemPrice.class);
builder.put(
price.getId(),
price
);
}
InputStream in = response.body().byteStream();
return Observable.just(RuneLiteAPI.GSON.fromJson(new InputStreamReader(in), ItemPrice[].class));
}
catch (JsonParseException ex)
{
return Observable.error(ex);
reader.endArray();
return builder.build();
}
});
}
public Observable<Map<Integer, ItemStats>> getStats()
public Observable<ImmutableMap<Integer, ItemStats>> getStats()
{
HttpUrl.Builder urlBuilder = RuneLiteAPI.getStaticBase().newBuilder()
HttpUrl url = RuneLiteAPI.getStaticBase()
.newBuilder()
.addPathSegment("item")
// TODO: Change this to stats.min.json later after release is undeployed
.addPathSegment("stats.ids.min.json");
.addPathSegment("stats.ids.min.json")
.build();
HttpUrl url = urlBuilder.build();
logger.debug("Built URI: {}", url);
return Observable.defer(() ->
logger.debug("Built URI {}", url);
return Observable.fromCallable(() ->
{
Request request = new Request.Builder()
.url(url)
.build();
try (Response response = client.newCall(request).execute())
try (JsonReader reader = new JsonReader(client.newCall(request).execute().body().charStream()))
{
if (!response.isSuccessful())
// This is the size the items are as I wrote this. the builder gets increased by 1 every time otherwise
ImmutableMap.Builder<Integer, ItemStats> builder = ImmutableMap.builderWithExpectedSize(7498);
reader.beginObject();
while (reader.hasNext())
{
logger.warn("Error looking up item stats: {}", response);
return Observable.just(null);
builder.put(
Integer.parseInt(reader.nextName()),
RuneLiteAPI.GSON.fromJson(reader, ItemStats.class)
);
}
InputStream in = response.body().byteStream();
final Type typeToken = new TypeToken<Map<Integer, ItemStats>>()
{
}.getType();
return Observable.just(RuneLiteAPI.GSON.fromJson(new InputStreamReader(in), typeToken));
}
catch (JsonParseException ex)
{
return Observable.error(ex);
reader.endObject();
return builder.build();
}
});
}

View File

@@ -28,7 +28,7 @@ import lombok.Builder;
import lombok.Value;
@Value
@Builder
@Builder(builderClassName = "Builder")
public class ItemEquipmentStats
{
private int slot;

View File

@@ -24,12 +24,15 @@
*/
package net.runelite.http.api.item;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
@Value
@AllArgsConstructor
@Builder(builderClassName = "Builder")
public class ItemStats
{
private String name;
private boolean quest;
private boolean equipable;
private double weight;
@@ -51,9 +54,9 @@ public class ItemStats
{
final ItemEquipmentStats equipment = this.equipment != null
? this.equipment
: new ItemEquipmentStats.ItemEquipmentStatsBuilder().build();
: new ItemEquipmentStats.Builder().build();
newEquipment = new ItemEquipmentStats.ItemEquipmentStatsBuilder()
newEquipment = new ItemEquipmentStats.Builder()
.slot(equipment.getSlot())
.astab(equipment.getAstab() - other.equipment.getAstab())
.aslash(equipment.getAslash() - other.equipment.getAslash())
@@ -77,7 +80,7 @@ public class ItemStats
newEquipment = equipment;
}
return new ItemStats(name, quest, equipable, newWeight, newEquipment);
return new ItemStats(quest, equipable, newWeight, newEquipment);
}
}

View File

@@ -0,0 +1,215 @@
/*
* Copyright (c) 2019, Lucas <https://github.com/lucwousin>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.http.api.util;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.time.Instant;
import net.runelite.http.api.item.ItemEquipmentStats;
import net.runelite.http.api.item.ItemPrice;
import net.runelite.http.api.item.ItemStats;
/**
* A class to put GSON TypeAdapters. These make just as fast the first time you
* deserialize something as it would otherwise be after creating these itself.
* Kinda funny actually, cause the first time probably matters the most, especially
* for jsons that only get deserialized once.
*/
public class TypeAdapters
{
public static final TypeAdapter<ItemStats> ITEMSTATS = new TypeAdapter<ItemStats>()
{
@Deprecated
@Override
public void write(JsonWriter out, ItemStats value)
{
throw new UnsupportedOperationException("Not supported");
}
@Override
public ItemStats read(JsonReader in) throws IOException
{
in.beginObject();
boolean quest = false;
boolean equip = false;
double weight = 0;
ItemEquipmentStats stats = null;
while (in.peek() != JsonToken.END_OBJECT)
{
switch (in.nextName())
{
case "quest":
quest = in.nextBoolean();
break;
case "equipable":
equip = in.nextBoolean();
break;
case "weight":
weight = in.nextDouble();
break;
case "equipment":
stats = EQUIPMENTSTATS.read(in);
break;
}
}
in.endObject();
return new ItemStats(quest, equip, weight, stats);
}
};
public static final TypeAdapter<ItemEquipmentStats> EQUIPMENTSTATS = new TypeAdapter<ItemEquipmentStats>()
{
@Deprecated
@Override
public void write(JsonWriter out, ItemEquipmentStats value)
{
throw new UnsupportedOperationException("Not supported");
}
@Override
public ItemEquipmentStats read(JsonReader in) throws IOException
{
ItemEquipmentStats.Builder builder = ItemEquipmentStats.builder();
in.beginObject();
while (in.peek() != JsonToken.END_OBJECT)
{
String name = in.nextName();
int val = in.nextInt();
switch (name)
{
case "slot":
builder.slot(val);
break;
case "astab":
builder.astab(val);
break;
case "aslash":
builder.aslash(val);
break;
case "acrush":
builder.acrush(val);
break;
case "amagic":
builder.amagic(val);
break;
case "arange":
builder.arange(val);
break;
case "dstab":
builder.dstab(val);
break;
case "dslash":
builder.dslash(val);
break;
case "dcrush":
builder.dcrush(val);
break;
case "dmagic":
builder.dmagic(val);
break;
case "drange":
builder.drange(val);
break;
case "str":
builder.str(val);
break;
case "rstr":
builder.rstr(val);
break;
case "mdmg":
builder.mdmg(val);
break;
case "prayer":
builder.prayer(val);
break;
case "aspeed":
builder.aspeed(val);
break;
}
}
in.endObject();
return builder.build();
}
};
public static final TypeAdapter<ItemPrice> ITEMPRICE = new TypeAdapter<ItemPrice>()
{
@Override
public void write(JsonWriter out, ItemPrice value)
{
throw new UnsupportedOperationException("Not supported");
}
@Override
public ItemPrice read(JsonReader in) throws IOException
{
/*
* The ItemPrice json hosted by runelite is 'perfect'
* by that I mean every field always exists, even with value 0.
* This is why we can skip names and known-0 values
*/
ItemPrice ret = new ItemPrice();
in.beginObject();
// ID
in.skipValue();
ret.setId(in.nextInt());
// Name
in.skipValue();
ret.setName(in.nextString());
// Price
in.skipValue();
ret.setPrice(in.nextInt());
// Time
in.skipValue();
in.beginObject();
// Secs
in.skipValue();
ret.setTime(Instant.ofEpochSecond(in.nextLong()));
// Nanos
in.skipValue();
in.skipValue();
in.endObject();
in.endObject();
return ret;
}
};
}

View File

@@ -51,7 +51,7 @@ public class XteaClient
{
String json = RuneLiteAPI.GSON.toJson(xteaRequest);
HttpUrl url = RuneLiteAPI.getPlusApiBase().newBuilder()
HttpUrl url = RuneLiteAPI.getOpenOSRSApiBase().newBuilder()
.addPathSegment("xtea")
.build();
@@ -63,7 +63,7 @@ public class XteaClient
.url(url)
.build();
RuneLiteAPI.RLP_CLIENT.newCall(request).enqueue(new Callback()
RuneLiteAPI.CLIENT.newCall(request).enqueue(new Callback()
{
@Override
public void onFailure(Call call, IOException e)
@@ -91,7 +91,7 @@ public class XteaClient
public List<XteaKey> get() throws IOException
{
HttpUrl url = RuneLiteAPI.getPlusApiBase().newBuilder()
HttpUrl url = RuneLiteAPI.getOpenOSRSApiBase().newBuilder()
.addPathSegment("xtea")
.build();
@@ -116,7 +116,7 @@ public class XteaClient
public XteaKey get(int region) throws IOException
{
HttpUrl url = RuneLiteAPI.getPlusApiBase().newBuilder()
HttpUrl url = RuneLiteAPI.getOpenOSRSApiBase().newBuilder()
.addPathSegment("xtea")
.addPathSegment(Integer.toString(region))
.build();

View File

@@ -24,77 +24,78 @@
*/
package net.runelite.client;
import java.io.IOException;
import io.reactivex.schedulers.Schedulers;
import java.time.temporal.ChronoUnit;
import java.util.UUID;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j;
import net.runelite.client.callback.ClientThread;
import net.runelite.client.task.Schedule;
@Singleton
@Slf4j
public class ClientSessionManager
{
private final SessionClient sessionClient = new SessionClient(this);
private final ScheduledExecutorService executorService;
private ScheduledFuture<?> scheduledFuture;
private final SessionClient sessionClient;
private final ClientThread clientThread;
private UUID sessionId;
@Inject
ClientSessionManager(ScheduledExecutorService executorService)
ClientSessionManager(ClientThread clientThread)
{
this.executorService = executorService;
this.sessionClient = new SessionClient();
this.clientThread = clientThread;
}
public void start()
void start()
{
sessionClient.open();
scheduledFuture = executorService.scheduleWithFixedDelay(this::ping, 1, 10, TimeUnit.MINUTES);
sessionClient.openSession()
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.from(clientThread))
.subscribe(this::setUuid, this::error);
}
void setUuid(UUID uuid)
@Schedule(period = 10, unit = ChronoUnit.MINUTES, asynchronous = true)
private void ping()
{
this.sessionId = uuid;
log.debug("Opened session {}", sessionId);
if (sessionId == null)
{
start();
return;
}
void error(IOException e)
{
log.warn("Client session error, resetting UUID", e.getCause());
sessionId = null;
sessionClient.pingSession(sessionId)
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.from(clientThread))
.doOnError(e -> this.error((Throwable) e))
.subscribe();
}
public void shutdown()
{
if (sessionId != null)
{
try
{
sessionClient.delete(sessionId);
}
catch (IOException ex)
{
log.warn(null, ex);
}
sessionClient.delete(sessionId)
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.from(clientThread))
.doOnError(e -> this.error((Throwable) e))
.subscribe();
sessionId = null;
}
scheduledFuture.cancel(true);
}
private void ping()
private void setUuid(UUID uuid)
{
if (sessionId == null)
{
sessionClient.open();
return;
this.sessionId = uuid;
log.debug("Opened session {}.", sessionId);
}
sessionClient.ping(sessionId);
private void error(Throwable error)
{
log.debug("Error in client session.");
log.trace(null, error);
}
}

View File

@@ -375,6 +375,7 @@ public class RuneLite
if (this.client != null)
{
scheduler.registerObject(modelOutlineRenderer.get());
scheduler.registerObject(clientSessionManager);
}
// Close the splash screen

View File

@@ -24,106 +24,68 @@
*/
package net.runelite.client;
import com.google.gson.JsonParseException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import io.reactivex.Observable;
import java.util.UUID;
import net.runelite.http.api.RuneLiteAPI;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.HttpUrl;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
import org.jetbrains.annotations.NotNull;
class SessionClient
{
private final ClientSessionManager manager;
SessionClient(ClientSessionManager manager)
Observable<UUID> openSession()
{
this.manager = manager;
}
final HttpUrl url = RuneLiteAPI.getSessionBase();
void open()
return Observable.fromCallable(() ->
{
HttpUrl url = RuneLiteAPI.getopenosrsSessionBase().newBuilder()
.build();
Request request = new Request.Builder()
.url(url)
.build();
RuneLiteAPI.CLIENT.newCall(request).enqueue(new Callback()
try (Response response = RuneLiteAPI.CLIENT.newCall(request).execute())
{
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e)
{
manager.error(e);
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response)
{
try
{
ResponseBody body = response.body();
InputStream in = body.byteStream();
manager.setUuid(RuneLiteAPI.GSON.fromJson(new InputStreamReader(in), UUID.class));
}
catch (JsonParseException | IllegalArgumentException ex) // UUID.fromString can throw IllegalArgumentException
{
manager.error(new IOException(ex));
}
return RuneLiteAPI.GSON.fromJson(response.body().string(), UUID.class);
}
});
}
void ping(UUID uuid)
Observable pingSession(UUID uuid)
{
HttpUrl url = RuneLiteAPI.getopenosrsSessionBase().newBuilder()
final HttpUrl url = RuneLiteAPI.getSessionBase().newBuilder()
.addPathSegment("ping")
.addQueryParameter("session", uuid.toString())
.build();
return Observable.defer(() ->
{
Request request = new Request.Builder()
.url(url)
.build();
RuneLiteAPI.CLIENT.newCall(request).enqueue(new Callback()
{
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e)
{
manager.error(e);
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response)
try (Response response = RuneLiteAPI.CLIENT.newCall(request).execute())
{
if (!response.isSuccessful())
{
manager.error(new IOException("Failed ping"));
}
return Observable.empty();
}
});
}
void delete(UUID uuid) throws IOException
Observable delete(UUID uuid)
{
HttpUrl url = RuneLiteAPI.getopenosrsSessionBase().newBuilder()
final HttpUrl url = RuneLiteAPI.getSessionBase().newBuilder()
.addQueryParameter("session", uuid.toString())
.build();
return Observable.defer(() ->
{
Request request = new Request.Builder()
.delete()
.url(url)
.build();
RuneLiteAPI.CLIENT.newCall(request).execute().close();
return Observable.empty();
});
}
}

View File

@@ -25,6 +25,7 @@
package net.runelite.client.account;
import com.google.gson.Gson;
import io.reactivex.schedulers.Schedulers;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
@@ -37,7 +38,6 @@ import javax.inject.Singleton;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.runelite.client.RuneLite;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.events.SessionClose;
import net.runelite.client.events.SessionOpen;
@@ -57,13 +57,11 @@ public class SessionManager
private AccountSession accountSession;
private final EventBus eventBus;
private final ConfigManager configManager;
private final WSClient wsClient;
@Inject
private SessionManager(ConfigManager configManager, EventBus eventBus, WSClient wsClient)
private SessionManager(EventBus eventBus, WSClient wsClient)
{
this.configManager = configManager;
this.eventBus = eventBus;
this.wsClient = wsClient;
@@ -94,14 +92,27 @@ public class SessionManager
// Check if session is still valid
AccountClient accountClient = new AccountClient(session.getUuid());
if (!accountClient.sessionCheck())
accountClient.sessionCheck()
.subscribeOn(Schedulers.io())
.subscribe(b ->
{
if (!b)
{
log.debug("Loaded session {} is invalid", session.getUuid());
return;
}
else
{
openSession(session, false);
}
}, ex ->
{
if (ex instanceof IOException)
{
log.debug("Unable to verify session", ex);
openSession(session, false);
}
});
}
private void saveSession()
{
@@ -143,13 +154,6 @@ public class SessionManager
accountSession = session;
if (session.getUsername() != null)
{
// Initialize config for new session
// If the session isn't logged in yet, don't switch to the new config
configManager.switchSession();
}
eventBus.post(SessionOpen.class, new SessionOpen());
}
@@ -176,9 +180,6 @@ public class SessionManager
accountSession = null; // No more account
// Restore config
configManager.switchSession();
eventBus.post(SessionClose.class, new SessionClose());
}

View File

@@ -28,14 +28,10 @@ import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableMap;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import io.reactivex.Completable;
import io.reactivex.schedulers.Schedulers;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -137,12 +133,11 @@ public class ItemManager
private final Client client;
private final ClientThread clientThread;
private final ItemClient itemClient;
private final ImmutableMap<Integer, ItemStats> itemStatMap;
private final LoadingCache<ImageKey, AsyncBufferedImage> itemImages;
private final LoadingCache<Integer, ItemDefinition> itemDefinitions;
private final LoadingCache<OutlineKey, BufferedImage> itemOutlines;
private Map<Integer, ItemPrice> itemPrices = Collections.emptyMap();
private Map<Integer, ItemStats> itemStats = Collections.emptyMap();
private ImmutableMap<Integer, ItemStats> itemStats = ImmutableMap.of();
@Inject
public ItemManager(
@@ -166,7 +161,7 @@ public class ItemManager
.build(new CacheLoader<ImageKey, AsyncBufferedImage>()
{
@Override
public AsyncBufferedImage load(@NotNull ImageKey key) throws Exception
public AsyncBufferedImage load(@NotNull ImageKey key)
{
return loadImage(key.itemId, key.itemQuantity, key.stackable);
}
@@ -178,7 +173,7 @@ public class ItemManager
.build(new CacheLoader<Integer, ItemDefinition>()
{
@Override
public ItemDefinition load(@NotNull Integer key) throws Exception
public ItemDefinition load(@NotNull Integer key)
{
return client.getItemDefinition(key);
}
@@ -190,24 +185,21 @@ public class ItemManager
.build(new CacheLoader<OutlineKey, BufferedImage>()
{
@Override
public BufferedImage load(@NotNull OutlineKey key) throws Exception
public BufferedImage load(@NotNull OutlineKey key)
{
return loadItemOutline(key.itemId, key.itemQuantity, key.outlineColor);
}
});
final Gson gson = new Gson();
final Type typeToken = new TypeToken<Map<Integer, ItemStats>>()
{
}.getType();
final InputStream statsFile = getClass().getResourceAsStream("/item_stats.json");
final Map<Integer, ItemStats> stats = gson.fromJson(new InputStreamReader(statsFile), typeToken);
itemStatMap = ImmutableMap.copyOf(stats);
eventbus.subscribe(GameStateChanged.class, this, this::onGameStateChanged);
eventbus.subscribe(PostItemDefinition.class, this, this::onPostItemDefinition);
Completable.fromAction(ItemVariationMapping::load)
.subscribeOn(Schedulers.computation())
.subscribe(
() -> log.debug("Loaded {} item variations", ItemVariationMapping.getSize()),
ex -> log.warn("Error loading item variations", ex)
);
}
private void loadPrices()
@@ -215,21 +207,9 @@ public class ItemManager
itemClient.getPrices()
.subscribeOn(Schedulers.io())
.subscribe(
(prices) ->
{
if (prices != null)
{
ImmutableMap.Builder<Integer, ItemPrice> map = ImmutableMap.builderWithExpectedSize(prices.length);
for (ItemPrice price : prices)
{
map.put(price.getId(), price);
}
itemPrices = map.build();
}
log.debug("Loaded {} prices", itemPrices.size());
},
(e) -> log.warn("error loading prices!", e)
m -> itemPrices = m,
e -> log.warn("Error loading prices", e),
() -> log.debug("Loaded {} prices", itemPrices.size())
);
}
@@ -238,16 +218,9 @@ public class ItemManager
itemClient.getStats()
.subscribeOn(Schedulers.io())
.subscribe(
(stats) ->
{
if (stats != null)
{
itemStats = ImmutableMap.copyOf(stats);
}
log.debug("Loaded {} stats", itemStats.size());
},
(e) -> log.warn("error loading stats!", e)
m -> itemStats = m,
e -> log.warn("Error fetching stats", e),
() -> log.debug("Loaded {} stats", itemStats.size())
);
}
@@ -376,12 +349,12 @@ public class ItemManager
{
ItemDefinition itemDefinition = getItemDefinition(itemId);
if (itemDefinition.getName() == null || !allowNote && itemDefinition.getNote() != -1)
if (!allowNote && itemDefinition.getNote() != -1)
{
return null;
}
return itemStatMap.get(canonicalize(itemId));
return itemStats.get(canonicalize(itemId));
}
/**

View File

@@ -1,27 +0,0 @@
package net.runelite.client.game;
import lombok.Value;
@Value
public class ItemStat
{
private int slot;
private int astab;
private int aslash;
private int acrush;
private int amagic;
private int arange;
private int dstab;
private int dslash;
private int dcrush;
private int dmagic;
private int drange;
private int str;
private int rstr;
private int mdmg;
private int prayer;
private int aspeed;
}

View File

@@ -26,44 +26,20 @@
package net.runelite.client.game;
import com.google.common.collect.ImmutableMap;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.io.InputStream;
import com.google.gson.stream.JsonReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Collection;
import java.util.Iterator;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
/**
* Converts variation items to it's base item counterparts
*/
@Slf4j
public class ItemVariationMapping
{
private static final Map<Integer, Integer> MAPPINGS;
static
{
final Gson gson = new Gson();
final TypeToken<Map<String, Collection<Integer>>> typeToken = new TypeToken<Map<String, Collection<Integer>>>()
{
};
final InputStream geLimitData = ItemVariationMapping.class.getResourceAsStream("/item_variations.json");
final Map<String, Collection<Integer>> itemVariations = gson.fromJson(new InputStreamReader(geLimitData), typeToken.getType());
ImmutableMap.Builder<Integer, Integer> builder = new ImmutableMap.Builder<>();
for (Collection<Integer> value : itemVariations.values())
{
final Iterator<Integer> iterator = value.iterator();
final int base = iterator.next();
while (iterator.hasNext())
{
builder.put(iterator.next(), base);
}
}
MAPPINGS = builder.build();
}
private static Map<Integer, Integer> MAPPINGS;
/**
* Get base item id for provided variation item id.
@@ -75,4 +51,39 @@ public class ItemVariationMapping
{
return MAPPINGS.getOrDefault(itemId, itemId);
}
static void load() throws IOException
{
try (JsonReader reader = new JsonReader(new InputStreamReader(ItemVariationMapping.class.getResourceAsStream("/item_variations.json"), StandardCharsets.UTF_8)))
{
ImmutableMap.Builder<Integer, Integer> builder = ImmutableMap.builderWithExpectedSize(5039);
reader.beginObject();
while (reader.hasNext())
{
// Names are useless
reader.skipValue();
reader.beginArray();
int base = reader.nextInt();
while (reader.hasNext())
{
builder.put(
reader.nextInt(),
base
);
}
reader.endArray();
}
reader.endObject();
MAPPINGS = builder.build();
}
}
static int getSize()
{
return MAPPINGS.size();
}
}

View File

@@ -26,12 +26,12 @@
package net.runelite.client.game;
import com.google.common.collect.ImmutableMap;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.io.InputStream;
import com.google.gson.stream.JsonReader;
import io.reactivex.Completable;
import io.reactivex.schedulers.Schedulers;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Type;
import java.util.Map;
import java.nio.charset.StandardCharsets;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -41,20 +41,37 @@ import lombok.extern.slf4j.Slf4j;
@Singleton
public class NPCManager
{
private final ImmutableMap<Integer, NPCStats> statsMap;
private ImmutableMap<Integer, NPCStats> statsMap;
@Inject
private NPCManager()
{
final Gson gson = new Gson();
Completable.fromAction(this::loadStats)
.subscribeOn(Schedulers.computation())
.subscribe(
() -> log.debug("Loaded {} NPC stats", statsMap.size()),
ex -> log.warn("Error loading NPC stats", ex)
);
}
final Type typeToken = new TypeToken<Map<Integer, NPCStats>>()
private void loadStats() throws IOException
{
}.getType();
try (JsonReader reader = new JsonReader(new InputStreamReader(NPCManager.class.getResourceAsStream("/npc_stats.json"), StandardCharsets.UTF_8)))
{
ImmutableMap.Builder<Integer, NPCStats> builder = ImmutableMap.builderWithExpectedSize(2821);
reader.beginObject();
final InputStream statsFile = getClass().getResourceAsStream("/npc_stats.json");
final Map<Integer, NPCStats> stats = gson.fromJson(new InputStreamReader(statsFile), typeToken);
statsMap = ImmutableMap.copyOf(stats);
while (reader.hasNext())
{
builder.put(
Integer.parseInt(reader.nextName()),
NPCStats.NPC_STATS_TYPE_ADAPTER.read(reader)
);
}
reader.endObject();
statsMap = builder.build();
}
}
/**

View File

@@ -24,9 +24,15 @@
*/
package net.runelite.client.game;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import lombok.Builder;
import lombok.Value;
@Value
@Builder(builderClassName = "Builder")
public class NPCStats
{
private final String name;
@@ -77,4 +83,120 @@ public class NPCStats
return (1 + Math.floor(averageLevel * (averageDefBonus + bonusStrength + bonusAttack) / 5120) / 40);
}
// Because this class is here we can't add the TypeAdapter to gson (easily)
// doesn't mean we can't use one to do it a bit quicker
public static final TypeAdapter<NPCStats> NPC_STATS_TYPE_ADAPTER = new TypeAdapter<NPCStats>()
{
@Override
public void write(JsonWriter out, NPCStats value)
{
throw new UnsupportedOperationException("Not supported");
}
@Override
public NPCStats read(JsonReader in) throws IOException
{
in.beginObject();
NPCStats.Builder builder = NPCStats.builder();
// Name is the only one that's guaranteed
in.skipValue();
builder.name(in.nextString());
while (in.hasNext())
{
switch (in.nextName())
{
case "hitpoints":
builder.hitpoints(in.nextInt());
break;
case "combatLevel":
builder.combatLevel(in.nextInt());
break;
case "slayerLevel":
builder.slayerLevel(in.nextInt());
break;
case "attackSpeed":
builder.attackSpeed(in.nextInt());
break;
case "attackLevel":
builder.attackLevel(in.nextInt());
break;
case "strengthLevel":
builder.strengthLevel(in.nextInt());
break;
case "defenceLevel":
builder.defenceLevel(in.nextInt());
break;
case "rangeLevel":
builder.rangeLevel(in.nextInt());
break;
case "magicLevel":
builder.magicLevel(in.nextInt());
break;
case "stab":
builder.stab(in.nextInt());
break;
case "slash":
builder.slash(in.nextInt());
break;
case "crush":
builder.crush(in.nextInt());
break;
case "range":
builder.range(in.nextInt());
break;
case "magic":
builder.magic(in.nextInt());
break;
case "stabDef":
builder.stabDef(in.nextInt());
break;
case "slashDef":
builder.slashDef(in.nextInt());
break;
case "crushDef":
builder.crushDef(in.nextInt());
break;
case "rangeDef":
builder.rangeDef(in.nextInt());
break;
case "magicDef":
builder.magicDef(in.nextInt());
break;
case "bonusAttack":
builder.bonusAttack(in.nextInt());
break;
case "bonusStrength":
builder.bonusStrength(in.nextInt());
break;
case "bonusRangeStrength":
builder.bonusRangeStrength(in.nextInt());
break;
case "bonusMagicDamage":
builder.bonusMagicDamage(in.nextInt());
break;
case "poisonImmune":
builder.poisonImmune(in.nextBoolean());
break;
case "venomImmune":
builder.venomImmune(in.nextBoolean());
break;
case "dragon":
builder.dragon(in.nextBoolean());
break;
case "demon":
builder.demon(in.nextBoolean());
break;
case "undead":
builder.undead(in.nextBoolean());
break;
}
}
in.endObject();
return builder.build();
}
};
}

View File

@@ -56,9 +56,9 @@ public class HighAlchemyOverlay extends WidgetItemOverlay
this.itemManager = itemManager;
this.plugin = plugin;
int natPrice = itemManager.getItemPrice(ItemID.NATURE_RUNE);
int natPrice = itemManager.getItemPrice(ItemID.NATURE_RUNE, true);
this.alchPrice = natPrice;
this.alchPriceNoStaff = natPrice + 5 * itemManager.getItemPrice(ItemID.FIRE_RUNE);
this.alchPriceNoStaff = natPrice + 5 * itemManager.getItemPrice(ItemID.FIRE_RUNE, true);
showOnBank();
showOnInventory();

View File

@@ -48,7 +48,7 @@ import net.runelite.http.api.item.ItemStats;
public class ItemStatOverlay extends Overlay
{
// Unarmed attack speed is 6
private static final ItemStats UNARMED = new ItemStats("", false, true, 0,
private static final ItemStats UNARMED = new ItemStats(false, true, 0,
ItemEquipmentStats.builder()
.aspeed(6)
.build());

View File

@@ -551,6 +551,7 @@ public class PlayerScouter extends Plugin
}
ItemStats item = itemManager.getItemStats(gear, false);
String name = itemManager.getItemDefinition(gear).getName();
if (item == null)
{
@@ -559,7 +560,7 @@ public class PlayerScouter extends Plugin
}
fieldList.add(FieldEmbed.builder()
.name(item.getName())
.name(name)
.value("Value: " + StackFormatter.quantityToRSDecimalStack(value))
.inline(true)
.build());

View File

@@ -77,10 +77,13 @@ public class StonedTrackerPlugin extends Plugin
@Inject
private ClientToolbar clientToolbar;
@Inject
public StonedTrackerConfig config;
@Inject
private Client client;
@Inject
private ItemManager itemManager;

View File

@@ -25,6 +25,7 @@
*/
package net.runelite.client.plugins.timetracking.farming;
import com.google.common.collect.ImmutableMap;
import com.google.inject.Singleton;
import java.util.Collections;
import java.util.Comparator;
@@ -42,7 +43,7 @@ import net.runelite.client.plugins.timetracking.Tab;
class FarmingWorld
{
@Getter
private Map<Integer, FarmingRegion> regions = new HashMap<>();
private final ImmutableMap<Integer, FarmingRegion> regions;
@Getter
private Map<Tab, Set<FarmingPatch>> tabs = new HashMap<>();
@@ -54,58 +55,60 @@ class FarmingWorld
FarmingWorld()
{
ImmutableMap.Builder<Integer, FarmingRegion> regionBuilder = ImmutableMap.builderWithExpectedSize(40);
// Some of these patches get updated in multiple regions.
// It may be worth it to add a specialization for these patches
add(new FarmingRegion("Al Kharid", 13106,
add(regionBuilder, new FarmingRegion("Al Kharid", 13106,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.CACTUS)
));
add(new FarmingRegion("Ardougne", 10290,
add(regionBuilder, new FarmingRegion("Ardougne", 10290,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.BUSH)
));
add(new FarmingRegion("Ardougne", 10548,
add(regionBuilder, new FarmingRegion("Ardougne", 10548,
new FarmingPatch("North", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT),
new FarmingPatch("South", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT),
new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER),
new FarmingPatch("", Varbits.FARMING_4774, PatchImplementation.HERB)
));
add(new FarmingRegion("Brimhaven", 11058,
add(regionBuilder, new FarmingRegion("Brimhaven", 11058,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.FRUIT_TREE),
new FarmingPatch("", Varbits.FARMING_4772, PatchImplementation.SPIRIT_TREE)
));
add(new FarmingRegion("Catherby", 11062,
add(regionBuilder, new FarmingRegion("Catherby", 11062,
new FarmingPatch("North", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT),
new FarmingPatch("South", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT),
new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER),
new FarmingPatch("", Varbits.FARMING_4774, PatchImplementation.HERB)
));
add(new FarmingRegion("Catherby", 11317,
add(regionBuilder, new FarmingRegion("Catherby", 11317,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.FRUIT_TREE)
));
add(new FarmingRegion("Champions' Guild", 12596,
add(regionBuilder, new FarmingRegion("Champions' Guild", 12596,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.BUSH)
));
add(new FarmingRegion("Draynor Manor", 12340,
add(regionBuilder, new FarmingRegion("Draynor Manor", 12340,
new FarmingPatch("Belladonna", Varbits.FARMING_4771, PatchImplementation.BELLADONNA)
));
add(new FarmingRegion("Entrana", 11060,
add(regionBuilder, new FarmingRegion("Entrana", 11060,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.HOPS)
));
add(new FarmingRegion("Etceteria", 10300,
add(regionBuilder, new FarmingRegion("Etceteria", 10300,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.BUSH),
new FarmingPatch("", Varbits.FARMING_4772, PatchImplementation.SPIRIT_TREE)
));
add(new FarmingRegion("Falador", 11828,
add(regionBuilder, new FarmingRegion("Falador", 11828,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.TREE)
));
add(new FarmingRegion("Falador", 12083,
add(regionBuilder, new FarmingRegion("Falador", 12083,
new FarmingPatch("North West", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT),
new FarmingPatch("South East", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT),
new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER),
@@ -119,36 +122,36 @@ class FarmingWorld
}
});
add(new FarmingRegion("Fossil Island", 14651,
add(regionBuilder, new FarmingRegion("Fossil Island", 14651,
new FarmingPatch("East", Varbits.FARMING_4771, PatchImplementation.HARDWOOD_TREE),
new FarmingPatch("Middle", Varbits.FARMING_4772, PatchImplementation.HARDWOOD_TREE),
new FarmingPatch("West", Varbits.FARMING_4773, PatchImplementation.HARDWOOD_TREE)
), 14907);
add(new FarmingRegion("Seaweed", 15008,
add(regionBuilder, new FarmingRegion("Seaweed", 15008,
new FarmingPatch("North", Varbits.FARMING_4771, PatchImplementation.SEAWEED),
new FarmingPatch("South", Varbits.FARMING_4772, PatchImplementation.SEAWEED)
));
add(new FarmingRegion("Gnome Stronghold", 9781,
add(regionBuilder, new FarmingRegion("Gnome Stronghold", 9781,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.TREE),
new FarmingPatch("", Varbits.FARMING_4772, PatchImplementation.FRUIT_TREE)
));
add(new FarmingRegion("Harmony", 15148,
add(regionBuilder, new FarmingRegion("Harmony", 15148,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT),
new FarmingPatch("", Varbits.FARMING_4772, PatchImplementation.HERB)
));
add(new FarmingRegion("Kourend", 6967,
add(regionBuilder, new FarmingRegion("Kourend", 6967,
new FarmingPatch("North East", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT),
new FarmingPatch("South West", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT),
new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER),
new FarmingPatch("", Varbits.FARMING_4774, PatchImplementation.HERB)
));
add(new FarmingRegion("Kourend", 6711,
add(regionBuilder, new FarmingRegion("Kourend", 6711,
new FarmingPatch("", Varbits.FARMING_7904, PatchImplementation.SPIRIT_TREE)
));
add(new FarmingRegion("Kourend", 7223,
add(regionBuilder, new FarmingRegion("Kourend", 7223,
new FarmingPatch("East 1", Varbits.GRAPES_4953, PatchImplementation.GRAPES),
new FarmingPatch("East 2", Varbits.GRAPES_4954, PatchImplementation.GRAPES),
new FarmingPatch("East 3", Varbits.GRAPES_4955, PatchImplementation.GRAPES),
@@ -163,21 +166,21 @@ class FarmingWorld
new FarmingPatch("West 6", Varbits.GRAPES_4964, PatchImplementation.GRAPES)
));
add(new FarmingRegion("Lletya", 9265,
add(regionBuilder, new FarmingRegion("Lletya", 9265,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.FRUIT_TREE)
));
add(new FarmingRegion("Lumbridge", 12851,
add(regionBuilder, new FarmingRegion("Lumbridge", 12851,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.HOPS)
));
add(new FarmingRegion("Lumbridge", 12594,
add(regionBuilder, new FarmingRegion("Lumbridge", 12594,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.TREE)
));
add(new FarmingRegion("Morytania", 13622,
add(regionBuilder, new FarmingRegion("Morytania", 13622,
new FarmingPatch("Mushroom", Varbits.FARMING_4771, PatchImplementation.MUSHROOM)
));
add(new FarmingRegion("Morytania", 14391,
add(regionBuilder, new FarmingRegion("Morytania", 14391,
new FarmingPatch("North West", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT),
new FarmingPatch("South East", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT),
new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER),
@@ -185,51 +188,51 @@ class FarmingWorld
));
add(new FarmingRegion("Port Sarim", 12082,
add(regionBuilder, new FarmingRegion("Port Sarim", 12082,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.SPIRIT_TREE)
));
add(new FarmingRegion("Rimmington", 11570,
add(regionBuilder, new FarmingRegion("Rimmington", 11570,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.BUSH)
), 11826);
add(new FarmingRegion("Seers' Village", 10551,
add(regionBuilder, new FarmingRegion("Seers' Village", 10551,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.HOPS)
));
add(new FarmingRegion("Tai Bwo Wannai", 11056,
add(regionBuilder, new FarmingRegion("Tai Bwo Wannai", 11056,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.CALQUAT)
));
add(new FarmingRegion("Taverley", 11573,
add(regionBuilder, new FarmingRegion("Taverley", 11573,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.TREE)
));
add(new FarmingRegion("Tree Gnome Village", 9777,
add(regionBuilder, new FarmingRegion("Tree Gnome Village", 9777,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.FRUIT_TREE)
));
add(new FarmingRegion("Troll Stronghold", 11321,
add(regionBuilder, new FarmingRegion("Troll Stronghold", 11321,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.HERB)
));
add(new FarmingRegion("Varrock", 12854,
add(regionBuilder, new FarmingRegion("Varrock", 12854,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.TREE)
), 12853);
add(new FarmingRegion("Yanille", 10288,
add(regionBuilder, new FarmingRegion("Yanille", 10288,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.HOPS)
));
add(new FarmingRegion("Weiss", 11325,
add(regionBuilder, new FarmingRegion("Weiss", 11325,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.HERB)
));
add(new FarmingRegion("Farming Guild", 5021,
add(regionBuilder, new FarmingRegion("Farming Guild", 5021,
new FarmingPatch("Hespori", Varbits.FARMING_7908, PatchImplementation.HESPORI)
));
add(new FarmingRegion("Farming Guild", 4922,
add(regionBuilder, new FarmingRegion("Farming Guild", 4922,
new FarmingPatch("", Varbits.FARMING_7905, PatchImplementation.TREE),
new FarmingPatch("", Varbits.FARMING_4775, PatchImplementation.HERB),
new FarmingPatch("", Varbits.FARMING_4772, PatchImplementation.BUSH),
@@ -244,7 +247,7 @@ class FarmingWorld
new FarmingPatch("", Varbits.FARMING_7907, PatchImplementation.REDWOOD)
));
add(new FarmingRegion("Prifddinas", 13151,
add(regionBuilder, new FarmingRegion("Prifddinas", 13151,
new FarmingPatch("North", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT),
new FarmingPatch("South", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT),
new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER),
@@ -252,7 +255,8 @@ class FarmingWorld
));
// Finalize
this.regions = Collections.unmodifiableMap(regions);
this.regions = regionBuilder.build();
Map<Tab, Set<FarmingPatch>> umtabs = new TreeMap<>();
for (Map.Entry<Tab, Set<FarmingPatch>> e : tabs.entrySet())
{
@@ -261,12 +265,12 @@ class FarmingWorld
this.tabs = Collections.unmodifiableMap(umtabs);
}
private void add(FarmingRegion r, int... extraRegions)
private void add(ImmutableMap.Builder<Integer, FarmingRegion> builder, FarmingRegion r, int... extraRegions)
{
regions.put(r.getRegionID(), r);
builder.put(r.getRegionID(), r);
for (int er : extraRegions)
{
regions.put(er, r);
builder.put(er, r);
}
for (FarmingPatch p : r.getPatches())
{

View File

@@ -25,8 +25,10 @@
*/
package net.runelite.client.plugins.worldmap;
import java.awt.image.BufferedImage;
import lombok.Getter;
import net.runelite.api.coords.WorldPoint;
import net.runelite.client.util.ImageUtil;
@Getter
enum TeleportLocationData
@@ -188,6 +190,24 @@ enum TeleportLocationData
private final String tooltip;
private final WorldPoint location;
private final String iconPath;
private BufferedImage image;
BufferedImage getImage()
{
if (image == null)
{
try
{
image = ImageUtil.getResourceStreamFromClass(WorldMapPlugin.class, this.getIconPath());
}
catch (RuntimeException e)
{
return WorldMapPlugin.BLANK_ICON;
}
}
return image;
}
TeleportLocationData(TeleportType type, String destination, int magicLevel, WorldPoint location, String iconPath)
{

View File

@@ -26,14 +26,12 @@
package net.runelite.client.plugins.worldmap;
import net.runelite.client.ui.overlay.worldmap.WorldMapPoint;
import net.runelite.client.util.ImageUtil;
class TeleportPoint extends WorldMapPoint
{
TeleportPoint(TeleportLocationData data)
{
super(data.getLocation(), WorldMapPlugin.BLANK_ICON);
super(data.getLocation(), data.getImage());
setTooltip(data.getTooltip());
setImage(ImageUtil.getResourceStreamFromClass(WorldMapPlugin.class, data.getIconPath()));
}
}

View File

@@ -29,6 +29,7 @@ import com.google.inject.Inject;
import com.google.inject.Provides;
import java.awt.image.BufferedImage;
import java.util.Arrays;
import java.util.concurrent.ScheduledExecutorService;
import net.runelite.api.Client;
import net.runelite.api.Experience;
import net.runelite.api.GameState;
@@ -127,6 +128,9 @@ public class WorldMapPlugin extends Plugin
@Inject
private EventBus eventBus;
@Inject
private ScheduledExecutorService executor;
private int agilityLevel = 0;
private int woodcuttingLevel = 0;
@@ -308,6 +312,8 @@ public class WorldMapPlugin extends Plugin
}
worldMapPointManager.removeIf(TeleportPoint.class::isInstance);
// This next part gets 142 icons from disk, and does so on the EDT (at first run)
executor.submit(() ->
Arrays.stream(TeleportLocationData.values())
.filter(data ->
{
@@ -331,7 +337,8 @@ public class WorldMapPlugin extends Plugin
return false;
}
}).map(TeleportPoint::new)
.forEach(worldMapPointManager::add);
.forEach(worldMapPointManager::add)
);
}
private void updateQuestStartPointIcons()

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -340,8 +340,10 @@ public class ItemVariationMappingTest
}};
@Test
public void testMappedNames()
public void testMappedNames() throws Exception
{
ItemVariationMapping.load();
ITEMS_MAP.forEach((key, value) ->
{
assertEquals(value, (Integer) ItemVariationMapping.map(key));