ground markers: add option to export and import

Co-authored-by: Paul Norton <napkinorton@gmail.com>
This commit is contained in:
Adam
2021-01-25 18:09:26 -05:00
committed by Adam
parent cc56854da9
commit 426265c765
2 changed files with 257 additions and 3 deletions

View File

@@ -51,6 +51,7 @@ import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.MenuEntryAdded;
import net.runelite.api.events.MenuOptionClicked;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.game.chatbox.ChatboxPanelManager;
import net.runelite.client.plugins.Plugin;
@@ -98,7 +99,13 @@ public class GroundMarkerPlugin extends Plugin
@Inject
private ChatboxPanelManager chatboxPanelManager;
private void savePoints(int regionId, Collection<GroundMarkerPoint> points)
@Inject
private EventBus eventBus;
@Inject
private GroundMarkerSharingManager sharingManager;
void savePoints(int regionId, Collection<GroundMarkerPoint> points)
{
if (points == null || points.isEmpty())
{
@@ -110,7 +117,7 @@ public class GroundMarkerPlugin extends Plugin
configManager.setConfiguration(CONFIG_GROUP, REGION_PREFIX + regionId, json);
}
private Collection<GroundMarkerPoint> getPoints(int regionId)
Collection<GroundMarkerPoint> getPoints(int regionId)
{
String json = configManager.getConfiguration(CONFIG_GROUP, REGION_PREFIX + regionId);
if (Strings.isNullOrEmpty(json))
@@ -129,7 +136,7 @@ public class GroundMarkerPlugin extends Plugin
return configManager.getConfig(GroundMarkerConfig.class);
}
private void loadPoints()
void loadPoints()
{
points.clear();
@@ -181,14 +188,18 @@ public class GroundMarkerPlugin extends Plugin
{
overlayManager.add(overlay);
overlayManager.add(minimapOverlay);
sharingManager.addMenuOptions();
loadPoints();
eventBus.register(sharingManager);
}
@Override
public void shutDown()
{
eventBus.unregister(sharingManager);
overlayManager.remove(overlay);
overlayManager.remove(minimapOverlay);
sharingManager.removeMenuOptions();
points.clear();
}

View File

@@ -0,0 +1,243 @@
/*
* Copyright (c) 2021, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.client.plugins.groundmarkers;
import com.google.common.base.Strings;
import com.google.common.util.concurrent.Runnables;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken;
import java.awt.Toolkit;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.inject.Inject;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.ChatMessageType;
import net.runelite.api.Client;
import net.runelite.api.events.WidgetMenuOptionClicked;
import static net.runelite.api.widgets.WidgetInfo.WORLD_MAP_OPTION;
import net.runelite.client.chat.ChatMessageManager;
import net.runelite.client.chat.QueuedMessage;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.game.chatbox.ChatboxPanelManager;
import net.runelite.client.menus.MenuManager;
import net.runelite.client.menus.WidgetMenuOption;
import net.runelite.http.api.RuneLiteAPI;
@Slf4j
class GroundMarkerSharingManager
{
private static final WidgetMenuOption EXPORT_MARKERS_OPTION = new WidgetMenuOption("Export", "Ground Markers", WORLD_MAP_OPTION);
private static final WidgetMenuOption IMPORT_MARKERS_OPTION = new WidgetMenuOption("Import", "Ground Markers", WORLD_MAP_OPTION);
private static final Gson GSON = RuneLiteAPI.GSON;
private final GroundMarkerPlugin plugin;
private final Client client;
private final MenuManager menuManager;
private final ChatMessageManager chatMessageManager;
private final ChatboxPanelManager chatboxPanelManager;
@Inject
private GroundMarkerSharingManager(GroundMarkerPlugin plugin, Client client, MenuManager menuManager, ChatMessageManager chatMessageManager, ChatboxPanelManager chatboxPanelManager)
{
this.plugin = plugin;
this.client = client;
this.menuManager = menuManager;
this.chatMessageManager = chatMessageManager;
this.chatboxPanelManager = chatboxPanelManager;
}
void addMenuOptions()
{
menuManager.addManagedCustomMenu(EXPORT_MARKERS_OPTION);
menuManager.addManagedCustomMenu(IMPORT_MARKERS_OPTION);
}
void removeMenuOptions()
{
menuManager.removeManagedCustomMenu(EXPORT_MARKERS_OPTION);
menuManager.removeManagedCustomMenu(IMPORT_MARKERS_OPTION);
}
private boolean widgetMenuClickedEquals(final WidgetMenuOptionClicked event, final WidgetMenuOption target)
{
return event.getMenuTarget().equals(target.getMenuTarget()) &&
event.getMenuOption().equals(target.getMenuOption());
}
@Subscribe
public void onWidgetMenuOptionClicked(WidgetMenuOptionClicked event)
{
// ensure that the option clicked is the export markers option
if (event.getWidget() != WORLD_MAP_OPTION)
{
return;
}
if (widgetMenuClickedEquals(event, EXPORT_MARKERS_OPTION))
{
exportGroundMarkers();
}
else if (widgetMenuClickedEquals(event, IMPORT_MARKERS_OPTION))
{
promptForImport();
}
}
private void exportGroundMarkers()
{
int[] regions = client.getMapRegions();
if (regions == null)
{
return;
}
List<GroundMarkerPoint> activePoints = Arrays.stream(regions)
.mapToObj(regionId -> plugin.getPoints(regionId).stream())
.flatMap(Function.identity())
.collect(Collectors.toList());
if (activePoints.isEmpty())
{
sendChatMessage("You have no ground markers to export.");
return;
}
final String exportDump = GSON.toJson(activePoints);
log.debug("Exported ground markers: {}", exportDump);
Toolkit.getDefaultToolkit()
.getSystemClipboard()
.setContents(new StringSelection(exportDump), null);
sendChatMessage(activePoints.size() + " ground markers were copied to your clipboard.");
}
private void promptForImport()
{
final String clipboardText;
try
{
clipboardText = Toolkit.getDefaultToolkit()
.getSystemClipboard()
.getData(DataFlavor.stringFlavor)
.toString();
}
catch (IOException | UnsupportedFlavorException ex)
{
sendChatMessage("Unable to read system clipboard.");
log.warn("error reading clipboard", ex);
return;
}
log.debug("Clipboard contents: {}", clipboardText);
if (Strings.isNullOrEmpty(clipboardText))
{
sendChatMessage("You do not have any ground markers copied in your clipboard.");
return;
}
List<GroundMarkerPoint> importPoints;
try
{
// CHECKSTYLE:OFF
importPoints = GSON.fromJson(clipboardText, new TypeToken<List<GroundMarkerPoint>>(){}.getType());
// CHECKSTYLE:ON
}
catch (JsonSyntaxException e)
{
log.debug("Malformed JSON for clipboard import", e);
sendChatMessage("You do not have any ground markers copied in your clipboard.");
return;
}
if (importPoints.isEmpty())
{
sendChatMessage("You do not have any ground markers copied in your clipboard.");
return;
}
chatboxPanelManager.openTextMenuInput("Are you sure you want to import " + importPoints.size() + " ground markers?")
.option("Yes", () -> importGroundMarkers(importPoints))
.option("No", Runnables::doNothing)
.build();
}
private void importGroundMarkers(Collection<GroundMarkerPoint> importPoints)
{
// regions being imported may not be loaded on client,
// so need to import each bunch directly into the config
// first, collate the list of unique region ids in the import
Map<Integer, List<GroundMarkerPoint>> regionGroupedPoints = importPoints.stream()
.collect(Collectors.groupingBy(GroundMarkerPoint::getRegionId));
// now import each region into the config
regionGroupedPoints.forEach((regionId, groupedPoints) ->
{
// combine imported points with existing region points
log.debug("Importing {} points to region {}", groupedPoints.size(), regionId);
Collection<GroundMarkerPoint> regionPoints = plugin.getPoints(regionId);
List<GroundMarkerPoint> mergedList = new ArrayList<>(regionPoints.size() + groupedPoints.size());
// add existing points
mergedList.addAll(regionPoints);
// add new points
for (GroundMarkerPoint point : groupedPoints)
{
// filter out duplicates
if (!mergedList.contains(point))
{
mergedList.add(point);
}
}
plugin.savePoints(regionId, mergedList);
});
// reload points from config
log.debug("Reloading points after import");
plugin.loadPoints();
sendChatMessage(importPoints.size() + " ground markers were imported from the clipboard.");
}
private void sendChatMessage(final String message)
{
chatMessageManager.queue(QueuedMessage.builder()
.type(ChatMessageType.CONSOLE)
.runeLiteFormattedMessage(message)
.build());
}
}