diff --git a/Plugins/mineplex-questmanager/pom.xml b/Plugins/mineplex-questmanager/pom.xml
deleted file mode 100644
index 5678aecff..000000000
--- a/Plugins/mineplex-questmanager/pom.xml
+++ /dev/null
@@ -1,69 +0,0 @@
-
- 4.0.0
-
- com.mineplex
- mineplex-parent
- dev-SNAPSHOT
-
- mineplex-questmanager
- Mineplex.questmanager
- A centralized service that selects daily quests
-
-
- 23.0
- 2.12
- 1.8.8-1.9-SNAPSHOT
-
-
-
-
- ${project.groupId}
- mineplex-serverdata
- ${project.version}
-
-
- com.google.guava
- guava
- ${version.guava}
- compile
-
-
- jline
- jline
- ${version.jline}
- compile
-
-
- com.mineplex
- spigot
- provided
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-shade-plugin
-
- false
-
-
- mineplex.quest.daemon.QuestDaemon
-
-
-
-
-
- package
-
- shade
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Plugins/mineplex-questmanager/src/mineplex/quest/client/RedisQuestSupplier.java b/Plugins/mineplex-questmanager/src/mineplex/quest/client/RedisQuestSupplier.java
deleted file mode 100644
index 127862a66..000000000
--- a/Plugins/mineplex-questmanager/src/mineplex/quest/client/RedisQuestSupplier.java
+++ /dev/null
@@ -1,111 +0,0 @@
-package mineplex.quest.client;
-
-import java.util.HashSet;
-import java.util.Optional;
-import java.util.Set;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
-import org.bukkit.plugin.java.JavaPlugin;
-
-import com.google.common.collect.ImmutableSet;
-
-import mineplex.quest.client.event.QuestsUpdatedEvent;
-import mineplex.quest.common.Quest;
-import mineplex.quest.common.QuestSupplier;
-import mineplex.quest.common.redis.PubSubChannels;
-import mineplex.quest.common.redis.QuestTypeSerializer;
-import mineplex.serverdata.redis.messaging.PubSubMessager;
-
-/**
- * Provides methods to retrieve currently active quests. Retrieves active quests from the
- * centralized quest service via redis.
- *
- * Intended to be thread-safe.
- */
-public class RedisQuestSupplier implements QuestSupplier
-{
-
- private final JavaPlugin _plugin;
- private final String _serverUniqueId;
- private final PubSubMessager _pubSub;
- private final ReadWriteLock _lock = new ReentrantReadWriteLock();
-
- private final Set _quests = new HashSet<>();
-
- public RedisQuestSupplier(JavaPlugin plugin, PubSubMessager pubSub)
- {
- _plugin = plugin;
- _pubSub = pubSub;
-
- _serverUniqueId = plugin.getConfig().getString("serverstatus.name");
-
- // update quests sent specifically to this server when it requests them (like on startup)
- _pubSub.subscribe(PubSubChannels.QUEST_REQUEST_BASE + _serverUniqueId, this::updateQuests);
-
- requestActiveQuests();
-
- // update quests when received
- _pubSub.subscribe(PubSubChannels.QUEST_SUPPLIER_CHANNEL, this::updateQuests);
- }
-
- private void updateQuests(String channel, String message)
- {
- _lock.writeLock().lock();
- try
- {
- _quests.clear();
- _quests.addAll(deserialize(message));
-
- System.out.println("[QUEST-SUPPLIER] Quest update received from daemon, active quests: ");
- _quests.forEach(q -> System.out.println("[QUEST-SUPPLIER] " + q.toString()));
-
- // notify
- _plugin.getServer().getPluginManager().callEvent(new QuestsUpdatedEvent(get()));
- }
- finally
- {
- _lock.writeLock().unlock();
- }
- }
-
- private Set deserialize(String json)
- {
- return QuestTypeSerializer.QUEST_GSON.fromJson(json, QuestTypeSerializer.QUEST_TYPE);
- }
-
- private void requestActiveQuests()
- {
- System.out.println("[QUEST-SUPPLIER] Requesting active quests from QuestDaemon");
- // request current active quests, send server unique id so we can send a response just to this server
- _pubSub.publish(PubSubChannels.QUEST_REQUEST_BASE, _serverUniqueId);
- }
-
- @Override
- public Set get()
- {
- _lock.readLock().lock();
- try
- {
- return ImmutableSet.copyOf(_quests);
- }
- finally
- {
- _lock.readLock().unlock();
- }
- }
-
- @Override
- public Optional getById(int uniquePersistentId)
- {
- _lock.readLock().lock();
- try
- {
- return _quests.stream().filter(q -> q.getUniqueId() == uniquePersistentId).findFirst();
- }
- finally
- {
- _lock.readLock().unlock();
- }
- }
-}
\ No newline at end of file
diff --git a/Plugins/mineplex-questmanager/src/mineplex/quest/client/event/QuestsUpdatedEvent.java b/Plugins/mineplex-questmanager/src/mineplex/quest/client/event/QuestsUpdatedEvent.java
deleted file mode 100644
index 82b70450a..000000000
--- a/Plugins/mineplex-questmanager/src/mineplex/quest/client/event/QuestsUpdatedEvent.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package mineplex.quest.client.event;
-
-import java.util.Set;
-
-import org.bukkit.event.Event;
-import org.bukkit.event.HandlerList;
-
-import mineplex.quest.common.Quest;
-
-/**
- * An event called when the currently active quests are rotated at the end of a day.
- */
-public class QuestsUpdatedEvent extends Event
-{
-
- private static final HandlerList HANDLERS = new HandlerList();
-
- private final Set _to;
-
- public QuestsUpdatedEvent(Set to)
- {
- _to = to;
- }
-
- /**
- * @return The currently active quests.
- */
- public Set getActiveQuests()
- {
- return _to;
- }
-
- @Override
- public HandlerList getHandlers()
- {
- return HANDLERS;
- }
-
- public static HandlerList getHandlerList()
- {
- return HANDLERS;
- }
-
-}
diff --git a/Plugins/mineplex-questmanager/src/mineplex/quest/common/BaseQuest.java b/Plugins/mineplex-questmanager/src/mineplex/quest/common/BaseQuest.java
deleted file mode 100644
index afc387d3a..000000000
--- a/Plugins/mineplex-questmanager/src/mineplex/quest/common/BaseQuest.java
+++ /dev/null
@@ -1,59 +0,0 @@
-package mineplex.quest.common;
-
-/**
- * Implementation of baseline {@link Quest}.
- */
-public class BaseQuest implements Quest
-{
-
- private final int _uniqueId;
- private final String _name;
- private final QuestRarity _rarity;
- private final int _cost;
-
- public BaseQuest(int uniqueId, String name, int cost, QuestRarity rarity)
- {
- _uniqueId = uniqueId;
- _name = name;
- _cost = cost;
- _rarity = rarity;
- }
-
- @Override
- public String getName()
- {
- return _name;
- }
-
- @Override
- public int getUniqueId()
- {
- return _uniqueId;
- }
-
- @Override
- public QuestRarity getRarity()
- {
- return _rarity;
- }
-
- @Override
- public String getDataId()
- {
- return _uniqueId + "";
- }
-
- @Override
- public boolean shouldRotate()
- {
- return _cost != -1;
- }
-
- @Override
- public String toString()
- {
- return "BaseQuest [uniqueId=" + _uniqueId + ", name=" + _name + ", rarity=" + _rarity
- + ", cost=" + _cost + "]";
- }
-
-}
diff --git a/Plugins/mineplex-questmanager/src/mineplex/quest/common/Quest.java b/Plugins/mineplex-questmanager/src/mineplex/quest/common/Quest.java
deleted file mode 100644
index f2c7ac799..000000000
--- a/Plugins/mineplex-questmanager/src/mineplex/quest/common/Quest.java
+++ /dev/null
@@ -1,53 +0,0 @@
-package mineplex.quest.common;
-
-import mineplex.quest.daemon.QuestManager;
-import mineplex.serverdata.data.Data;
-import mineplex.serverdata.data.DataRepository;
-
-/**
- * A quest that can be completed by users for a reward.
- */
-public interface Quest extends Data
-{
- /**
- * Gets the name of this quest.
- *
- * @return The name of this quest.
- */
- String getName();
-
- /**
- * Gets the unique persistent id for this quest. This id will be used to store quests per user
- * and should not be changed.
- *
- * @return The unique persistent id for this quest.
- */
- int getUniqueId();
-
- /**
- * Gets the {@link QuestRarity} of this quest.
- *
- * @return The rarity of this quest.
- */
- QuestRarity getRarity();
-
- /**
- * Get the unique persistent id for this quest as a String. Intended to be used for storage
- * within Redis via {@link DataRepository}.
- *
- * Don't use this to get the quest unique id, use {@link Quest#getUniqueId()} instead.
- *
- * @return A string version of the unique persistent id for this quest.
- */
- @Override
- String getDataId();
-
- /**
- * Checks whether this quest should be selected by the {@link QuestManager} daemon process.
- * Quests with a cost of -1 should not ever be selected as active quests by the daemon.
- *
- * @return true
if this quest should be selected as an active quest, or
- * false
otherwise.
- */
- boolean shouldRotate();
-}
diff --git a/Plugins/mineplex-questmanager/src/mineplex/quest/common/QuestRarity.java b/Plugins/mineplex-questmanager/src/mineplex/quest/common/QuestRarity.java
deleted file mode 100644
index 4ef625ee0..000000000
--- a/Plugins/mineplex-questmanager/src/mineplex/quest/common/QuestRarity.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package mineplex.quest.common;
-
-/**
- * How rare a quest is. In other words, how often this quest should be chosen.
- */
-public enum QuestRarity
-{
- COMMON(1.0),
- RARE(0.5),
- LEGENDARY(0.1)
- ;
-
- private final double _weight;
-
- private QuestRarity(double weight)
- {
- _weight = weight;
- }
-
- public double getWeight()
- {
- return _weight;
- }
-}
diff --git a/Plugins/mineplex-questmanager/src/mineplex/quest/common/QuestSupplier.java b/Plugins/mineplex-questmanager/src/mineplex/quest/common/QuestSupplier.java
deleted file mode 100644
index 9e32a2d80..000000000
--- a/Plugins/mineplex-questmanager/src/mineplex/quest/common/QuestSupplier.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package mineplex.quest.common;
-
-import java.util.Optional;
-import java.util.Set;
-import java.util.function.Supplier;
-
-/**
- * Provides access to {@link Quest}s tracked by this QuestManager.
- */
-public interface QuestSupplier extends Supplier>
-{
-
- /**
- * Get an immutable set containing all of the currently active quests.
- */
- @Override
- Set get();
-
- /**
- * Attempts to get the {@link Quest} matching the supplied persistent id.
- *
- * @param uniquePersistentId The unique id of the quest.
- *
- * @return An {@link Optional} describing the {@link Quest}, or an empty Optional if none is
- * found.
- */
- Optional getById(int uniquePersistentId);
-
-}
diff --git a/Plugins/mineplex-questmanager/src/mineplex/quest/common/Quests.java b/Plugins/mineplex-questmanager/src/mineplex/quest/common/Quests.java
deleted file mode 100644
index 1b1bd41dd..000000000
--- a/Plugins/mineplex-questmanager/src/mineplex/quest/common/Quests.java
+++ /dev/null
@@ -1,73 +0,0 @@
-package mineplex.quest.common;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-
-import com.google.common.collect.ImmutableSet;
-
-import mineplex.quest.common.util.UtilGoogleSheet;
-import mineplex.quest.daemon.QuestDaemon;
-
-/**
- * Provides access to all quests.
- *
- * Loads quests from a google sheets json file.
- */
-public class Quests
-{
-
- private static final String ALL_QUESTS_FILE = "QUESTS_SHEET";
- private static final String QUEST_SHEET_KEY = "Quests";
-
- private static final int UNIQUE_ID_COLUMN = 0;
- private static final int NAME_COLUMN = 1;
- private static final int COST_COLUMN = 4;
- private static final int RARITY_COLUMN = 9;
-
- public static final Set QUESTS;
-
- static
- {
- ImmutableSet.Builder builder = ImmutableSet.builder();
-
- Map>> sheets = UtilGoogleSheet.getSheetData(ALL_QUESTS_FILE);
-
- List> rows = sheets.getOrDefault(QUEST_SHEET_KEY, Collections.emptyList());
-
- // get each row of spreadsheet, start at 1 since row 0 contains headers
- for (int i = 1; i < rows.size(); i++)
- {
- List row = rows.get(i);
-
- // attempt to parse quest data we need
- try
- {
- int uniqueId = Integer.parseInt(row.get(UNIQUE_ID_COLUMN));
- String name = row.get(NAME_COLUMN);
- int cost = Integer.parseInt(row.get(COST_COLUMN));
- QuestRarity rarity = QuestRarity.valueOf(row.get(RARITY_COLUMN).toUpperCase());
-
- Quest quest = new BaseQuest(uniqueId, name, cost, rarity);
-
- builder.add(quest);
- }
- catch (Exception e)
- {
- QuestDaemon.log("Exception encountered while parsing quest sheet row: " + row + ", "
- + e.getMessage());
- e.printStackTrace();
- }
- }
-
- QUESTS = builder.build();
- }
-
- public static Optional fromId(int uniqueId)
- {
- return QUESTS.stream().filter(quest -> quest.getUniqueId() == uniqueId).findFirst();
- }
-
-}
diff --git a/Plugins/mineplex-questmanager/src/mineplex/quest/common/redis/PubSubChannels.java b/Plugins/mineplex-questmanager/src/mineplex/quest/common/redis/PubSubChannels.java
deleted file mode 100644
index f28490ec8..000000000
--- a/Plugins/mineplex-questmanager/src/mineplex/quest/common/redis/PubSubChannels.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package mineplex.quest.common.redis;
-
-/**
- * Provides constants for Quests redis pub sub channels.
- */
-public class PubSubChannels
-{
-
- public static final String QUEST_SUPPLIER_CHANNEL = "quest-manager";
-
- public static final String QUEST_REQUEST_BASE = "quest-manager-request:";
-
-}
diff --git a/Plugins/mineplex-questmanager/src/mineplex/quest/common/redis/QuestRedisDataRepository.java b/Plugins/mineplex-questmanager/src/mineplex/quest/common/redis/QuestRedisDataRepository.java
deleted file mode 100644
index 825c1364e..000000000
--- a/Plugins/mineplex-questmanager/src/mineplex/quest/common/redis/QuestRedisDataRepository.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package mineplex.quest.common.redis;
-
-import mineplex.quest.common.Quest;
-import mineplex.quest.common.Quests;
-import mineplex.serverdata.Region;
-import mineplex.serverdata.redis.RedisDataRepository;
-import mineplex.serverdata.servers.ConnectionData;
-
-/**
- * A {@link RedisDataRepository} that can serialize & deserialize (and thus store & retrieve from
- * redis) Quest instances.
- */
-public class QuestRedisDataRepository extends RedisDataRepository
-{
-
- public QuestRedisDataRepository(ConnectionData writeConn, ConnectionData readConn, Region region,
- String elementLabel)
- {
- super(writeConn, readConn, region, Quest.class, elementLabel);
- }
-
- @Override
- protected Quest deserialize(String json)
- {
- if (json == null || json.isEmpty())
- {
- return null;
- }
-
- return Quests.fromId(Integer.parseInt(json)).orElse(null);
- }
-
- @Override
- protected String serialize(Quest quest)
- {
- return quest.getDataId();
- }
-
-}
diff --git a/Plugins/mineplex-questmanager/src/mineplex/quest/common/redis/QuestTypeDeserialiazer.java b/Plugins/mineplex-questmanager/src/mineplex/quest/common/redis/QuestTypeDeserialiazer.java
deleted file mode 100644
index 4b71efc77..000000000
--- a/Plugins/mineplex-questmanager/src/mineplex/quest/common/redis/QuestTypeDeserialiazer.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package mineplex.quest.common.redis;
-
-import java.lang.reflect.Type;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Optional;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import com.google.gson.Gson;
-import com.google.gson.JsonDeserializationContext;
-import com.google.gson.JsonDeserializer;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonParseException;
-
-import mineplex.quest.common.Quest;
-import mineplex.quest.common.Quests;
-
-/**
- * An implementation of a {@link JsonDeserializer} intended for use in {@link Gson}. Deserializes a
- * {@link JsonElement} String into a Set.
- */
-public class QuestTypeDeserialiazer implements JsonDeserializer>
-{
-
- @Override
- public Set deserialize(JsonElement json, Type typeOfT,
- JsonDeserializationContext context) throws JsonParseException
- {
- String[] split = json.getAsString().split(QuestTypeSerializer.SEPARATOR);
- return Arrays.stream(split).map(questId -> Quests.fromId(Integer.valueOf(questId)))
- .filter(Optional::isPresent).map(Optional::get)
- .collect(Collectors.toCollection(HashSet::new));
- }
-
-}
diff --git a/Plugins/mineplex-questmanager/src/mineplex/quest/common/redis/QuestTypeSerializer.java b/Plugins/mineplex-questmanager/src/mineplex/quest/common/redis/QuestTypeSerializer.java
deleted file mode 100644
index 67905f1a2..000000000
--- a/Plugins/mineplex-questmanager/src/mineplex/quest/common/redis/QuestTypeSerializer.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package mineplex.quest.common.redis;
-
-import java.lang.reflect.Type;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import com.google.common.base.Joiner;
-import com.google.common.reflect.TypeToken;
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonPrimitive;
-import com.google.gson.JsonSerializationContext;
-import com.google.gson.JsonSerializer;
-
-import mineplex.quest.common.Quest;
-
-/**
- * An implementation of a {@link JsonSerializer} intended for use in {@link Gson}. Serializes a
- * Set into a {@link JsonElement} String.
- */
-public class QuestTypeSerializer implements JsonSerializer>
-{
-
- @SuppressWarnings("serial")
- public static final Type QUEST_TYPE = new TypeToken>(){}.getType();
-
- public static final Gson QUEST_GSON = new GsonBuilder()
- .registerTypeAdapter(QuestTypeSerializer.QUEST_TYPE, new QuestTypeDeserialiazer())
- .registerTypeAdapter(QuestTypeSerializer.QUEST_TYPE, new QuestTypeSerializer())
- .create();
-
- public static final String SEPARATOR = ",";
-
- @Override
- public JsonElement serialize(Set src, Type typeOfSrc, JsonSerializationContext context)
- {
- StringBuilder builder = new StringBuilder();
- Joiner.on(SEPARATOR).appendTo(builder,
- src.stream().map(Quest::getDataId).collect(Collectors.toSet()));
- return new JsonPrimitive(builder.toString());
- }
-
-}
diff --git a/Plugins/mineplex-questmanager/src/mineplex/quest/common/util/RandomCollection.java b/Plugins/mineplex-questmanager/src/mineplex/quest/common/util/RandomCollection.java
deleted file mode 100644
index c74225c91..000000000
--- a/Plugins/mineplex-questmanager/src/mineplex/quest/common/util/RandomCollection.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package mineplex.quest.common.util;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Map;
-import java.util.NavigableMap;
-import java.util.Random;
-import java.util.TreeMap;
-import java.util.concurrent.ThreadLocalRandom;
-
-/**
- * Provides random, weighted access to a collection of elements.
- *
- * Intended to be thread-safe.
- *
- * @param The generic type parameter of the elements.
- */
-public class RandomCollection
-{
-
- private final NavigableMap _map = Collections.synchronizedNavigableMap(new TreeMap());
- private final Random _random;
-
- private double total = 0;
-
- public RandomCollection(Random random)
- {
- _random = random;
- }
-
- public RandomCollection()
- {
- this(ThreadLocalRandom.current());
- }
-
- public void addAll(Map values)
- {
- values.forEach(this::add);
- }
-
- public void add(E result, double weight)
- {
- if (weight <= 0)
- {
- return;
- }
-
- total += weight;
- _map.put(total, result);
- }
-
- public E next()
- {
- double value = _random.nextDouble() * total;
- return _map.ceilingEntry(value).getValue();
- }
-
- public Collection values()
- {
- return _map.values();
- }
-}
diff --git a/Plugins/mineplex-questmanager/src/mineplex/quest/common/util/UtilGoogleSheet.java b/Plugins/mineplex-questmanager/src/mineplex/quest/common/util/UtilGoogleSheet.java
deleted file mode 100644
index 6dee08b44..000000000
--- a/Plugins/mineplex-questmanager/src/mineplex/quest/common/util/UtilGoogleSheet.java
+++ /dev/null
@@ -1,75 +0,0 @@
-package mineplex.quest.common.util;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-import com.google.gson.JsonArray;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonParser;
-
-/**
- * Provides utility methods for deserializing google sheets json files.
- */
-public class UtilGoogleSheet
-{
- private static final File DATA_STORE_DIR = new File(
- ".." + File.separatorChar + ".." + File.separatorChar + "update" + File.separatorChar
- + "files");
-
- public static Map>> getSheetData(String name)
- {
- return getSheetData(new File(DATA_STORE_DIR + File.separator + name + ".json"));
- }
-
- public static Map>> getSheetData(File file)
- {
- if (!file.exists())
- {
- return null;
- }
-
- Map>> valuesMap = new HashMap<>();
-
- try
- {
- JsonParser parser = new JsonParser();
- JsonElement data = parser.parse(new FileReader(file));
- JsonArray parent = data.getAsJsonObject().getAsJsonArray("data");
-
- for (int i = 0; i < parent.size(); i++)
- {
- JsonObject sheet = parent.get(i).getAsJsonObject();
- String name = sheet.get("name").getAsString();
- JsonArray values = sheet.getAsJsonArray("values");
- List> valuesList = new ArrayList<>(values.size());
-
- for (int j = 0; j < values.size(); j++)
- {
- List list = new ArrayList<>();
- Iterator iterator = values.get(j).getAsJsonArray().iterator();
-
- while (iterator.hasNext())
- {
- String value = iterator.next().getAsString();
- list.add(value);
- }
-
- valuesList.add(list);
- }
-
- valuesMap.put(name, valuesList);
- }
- }
- catch (FileNotFoundException e)
- {}
-
- return valuesMap;
- }
-}
\ No newline at end of file
diff --git a/Plugins/mineplex-questmanager/src/mineplex/quest/daemon/QuestDaemon.java b/Plugins/mineplex-questmanager/src/mineplex/quest/daemon/QuestDaemon.java
deleted file mode 100644
index 15dbb2d55..000000000
--- a/Plugins/mineplex-questmanager/src/mineplex/quest/daemon/QuestDaemon.java
+++ /dev/null
@@ -1,180 +0,0 @@
-package mineplex.quest.daemon;
-
-import java.io.IOException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.logging.FileHandler;
-import java.util.logging.Formatter;
-import java.util.logging.LogRecord;
-import java.util.logging.Logger;
-
-import org.fusesource.jansi.AnsiConsole;
-
-import com.google.common.base.Throwables;
-
-import mineplex.serverdata.redis.messaging.PubSubJedisClient;
-import mineplex.serverdata.redis.messaging.PubSubRouter;
-import mineplex.serverdata.servers.ServerManager;
-
-import jline.console.ConsoleReader;
-
-/**
- * Entry point for a {@link QuestManager} service.
- */
-public class QuestDaemon
-{
-
- public static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("MM:dd:yyyy HH:mm:ss");
-
- private static final Logger _logger = Logger.getLogger("QuestManager");
- static
- {
- FileHandler fileHandler;
- try
- {
- fileHandler = new FileHandler("monitor.log", true);
- fileHandler.setFormatter(new Formatter()
- {
- @Override
- public String format(LogRecord record)
- {
- return record.getMessage() + "\n";
- }
- });
- _logger.addHandler(fileHandler);
- _logger.setUseParentHandlers(false);
- }
- catch (SecurityException | IOException e)
- {
- log("COuld not initialize log file!");
- log(e);
- }
- }
-
- private volatile boolean _alive = true;
-
- private QuestManager _questManager;
-
- public static void main(String[] args)
- {
- try
- {
- new QuestDaemon().run();
- System.exit(0);
- }
- catch (Throwable t)
- {
- log("Error in startup/console thread.");
- log(t);
- System.exit(1);
- }
- }
-
- private void run() throws Exception
- {
- log("Starting QuestDaemon...");
-
- _questManager = new QuestManager(new PubSubRouter(new PubSubJedisClient(
- ServerManager.getMasterConnection(), ServerManager.getSlaveConnection())));
- _questManager.start();
-
- Runtime.getRuntime().addShutdownHook(new Thread(() -> _questManager.onShutdown()));
-
- AnsiConsole.systemInstall();
- ConsoleReader consoleReader = new ConsoleReader();
- consoleReader.setExpandEvents(false);
-
- String command;
- while (_alive && (command = consoleReader.readLine(">")) != null)
- {
- try
- {
- if (command.equals("help"))
- {
- log("QuestManager commands:");
- log("stop: Shuts down this QuestManager instance.");
- log("clearactivequests: Clears active quests. New ones will be selected on this"
- + " instance's next iteration.");
- log("clearrecentrequests: Clear recently selected quests. This effectively allows "
- + "any quest to be set to active, even ones selected within the past few days.");
- log("getactivequests: Displays the currently active quests.");
- }
- else if (command.contains("stop"))
- {
- stopCommand();
- }
- else if (command.contains("clearactivequests"))
- {
- clearQuestsCommand();
- }
- else if (command.contains("clearrecentquests"))
- {
- clearRecentQuestsCommand();
- }
- else if (command.contains("getactivequests"))
- {
- getActiveQuestsCommand();
- }
- }
- catch (Throwable t)
- {
- log("Exception encountered while executing command " + command + ": "
- + t.getMessage());
- log(t);
- }
- }
- }
-
- private void stopCommand() throws Exception
- {
- log("Shutting down QuestDaemon...");
-
- _alive = false;
-
- System.exit(0);
- }
-
- private void clearQuestsCommand()
- {
- _questManager.clearActiveQuests();
-
- log("Cleared active quests. New ones will be selected on this instance's next iteration.");
- }
-
- private void clearRecentQuestsCommand()
- {
- _questManager.clearRecentlyActiveQuests();
-
- log("Cleared recently active quests. This means that any quest can be chosen to be active now, even ones selected within the past few days.");
- }
-
- private void getActiveQuestsCommand()
- {
- _questManager.displayActiveQuests();
- }
-
- public static void log(String message)
- {
- log(message, false);
- }
-
- public static void log(Throwable t)
- {
- log(Throwables.getStackTraceAsString(t));
- }
-
- public static void log(String message, boolean fileOnly)
- {
- _logger.info("[" + DATE_FORMAT.format(new Date()) + "] " + message);
-
- if (!fileOnly)
- {
- System.out.println("[" + DATE_FORMAT.format(new Date()) + "] " + message);
- }
- }
-
- public static String getLogPrefix(Object loggingClass)
- {
- return "[" + DATE_FORMAT.format(new Date()) + "] ";
- }
-}
diff --git a/Plugins/mineplex-questmanager/src/mineplex/quest/daemon/QuestManager.java b/Plugins/mineplex-questmanager/src/mineplex/quest/daemon/QuestManager.java
deleted file mode 100644
index 46686de98..000000000
--- a/Plugins/mineplex-questmanager/src/mineplex/quest/daemon/QuestManager.java
+++ /dev/null
@@ -1,277 +0,0 @@
-package mineplex.quest.daemon;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.time.LocalDate;
-import java.time.ZoneId;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Optional;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
-
-import mineplex.quest.common.Quest;
-import mineplex.quest.common.QuestRarity;
-import mineplex.quest.common.Quests;
-import mineplex.quest.common.redis.PubSubChannels;
-import mineplex.quest.common.redis.QuestRedisDataRepository;
-import mineplex.quest.common.redis.QuestTypeSerializer;
-import mineplex.quest.common.util.RandomCollection;
-import mineplex.serverdata.Region;
-import mineplex.serverdata.data.DataRepository;
-import mineplex.serverdata.redis.messaging.PubSubMessager;
-import mineplex.serverdata.servers.ServerManager;
-
-/**
- * A centralized service that handles setting {@link Quest} instances as active for servers to
- * display to players. Uses redis to notify servers of active quests changes and to store recently
- * selected quests.
- *
- * Uses {@link QuestRarity} to randomly select active quests based on relative weight.
- */
-public class QuestManager extends Thread
-{
-
- private static final int RECENT_QUESTS_EXPIRE_SECONDS = (int) TimeUnit.DAYS.toSeconds(5);
- private static final long SLEEP_MILLIS = TimeUnit.MINUTES.toMillis(1);
- private static final int NUM_ACTIVE_QUESTS = 5;
-
- private static final ZoneId EST_TIMEZONE = ZoneId.of("America/New_York");
- private static final String LAST_UPDATE_FILE = "last-quest-update.dat";
-
- // all quests, mapped from rarity weight to quest
- private final RandomCollection _quests = new RandomCollection<>();
-
- // currently active quests
- private final Set _activeQuests = Collections.synchronizedSet(new HashSet<>());
-
- // redis pubsub messager, used to publish active quests to servers
- private final PubSubMessager _pubSub;
-
- // redis repository to track recently selected quests, to prevent selecting a quest too soon
- // after it's been active
- private final DataRepository _recentlySelectedQuestsRepo;
-
- // the current date, e.g. the last date active quests were updated
- private volatile LocalDate _currentDate;
-
- // whether this instance is running or not
- private volatile boolean _alive = true;
-
- public QuestManager(PubSubMessager pubSub)
- {
- _pubSub = pubSub;
-
- _recentlySelectedQuestsRepo = new QuestRedisDataRepository(
- ServerManager.getMasterConnection(), ServerManager.getSlaveConnection(),
- Region.currentRegion(), "recently-selected-quests");
-
- _quests.addAll(Quests.QUESTS.stream().collect(
- Collectors.toMap(q -> q, q -> q.getRarity().getWeight())));
-
- loadLastActiveQuests();
-
- if (_activeQuests.size() > 0)
- {
- QuestDaemon.log("Active quests loaded from file:");
- _activeQuests.forEach(quest -> QuestDaemon.log(quest.toString()));
- }
-
- // listen for servers requesting active quests on startup
- _pubSub.subscribe(PubSubChannels.QUEST_REQUEST_BASE, this::handleQuestRequest);
- }
-
- private void handleQuestRequest(String channel, String message)
- {
- QuestDaemon.log("Quests requestesd by server: " + message);
- // first make sure we have some active quests selected
- if (_activeQuests.isEmpty())
- {
- selectRandomQuests();
- }
-
- // send active quests to the server
- String server = message;
- publishActiveQuests(PubSubChannels.QUEST_REQUEST_BASE + server);
- }
-
- /**
- * Loads last set active quests & the date they were set to active from a flat file, if the file
- * exists.
- */
- private void loadLastActiveQuests()
- {
- File file = new File(LAST_UPDATE_FILE);
- if (!file.exists())
- {
- _currentDate = LocalDate.now(EST_TIMEZONE);
-
- return;
- }
-
- try
- {
- List lines = Files.readAllLines(Paths.get(file.getAbsolutePath()));
- _currentDate = LocalDate.parse(lines.get(0));
-
- if (lines.size() > 1)
- {
- for (int i = 1; i < lines.size(); i++)
- {
- int uniqueId = Integer.parseInt(lines.get(i));
- Optional quest = Quests.fromId(uniqueId);
- if (!quest.isPresent())
- {
- QuestDaemon.log("Tried to load active quest that doesn't exist: " + uniqueId);
- continue;
- }
-
- _activeQuests.add(quest.get());
- }
- }
- }
- catch (Exception e)
- {
- QuestDaemon.log(
- "Exception encountered while loading last updated quests: " + e.getMessage());
- QuestDaemon.log(e);
-
- _currentDate = LocalDate.now(EST_TIMEZONE);
- }
- }
-
- @Override
- public void run()
- {
- try
- {
- while (_alive)
- {
- // purge recently selected quests repo of expired entries
- _recentlySelectedQuestsRepo.clean();
-
- LocalDate now = LocalDate.now(EST_TIMEZONE);
- // check if date has changed; if so we need to choose new quests
- if (_currentDate.isBefore(now) || _activeQuests.isEmpty())
- {
- QuestDaemon.log("Updating active quests...");
- _currentDate = now;
-
- // select new quests
- selectRandomQuests();
-
- // publish new quests
- publishActiveQuests(PubSubChannels.QUEST_SUPPLIER_CHANNEL);
-
- QuestDaemon.log("Done updating active quests.");
- }
-
- // take a small break, important so CPU isn't constantly running
- Thread.sleep(SLEEP_MILLIS);
- }
- }
- catch (InterruptedException e)
- {
- QuestDaemon.log("Exception encountered updating active quests repo: " + e.getMessage());
- QuestDaemon.log(e);
- }
- }
-
- private void publishActiveQuests(String channel)
- {
- QuestDaemon.log("publishing active quests to channel: " + channel);
- QuestDaemon.log("Active quests: " + serialize(_activeQuests));
- _pubSub.publish(channel,
- serialize(_activeQuests));
- }
-
- /**
- * Called on shutdown of this service. Writes the date quests were last updated to a file, so
- * this service will know whether to update them or not on the next startup. This is all that's
- * needed to keep active quests in a sane state because they are stored in redis.
- */
- public void onShutdown()
- {
- _alive = false;
-
- try
- {
- File file = new File(LAST_UPDATE_FILE);
- if (!file.exists())
- {
- file.createNewFile();
- }
-
- List lines = new ArrayList<>();
-
- // add active quests date
- lines.add(_currentDate.toString());
-
- // add currently active quests
- _activeQuests.stream().map(Quest::getDataId).forEach(lines::add);
-
- Files.write(Paths.get(file.getAbsolutePath()), lines);
- }
- catch (IOException e)
- {
- QuestDaemon.log("Exception encountered saving " + LAST_UPDATE_FILE + " file: "
- + e.getMessage());
- QuestDaemon.log(e);
- }
- }
-
- protected void clearActiveQuests()
- {
- _activeQuests.clear();
- }
-
- protected void clearRecentlyActiveQuests()
- {
- _recentlySelectedQuestsRepo.getElements()
- .forEach(_recentlySelectedQuestsRepo::removeElement);
- }
-
- protected void displayActiveQuests()
- {
- QuestDaemon.log("Active quests:");
- _activeQuests.forEach(q -> QuestDaemon.log(q.toString()));
- }
-
- private void selectRandomQuests()
- {
- if (!_activeQuests.isEmpty())
- {
- _activeQuests.clear();
- }
-
- while (_activeQuests.size() < NUM_ACTIVE_QUESTS && _activeQuests.size() < _quests.values().size())
- {
- Quest q = _quests.next();
- // select random weighted quest, ignore those recently selected
- if (!q.shouldRotate() || _activeQuests.contains(q)
- || _recentlySelectedQuestsRepo.elementExists(q.getDataId()))
- {
- // quest is already active or it's been active recently
- continue;
- }
-
- // add active quest
- _activeQuests.add(q);
-
- QuestDaemon.log("Selected quest: " + q.getName());
-
- // flag quest as recently selected
- _recentlySelectedQuestsRepo.addElement(q, RECENT_QUESTS_EXPIRE_SECONDS);
- }
- }
-
- private String serialize(Set quests)
- {
- return QuestTypeSerializer.QUEST_GSON.toJson(quests, QuestTypeSerializer.QUEST_TYPE);
- }
-}