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

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()
{
HttpUrl url = RuneLiteAPI.getopenosrsSessionBase().newBuilder()
.build();
Request request = new Request.Builder()
.url(url)
.build();
RuneLiteAPI.CLIENT.newCall(request).enqueue(new Callback()
return Observable.fromCallable(() ->
{
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e)
Request request = new Request.Builder()
.url(url)
.build();
try (Response response = RuneLiteAPI.CLIENT.newCall(request).execute())
{
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();
Request request = new Request.Builder()
.url(url)
.build();
RuneLiteAPI.CLIENT.newCall(request).enqueue(new Callback()
return Observable.defer(() ->
{
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e)
{
manager.error(e);
}
Request request = new Request.Builder()
.url(url)
.build();
@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();
Request request = new Request.Builder()
.delete()
.url(url)
.build();
return Observable.defer(() ->
{
Request request = new Request.Builder()
.delete()
.url(url)
.build();
RuneLiteAPI.CLIENT.newCall(request).execute().close();
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,13 +92,26 @@ public class SessionManager
// Check if session is still valid
AccountClient accountClient = new AccountClient(session.getUuid());
if (!accountClient.sessionCheck())
{
log.debug("Loaded session {} is invalid", session.getUuid());
return;
}
openSession(session, false);
accountClient.sessionCheck()
.subscribeOn(Schedulers.io())
.subscribe(b ->
{
if (!b)
{
log.debug("Loaded session {} is invalid", session.getUuid());
}
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
{
try (JsonReader reader = new JsonReader(new InputStreamReader(NPCManager.class.getResourceAsStream("/npc_stats.json"), StandardCharsets.UTF_8)))
{
}.getType();
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,30 +312,33 @@ public class WorldMapPlugin extends Plugin
}
worldMapPointManager.removeIf(TeleportPoint.class::isInstance);
Arrays.stream(TeleportLocationData.values())
.filter(data ->
{
switch (data.getType())
// 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 ->
{
case NORMAL_MAGIC:
return this.normalTeleportIcon;
case ANCIENT_MAGICKS:
return this.ancientTeleportIcon;
case LUNAR_MAGIC:
return this.lunarTeleportIcon;
case ARCEUUS_MAGIC:
return this.arceuusTeleportIcon;
case JEWELLERY:
return this.jewelleryTeleportIcon;
case SCROLL:
return this.scrollTeleportIcon;
case OTHER:
return this.miscellaneousTeleportIcon;
default:
return false;
}
}).map(TeleportPoint::new)
.forEach(worldMapPointManager::add);
switch (data.getType())
{
case NORMAL_MAGIC:
return this.normalTeleportIcon;
case ANCIENT_MAGICKS:
return this.ancientTeleportIcon;
case LUNAR_MAGIC:
return this.lunarTeleportIcon;
case ARCEUUS_MAGIC:
return this.arceuusTeleportIcon;
case JEWELLERY:
return this.jewelleryTeleportIcon;
case SCROLL:
return this.scrollTeleportIcon;
case OTHER:
return this.miscellaneousTeleportIcon;
default:
return false;
}
}).map(TeleportPoint::new)
.forEach(worldMapPointManager::add)
);
}
private void updateQuestStartPointIcons()