Remove the QuestManager code from PC
This commit is contained in:
parent
4fb6608c3f
commit
e45deb127c
|
@ -1,69 +0,0 @@
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
<parent>
|
|
||||||
<groupId>com.mineplex</groupId>
|
|
||||||
<artifactId>mineplex-parent</artifactId>
|
|
||||||
<version>dev-SNAPSHOT</version>
|
|
||||||
</parent>
|
|
||||||
<artifactId>mineplex-questmanager</artifactId>
|
|
||||||
<name>Mineplex.questmanager</name>
|
|
||||||
<description>A centralized service that selects daily quests</description>
|
|
||||||
|
|
||||||
<properties>
|
|
||||||
<version.guava>23.0</version.guava>
|
|
||||||
<version.jline>2.12</version.jline>
|
|
||||||
<version.spigot>1.8.8-1.9-SNAPSHOT</version.spigot>
|
|
||||||
</properties>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>${project.groupId}</groupId>
|
|
||||||
<artifactId>mineplex-serverdata</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.google.guava</groupId>
|
|
||||||
<artifactId>guava</artifactId>
|
|
||||||
<version>${version.guava}</version>
|
|
||||||
<scope>compile</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>jline</groupId>
|
|
||||||
<artifactId>jline</artifactId>
|
|
||||||
<version>${version.jline}</version>
|
|
||||||
<scope>compile</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.mineplex</groupId>
|
|
||||||
<artifactId>spigot</artifactId>
|
|
||||||
<scope>provided</scope>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-shade-plugin</artifactId>
|
|
||||||
<configuration>
|
|
||||||
<minimizeJar>false</minimizeJar>
|
|
||||||
<transformers>
|
|
||||||
<transformer
|
|
||||||
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
|
||||||
<mainClass>mineplex.quest.daemon.QuestDaemon</mainClass>
|
|
||||||
</transformer>
|
|
||||||
</transformers>
|
|
||||||
</configuration>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<phase>package</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>shade</goal>
|
|
||||||
</goals>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
</project>
|
|
|
@ -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.
|
|
||||||
* <p>
|
|
||||||
* 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<Quest> _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<Quest> 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<Quest> get()
|
|
||||||
{
|
|
||||||
_lock.readLock().lock();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return ImmutableSet.copyOf(_quests);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_lock.readLock().unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Optional<Quest> getById(int uniquePersistentId)
|
|
||||||
{
|
|
||||||
_lock.readLock().lock();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return _quests.stream().filter(q -> q.getUniqueId() == uniquePersistentId).findFirst();
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_lock.readLock().unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<Quest> _to;
|
|
||||||
|
|
||||||
public QuestsUpdatedEvent(Set<Quest> to)
|
|
||||||
{
|
|
||||||
_to = to;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return The currently active quests.
|
|
||||||
*/
|
|
||||||
public Set<Quest> getActiveQuests()
|
|
||||||
{
|
|
||||||
return _to;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public HandlerList getHandlers()
|
|
||||||
{
|
|
||||||
return HANDLERS;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static HandlerList getHandlerList()
|
|
||||||
{
|
|
||||||
return HANDLERS;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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 + "]";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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 <b>should not be changed</b>.
|
|
||||||
*
|
|
||||||
* @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}.
|
|
||||||
* <p>
|
|
||||||
* 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 <code>true</code> if this quest should be selected as an active quest, or
|
|
||||||
* <code>false</code> otherwise.
|
|
||||||
*/
|
|
||||||
boolean shouldRotate();
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<Set<Quest>>
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an immutable set containing all of the currently active quests.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
Set<Quest> 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<Quest> getById(int uniquePersistentId);
|
|
||||||
|
|
||||||
}
|
|
|
@ -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.
|
|
||||||
* <p>
|
|
||||||
* 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<Quest> QUESTS;
|
|
||||||
|
|
||||||
static
|
|
||||||
{
|
|
||||||
ImmutableSet.Builder<Quest> builder = ImmutableSet.<Quest>builder();
|
|
||||||
|
|
||||||
Map<String, List<List<String>>> sheets = UtilGoogleSheet.getSheetData(ALL_QUESTS_FILE);
|
|
||||||
|
|
||||||
List<List<String>> 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<String> 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<Quest> fromId(int uniqueId)
|
|
||||||
{
|
|
||||||
return QUESTS.stream().filter(quest -> quest.getUniqueId() == uniqueId).findFirst();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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:";
|
|
||||||
|
|
||||||
}
|
|
|
@ -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<Quest>
|
|
||||||
{
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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<Quest>.
|
|
||||||
*/
|
|
||||||
public class QuestTypeDeserialiazer implements JsonDeserializer<Set<Quest>>
|
|
||||||
{
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<Quest> 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));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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<Quest> into a {@link JsonElement} String.
|
|
||||||
*/
|
|
||||||
public class QuestTypeSerializer implements JsonSerializer<Set<Quest>>
|
|
||||||
{
|
|
||||||
|
|
||||||
@SuppressWarnings("serial")
|
|
||||||
public static final Type QUEST_TYPE = new TypeToken<Set<Quest>>(){}.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<Quest> 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());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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.
|
|
||||||
* <p>
|
|
||||||
* Intended to be thread-safe.
|
|
||||||
*
|
|
||||||
* @param <E> The generic type parameter of the elements.
|
|
||||||
*/
|
|
||||||
public class RandomCollection<E>
|
|
||||||
{
|
|
||||||
|
|
||||||
private final NavigableMap<Double, E> _map = Collections.synchronizedNavigableMap(new TreeMap<Double, E>());
|
|
||||||
private final Random _random;
|
|
||||||
|
|
||||||
private double total = 0;
|
|
||||||
|
|
||||||
public RandomCollection(Random random)
|
|
||||||
{
|
|
||||||
_random = random;
|
|
||||||
}
|
|
||||||
|
|
||||||
public RandomCollection()
|
|
||||||
{
|
|
||||||
this(ThreadLocalRandom.current());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addAll(Map<E, Double> 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<E> values()
|
|
||||||
{
|
|
||||||
return _map.values();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<String, List<List<String>>> getSheetData(String name)
|
|
||||||
{
|
|
||||||
return getSheetData(new File(DATA_STORE_DIR + File.separator + name + ".json"));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Map<String, List<List<String>>> getSheetData(File file)
|
|
||||||
{
|
|
||||||
if (!file.exists())
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, List<List<String>>> 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<List<String>> valuesList = new ArrayList<>(values.size());
|
|
||||||
|
|
||||||
for (int j = 0; j < values.size(); j++)
|
|
||||||
{
|
|
||||||
List<String> list = new ArrayList<>();
|
|
||||||
Iterator<JsonElement> 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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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()) + "] ";
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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.
|
|
||||||
* <p>
|
|
||||||
* 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<Quest> _quests = new RandomCollection<>();
|
|
||||||
|
|
||||||
// currently active quests
|
|
||||||
private final Set<Quest> _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<Quest> _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<String> 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> 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<String> 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<Quest> quests)
|
|
||||||
{
|
|
||||||
return QuestTypeSerializer.QUEST_GSON.toJson(quests, QuestTypeSerializer.QUEST_TYPE);
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue