client: add world service to manage world fetching
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 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.game;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Comparator;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.GameState;
|
||||
import net.runelite.client.util.RunnableExceptionLogger;
|
||||
import net.runelite.http.api.worlds.World;
|
||||
import net.runelite.http.api.worlds.WorldClient;
|
||||
import net.runelite.http.api.worlds.WorldResult;
|
||||
|
||||
@Singleton
|
||||
@Slf4j
|
||||
public class WorldService
|
||||
{
|
||||
private static final int WORLD_FETCH_TIMER = 10; // minutes
|
||||
|
||||
private final Client client;
|
||||
private final ScheduledExecutorService scheduledExecutorService;
|
||||
private final WorldClient worldClient;
|
||||
private final CompletableFuture<WorldResult> firstRunFuture = new CompletableFuture<>();
|
||||
|
||||
private WorldResult worlds;
|
||||
|
||||
@Inject
|
||||
private WorldService(Client client, ScheduledExecutorService scheduledExecutorService, WorldClient worldClient)
|
||||
{
|
||||
this.client = client;
|
||||
this.scheduledExecutorService = scheduledExecutorService;
|
||||
this.worldClient = worldClient;
|
||||
|
||||
scheduledExecutorService.scheduleWithFixedDelay(RunnableExceptionLogger.wrap(this::tick), 0, WORLD_FETCH_TIMER, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
private void tick()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (worlds == null || client.getGameState() == GameState.LOGGED_IN)
|
||||
{
|
||||
fetch();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
firstRunFuture.complete(worlds);
|
||||
}
|
||||
}
|
||||
|
||||
private void fetch()
|
||||
{
|
||||
log.debug("Fetching worlds");
|
||||
|
||||
try
|
||||
{
|
||||
WorldResult worldResult = worldClient.lookupWorlds();
|
||||
worldResult.getWorlds().sort(Comparator.comparingInt(World::getId));
|
||||
worlds = worldResult;
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
log.warn("Error looking up worlds", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void refresh()
|
||||
{
|
||||
scheduledExecutorService.execute(this::fetch);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public WorldResult getWorlds()
|
||||
{
|
||||
if (!firstRunFuture.isDone())
|
||||
{
|
||||
try
|
||||
{
|
||||
return firstRunFuture.get(10, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (InterruptedException | ExecutionException | TimeoutException e)
|
||||
{
|
||||
log.warn("Failed to retrieve worlds on first run", e);
|
||||
}
|
||||
}
|
||||
|
||||
return worlds;
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,6 @@
|
||||
package net.runelite.client.plugins.defaultworld;
|
||||
|
||||
import com.google.inject.Provides;
|
||||
import java.io.IOException;
|
||||
import javax.inject.Inject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.api.Client;
|
||||
@@ -34,11 +33,11 @@ import net.runelite.api.events.GameStateChanged;
|
||||
import net.runelite.client.config.ConfigManager;
|
||||
import net.runelite.client.eventbus.Subscribe;
|
||||
import net.runelite.client.events.SessionOpen;
|
||||
import net.runelite.client.game.WorldService;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.plugins.PluginDescriptor;
|
||||
import net.runelite.client.util.WorldUtil;
|
||||
import net.runelite.http.api.worlds.World;
|
||||
import net.runelite.http.api.worlds.WorldClient;
|
||||
import net.runelite.http.api.worlds.WorldResult;
|
||||
|
||||
@PluginDescriptor(
|
||||
@@ -56,7 +55,7 @@ public class DefaultWorldPlugin extends Plugin
|
||||
private DefaultWorldConfig config;
|
||||
|
||||
@Inject
|
||||
private WorldClient worldClient;
|
||||
private WorldService worldService;
|
||||
|
||||
private int worldCache;
|
||||
private boolean worldChangeRequired;
|
||||
@@ -116,32 +115,32 @@ public class DefaultWorldPlugin extends Plugin
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
final WorldResult worldResult = worldService.getWorlds();
|
||||
|
||||
if (worldResult == null)
|
||||
{
|
||||
final WorldResult worldResult = worldClient.lookupWorlds();
|
||||
final World world = worldResult.findWorld(correctedWorld);
|
||||
|
||||
if (world != null)
|
||||
{
|
||||
final net.runelite.api.World rsWorld = client.createWorld();
|
||||
rsWorld.setActivity(world.getActivity());
|
||||
rsWorld.setAddress(world.getAddress());
|
||||
rsWorld.setId(world.getId());
|
||||
rsWorld.setPlayerCount(world.getPlayers());
|
||||
rsWorld.setLocation(world.getLocation());
|
||||
rsWorld.setTypes(WorldUtil.toWorldTypes(world.getTypes()));
|
||||
|
||||
client.changeWorld(rsWorld);
|
||||
log.debug("Applied new world {}", correctedWorld);
|
||||
}
|
||||
else
|
||||
{
|
||||
log.warn("World {} not found.", correctedWorld);
|
||||
}
|
||||
log.warn("Failed to lookup worlds.");
|
||||
return;
|
||||
}
|
||||
catch (IOException e)
|
||||
|
||||
final World world = worldResult.findWorld(correctedWorld);
|
||||
|
||||
if (world != null)
|
||||
{
|
||||
log.warn("Error looking up world {}. Error: {}", correctedWorld, e);
|
||||
final net.runelite.api.World rsWorld = client.createWorld();
|
||||
rsWorld.setActivity(world.getActivity());
|
||||
rsWorld.setAddress(world.getAddress());
|
||||
rsWorld.setId(world.getId());
|
||||
rsWorld.setPlayerCount(world.getPlayers());
|
||||
rsWorld.setLocation(world.getLocation());
|
||||
rsWorld.setTypes(WorldUtil.toWorldTypes(world.getTypes()));
|
||||
|
||||
client.changeWorld(rsWorld);
|
||||
log.debug("Applied new world {}", correctedWorld);
|
||||
}
|
||||
else
|
||||
{
|
||||
log.warn("World {} not found.", correctedWorld);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,10 +31,8 @@ import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ObjectArrays;
|
||||
import com.google.inject.Provides;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Comparator;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -76,6 +74,7 @@ import net.runelite.client.chat.QueuedMessage;
|
||||
import net.runelite.client.config.ConfigManager;
|
||||
import net.runelite.client.eventbus.Subscribe;
|
||||
import net.runelite.client.events.ConfigChanged;
|
||||
import net.runelite.client.game.WorldService;
|
||||
import net.runelite.client.input.KeyManager;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.plugins.PluginDescriptor;
|
||||
@@ -88,7 +87,6 @@ import net.runelite.client.util.HotkeyListener;
|
||||
import net.runelite.client.util.Text;
|
||||
import net.runelite.client.util.WorldUtil;
|
||||
import net.runelite.http.api.worlds.World;
|
||||
import net.runelite.http.api.worlds.WorldClient;
|
||||
import net.runelite.http.api.worlds.WorldResult;
|
||||
import net.runelite.http.api.worlds.WorldType;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
@@ -101,7 +99,6 @@ import org.apache.commons.lang3.ArrayUtils;
|
||||
@Slf4j
|
||||
public class WorldHopperPlugin extends Plugin
|
||||
{
|
||||
private static final int WORLD_FETCH_TIMER = 10;
|
||||
private static final int REFRESH_THROTTLE = 60_000; // ms
|
||||
private static final int TICK_THROTTLE = (int) Duration.ofMinutes(10).toMillis();
|
||||
|
||||
@@ -130,9 +127,6 @@ public class WorldHopperPlugin extends Plugin
|
||||
@Inject
|
||||
private ChatMessageManager chatMessageManager;
|
||||
|
||||
@Inject
|
||||
private ScheduledExecutorService executorService;
|
||||
|
||||
@Inject
|
||||
private WorldHopperConfig config;
|
||||
|
||||
@@ -143,7 +137,7 @@ public class WorldHopperPlugin extends Plugin
|
||||
private WorldHopperPingOverlay worldHopperOverlay;
|
||||
|
||||
@Inject
|
||||
private WorldClient worldClient;
|
||||
private WorldService worldService;
|
||||
|
||||
private ScheduledExecutorService hopperExecutorService;
|
||||
|
||||
@@ -158,11 +152,9 @@ public class WorldHopperPlugin extends Plugin
|
||||
|
||||
private int favoriteWorld1, favoriteWorld2;
|
||||
|
||||
private ScheduledFuture<?> worldResultFuture, pingFuture, currPingFuture;
|
||||
private WorldResult worldResult;
|
||||
private ScheduledFuture<?> pingFuture, currPingFuture;
|
||||
private int currentWorld;
|
||||
private Instant lastFetch;
|
||||
private boolean firstRun;
|
||||
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private int currentPing;
|
||||
@@ -193,7 +185,6 @@ public class WorldHopperPlugin extends Plugin
|
||||
@Override
|
||||
protected void startUp() throws Exception
|
||||
{
|
||||
firstRun = true;
|
||||
currentPing = -1;
|
||||
|
||||
keyManager.registerKeyListener(previousKeyListener);
|
||||
@@ -225,8 +216,8 @@ public class WorldHopperPlugin extends Plugin
|
||||
|
||||
// The plugin has its own executor for pings, as it blocks for a long time
|
||||
hopperExecutorService = new ExecutorServiceExceptionLogger(Executors.newSingleThreadScheduledExecutor());
|
||||
// On first run this schedules an initial ping on hopperExecutorService
|
||||
worldResultFuture = executorService.scheduleAtFixedRate(this::tick, 0, WORLD_FETCH_TIMER, TimeUnit.MINUTES);
|
||||
// Run the first-run ping
|
||||
hopperExecutorService.execute(this::pingInitialWorlds);
|
||||
|
||||
// Give some initial delay - this won't run until after pingInitialWorlds finishes from tick() anyway
|
||||
pingFuture = hopperExecutorService.scheduleWithFixedDelay(this::pingNextWorld, 15, 3, TimeUnit.SECONDS);
|
||||
@@ -247,11 +238,6 @@ public class WorldHopperPlugin extends Plugin
|
||||
keyManager.unregisterKeyListener(previousKeyListener);
|
||||
keyManager.unregisterKeyListener(nextKeyListener);
|
||||
|
||||
worldResultFuture.cancel(true);
|
||||
worldResultFuture = null;
|
||||
worldResult = null;
|
||||
lastFetch = null;
|
||||
|
||||
clientToolbar.removeNavigation(navButton);
|
||||
|
||||
hopperExecutorService.shutdown();
|
||||
@@ -385,6 +371,7 @@ public class WorldHopperPlugin extends Plugin
|
||||
|
||||
// Don't add entry if user is offline
|
||||
ChatPlayer player = getChatPlayerFromName(event.getTarget());
|
||||
WorldResult worldResult = worldService.getWorlds();
|
||||
|
||||
if (player == null || player.getWorld() == 0 || player.getWorld() == client.getWorld()
|
||||
|| worldResult == null)
|
||||
@@ -475,26 +462,6 @@ public class WorldHopperPlugin extends Plugin
|
||||
this.lastFetch = Instant.now(); // This counts as a fetch as it updates populations
|
||||
}
|
||||
|
||||
private void tick()
|
||||
{
|
||||
Instant now = Instant.now();
|
||||
if (lastFetch != null && now.toEpochMilli() - lastFetch.toEpochMilli() < TICK_THROTTLE)
|
||||
{
|
||||
log.debug("Throttling world refresh tick");
|
||||
return;
|
||||
}
|
||||
|
||||
fetchWorlds();
|
||||
|
||||
// Ping worlds once at startup
|
||||
if (firstRun)
|
||||
{
|
||||
firstRun = false;
|
||||
// On first run we ping all of the worlds at once to initialize the ping values
|
||||
hopperExecutorService.execute(this::pingInitialWorlds);
|
||||
}
|
||||
}
|
||||
|
||||
void refresh()
|
||||
{
|
||||
Instant now = Instant.now();
|
||||
@@ -504,25 +471,8 @@ public class WorldHopperPlugin extends Plugin
|
||||
return;
|
||||
}
|
||||
|
||||
fetchWorlds();
|
||||
}
|
||||
|
||||
private void fetchWorlds()
|
||||
{
|
||||
log.debug("Fetching worlds");
|
||||
|
||||
try
|
||||
{
|
||||
WorldResult worldResult = worldClient.lookupWorlds();
|
||||
worldResult.getWorlds().sort(Comparator.comparingInt(World::getId));
|
||||
this.worldResult = worldResult;
|
||||
this.lastFetch = Instant.now();
|
||||
updateList();
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
log.warn("Error looking up worlds", ex);
|
||||
}
|
||||
lastFetch = now;
|
||||
worldService.refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -530,11 +480,16 @@ public class WorldHopperPlugin extends Plugin
|
||||
*/
|
||||
private void updateList()
|
||||
{
|
||||
SwingUtilities.invokeLater(() -> panel.populate(worldResult.getWorlds()));
|
||||
WorldResult worldResult = worldService.getWorlds();
|
||||
if (worldResult != null)
|
||||
{
|
||||
SwingUtilities.invokeLater(() -> panel.populate(worldResult.getWorlds()));
|
||||
}
|
||||
}
|
||||
|
||||
private void hop(boolean previous)
|
||||
{
|
||||
WorldResult worldResult = worldService.getWorlds();
|
||||
if (worldResult == null || client.getGameState() != GameState.LOGGED_IN)
|
||||
{
|
||||
return;
|
||||
@@ -645,6 +600,7 @@ public class WorldHopperPlugin extends Plugin
|
||||
|
||||
private void hop(int worldId)
|
||||
{
|
||||
WorldResult worldResult = worldService.getWorlds();
|
||||
// Don't try to hop if the world doesn't exist
|
||||
World world = worldResult.findWorld(worldId);
|
||||
if (world == null)
|
||||
@@ -778,6 +734,7 @@ public class WorldHopperPlugin extends Plugin
|
||||
*/
|
||||
private void pingInitialWorlds()
|
||||
{
|
||||
WorldResult worldResult = worldService.getWorlds();
|
||||
if (worldResult == null || !config.showSidebar() || !config.ping())
|
||||
{
|
||||
return;
|
||||
@@ -801,6 +758,7 @@ public class WorldHopperPlugin extends Plugin
|
||||
*/
|
||||
private void pingNextWorld()
|
||||
{
|
||||
WorldResult worldResult = worldService.getWorlds();
|
||||
if (worldResult == null || !config.showSidebar() || !config.ping())
|
||||
{
|
||||
return;
|
||||
@@ -837,6 +795,7 @@ public class WorldHopperPlugin extends Plugin
|
||||
*/
|
||||
private void pingCurrentWorld()
|
||||
{
|
||||
WorldResult worldResult = worldService.getWorlds();
|
||||
// There is no reason to ping the current world if not logged in, as the overlay doesn't draw
|
||||
if (worldResult == null || !config.displayPing() || client.getGameState() != GameState.LOGGED_IN)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user