Report Feature Improvements (#269)

Report Feature Improvements
This commit is contained in:
Shaun Bennett 2016-11-16 22:52:14 -05:00 committed by GitHub
commit cbc9aeedc6
34 changed files with 954 additions and 294 deletions

View File

@ -20,6 +20,8 @@ import mineplex.core.report.data.Report;
*/
public class SnapshotManager
{
public static final int MAX_SNAPSHOTS = 5;
// There aren't any List or Set caching implementations
// For an easy work around, we store values as the Key
// For the value we just use some dummy object
@ -38,7 +40,7 @@ public class SnapshotManager
_snapshotRepository = snapshotRepository;
}
public SnapshotRepository getSnapshotRepository()
public SnapshotRepository getRepository()
{
return _snapshotRepository;
}
@ -74,21 +76,21 @@ public class SnapshotManager
* Does not include PMs unless sender or receiver is in the exclusions collection.
*
* @param accountId the account to search for messages involved in
* @param pmIdWhitelist a list of account ids of which to include PMs of
* @param pmWhitelistIds a list of account ids of which to include PMs of
* @return the messages that the account is involved in
*/
public Set<SnapshotMessage> getMessagesInvolving(int accountId, Collection<Integer> pmIdWhitelist)
public Set<SnapshotMessage> getMessagesInvolving(int accountId, Collection<Integer> pmWhitelistIds)
{
return getMessagesInvolving(accountId).stream()
.filter(message -> includeMessage(message, pmIdWhitelist))
.filter(message -> includeMessage(message, pmWhitelistIds))
.collect(Collectors.toCollection(TreeSet::new));
}
private boolean includeMessage(SnapshotMessage message, Collection<Integer> pmExclusions)
private boolean includeMessage(SnapshotMessage message, Collection<Integer> pmWhitelistIds)
{
return message.getType() != MessageType.PM ||
pmExclusions.contains(message.getSenderId()) ||
!Collections.disjoint(message.getRecipientIds(), pmExclusions);
pmWhitelistIds.contains(message.getSenderId()) ||
!Collections.disjoint(message.getRecipientIds(), pmWhitelistIds);
}
/**
@ -134,18 +136,19 @@ public class SnapshotManager
public CompletableFuture<SnapshotMetadata> saveReportSnapshot(Report report, Collection<SnapshotMessage> messages)
{
return _snapshotRepository
.saveReportSnapshot(report, messages)
.whenComplete((snapshotMetadata, throwable) ->
{
if (throwable == null)
{
report.setSnapshotMetadata(snapshotMetadata);
}
else
{
_javaPlugin.getLogger().log(Level.SEVERE, "Error whilst saving snapshot.", throwable);
}
});
SnapshotMetadata snapshotMetadata = report.getSnapshotMetadata().orElseThrow(() ->
new IllegalStateException("Report does not have associated snapshot."));
return _snapshotRepository.insertMessages(snapshotMetadata.getId(), messages).whenComplete(((aVoid, throwable) ->
{
if (throwable == null)
{
report.setSnapshotMetadata(snapshotMetadata);
}
else
{
_javaPlugin.getLogger().log(Level.SEVERE, "Error whilst saving snapshot.", throwable);
}
})).thenApply(aVoid -> snapshotMetadata);
}
}

View File

@ -11,6 +11,7 @@ import org.bukkit.plugin.java.JavaPlugin;
import mineplex.core.MiniPlugin;
import mineplex.core.account.CoreClientManager;
import mineplex.core.chatsnap.command.ChatSnapCommand;
import mineplex.core.message.PrivateMessageEvent;
/**
@ -36,7 +37,7 @@ public class SnapshotPlugin extends MiniPlugin
@Override
public void addCommands()
{
addCommand(new ChatSnapCommand(this));
}
@EventHandler(priority = EventPriority.MONITOR)

View File

@ -6,15 +6,16 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Level;
import java.util.logging.Logger;
import mineplex.core.common.util.UtilTime;
import mineplex.core.report.data.Report;
import mineplex.serverdata.database.DBPool;
/**
@ -65,13 +66,14 @@ public class SnapshotRepository
return token;
}
private static final String INSERT_SNAPSHOT = "INSERT INTO snapshots (token, creator) VALUES (?, ?);";
private static final String INSERT_SNAPSHOT = "INSERT INTO snapshots (token, creatorId) VALUES (?, ?);";
private static final String INSERT_MESSAGE = "INSERT INTO snapshotMessages (senderId, `server`, `time`, message, snapshotType) VALUES (?, ?, ?, ?, ?);";
private static final String INSERT_MESSAGE_RECIPIENT = "INSERT INTO snapshotRecipients (messageId, recipientId) VALUES (?, ?);";
private static final String INSERT_MESSAGE_MAPPING = "INSERT INTO snapshotMessageMap (snapshotId, messageId) VALUES (?, ?);";
private static final String GET_ID_FROM_TOKEN = "SELECT snapshots.id FROM snapshots WHERE snapshots.token = ?;";
private static final String GET_METADATA = "SELECT token, creator FROM snapshots WHERE id = ?;";
private static final String GET_METADATA = "SELECT token, creatorId FROM snapshots WHERE id = ?;";
private static final String SET_TOKEN = "UPDATE snapshots SET token = ? WHERE id = ?;";
private static final String GET_USER_SNAPSHOTS = "SELECT snapshots.id FROM snapshots WHERE snapshots.creatorId = ?;";
private final String _serverName;
private final Logger _logger;
@ -82,42 +84,6 @@ public class SnapshotRepository
_logger = logger;
}
public CompletableFuture<SnapshotMetadata> saveReportSnapshot(Report report, Collection<SnapshotMessage> messages)
{
return CompletableFuture.supplyAsync(() ->
{
try (Connection connection = DBPool.getAccount().getConnection())
{
SnapshotMetadata snapshotMetadata = report.getSnapshotMetadata().orElseThrow(() ->
new IllegalStateException("Report does not have associated snapshot."));
insertMessages(snapshotMetadata.getId(), messages, connection);
return snapshotMetadata;
}
catch (SQLException e)
{
throw new RuntimeException(e);
}
});
}
public CompletableFuture<SnapshotMetadata> saveSnapshot(Collection<SnapshotMessage> messages)
{
return CompletableFuture.supplyAsync(() ->
{
try (Connection connection = DBPool.getAccount().getConnection())
{
SnapshotMetadata snapshotMetadata = createSnapshot(connection, null);
insertMessages(snapshotMetadata.getId(), messages, connection);
return snapshotMetadata;
}
catch (SQLException e)
{
throw new RuntimeException(e);
}
});
}
public CompletableFuture<SnapshotMetadata> createSnapshot(Integer creatorAccountId)
{
return CompletableFuture.supplyAsync(() ->
@ -218,7 +184,7 @@ public class SnapshotRepository
setToken(connection, snapshotId, token);
}
Integer creatorId = resultSet.getInt("creator");
Integer creatorId = resultSet.getInt("creatorId");
if (resultSet.wasNull()) creatorId = null;
return new SnapshotMetadata(snapshotId, token, creatorId);
@ -244,6 +210,40 @@ public class SnapshotRepository
return future;
}
public CompletableFuture<List<Integer>> getUserSnapshots(int creatorId)
{
CompletableFuture<List<Integer>> future = CompletableFuture.supplyAsync(() ->
{
try (Connection connection = DBPool.getAccount().getConnection())
{
PreparedStatement preparedStatement = connection.prepareStatement(GET_USER_SNAPSHOTS);
preparedStatement.setInt(1, creatorId);
ResultSet resultSet = preparedStatement.executeQuery();
List<Integer> snapshotIds = new ArrayList<>();
while (resultSet.next())
{
snapshotIds.add(resultSet.getInt("id"));
}
return snapshotIds;
}
catch (SQLException e)
{
throw new RuntimeException(e);
}
});
future.exceptionally(throwable ->
{
_logger.log(Level.SEVERE, "Error getting snapshots for user " + creatorId + ".");
return new ArrayList<>();
});
return future;
}
private void setToken(Connection connection, int snapshotId, String token) throws SQLException
{
try (PreparedStatement setTokenStatement = connection.prepareStatement(SET_TOKEN))
@ -254,36 +254,9 @@ public class SnapshotRepository
}
}
private void insertMessages(int snapshotId, Collection<SnapshotMessage> messages, Connection connection) throws SQLException
public CompletableFuture<Void> insertMessages(int snapshotId, Collection<SnapshotMessage> messages)
{
try (PreparedStatement insertSnapshotStatement = connection.prepareStatement(INSERT_MESSAGE, new String[]{"id"}))
{
try (PreparedStatement insertRecipientStatement = connection.prepareStatement(INSERT_MESSAGE_RECIPIENT))
{
try (PreparedStatement insertMappingStatement = connection.prepareStatement(INSERT_MESSAGE_MAPPING))
{
for (SnapshotMessage message : messages)
{
try
{
insertMessage(insertSnapshotStatement, insertRecipientStatement, insertMappingStatement, snapshotId, message);
}
catch (Exception e)
{
_logger.log(Level.SEVERE, "Error inserting snapshot message.", e);
}
}
insertRecipientStatement.executeBatch();
insertMappingStatement.executeBatch();
}
}
}
}
public CompletableFuture<Void> insertMessage(int snapshotId, SnapshotMessage message)
{
return CompletableFuture.supplyAsync(() ->
CompletableFuture<Void> future = CompletableFuture.supplyAsync(() ->
{
try (Connection connection = DBPool.getAccount().getConnection())
{
@ -293,7 +266,21 @@ public class SnapshotRepository
{
try (PreparedStatement insertMappingStatement = connection.prepareStatement(INSERT_MESSAGE_MAPPING))
{
insertMessage(insertSnapshotStatement, insertRecipientStatement, insertMappingStatement, snapshotId, message);
for (SnapshotMessage message : messages)
{
try
{
insertMessage(insertSnapshotStatement, insertRecipientStatement, insertMappingStatement, snapshotId, message);
}
catch (Exception e)
{
_logger.log(Level.SEVERE, "Error inserting snapshot message.", e);
}
}
insertRecipientStatement.executeBatch();
insertMappingStatement.executeBatch();
return null;
}
}
}
@ -302,9 +289,15 @@ public class SnapshotRepository
{
throw new RuntimeException(e);
}
});
future.exceptionally(throwable ->
{
_logger.log(Level.SEVERE, "Error whilst inserting messages into snapshot.", throwable);
return null;
});
return future;
}
private void insertMessage(PreparedStatement insertSnapshotStatement, PreparedStatement insertRecipientStatement, PreparedStatement insertMappingStatement, int snapshotId, SnapshotMessage message) throws SQLException

View File

@ -0,0 +1,71 @@
package mineplex.core.chatsnap.command;
import java.util.Set;
import org.bukkit.entity.Player;
import mineplex.core.chatsnap.SnapshotManager;
import mineplex.core.chatsnap.SnapshotMessage;
import mineplex.core.chatsnap.SnapshotPlugin;
import mineplex.core.chatsnap.SnapshotRepository;
import mineplex.core.command.CommandBase;
import mineplex.core.common.Rank;
import mineplex.core.common.jsonchat.ClickEvent;
import mineplex.core.common.jsonchat.JsonMessage;
import mineplex.core.common.util.C;
import mineplex.core.common.util.F;
import mineplex.core.common.util.UtilPlayer;
/**
* A command which when executed will create a chat log which will be viewable online.
*/
public class ChatSnapCommand extends CommandBase<SnapshotPlugin>
{
public ChatSnapCommand(SnapshotPlugin plugin)
{
super(plugin, Rank.TITAN, "chatsnap");
}
@Override
public void Execute(Player player, String[] args)
{
if (args == null || args.length == 0)
{
SnapshotManager manager = Plugin.getSnapshotManager();
SnapshotRepository repository = manager.getRepository();
int accountId = _commandCenter.GetClientManager().getAccountId(player);
Plugin.getSnapshotManager().getRepository().getUserSnapshots(accountId).thenAccept(snapshotIds ->
{
if (snapshotIds.size() < SnapshotManager.MAX_SNAPSHOTS)
{
Set<SnapshotMessage> messages = manager.getMessagesInvolving(accountId);
repository.createSnapshot(accountId).thenAccept(snapshotMetadata ->
{
String token = snapshotMetadata.getToken().orElseThrow(() ->
new IllegalStateException("Snapshot doesn't have a token."));
repository.insertMessages(snapshotMetadata.getId(), messages).join();
UtilPlayer.message(player, F.main(Plugin.getName(), "Snapshot successfully created."));
new JsonMessage(F.main(Plugin.getName(), "Your snapshot token is: "))
.extra(F.elem(token))
.click(ClickEvent.OPEN_URL, SnapshotRepository.getURL(token))
.sendToPlayer(player);
});
}
else
{
UtilPlayer.message(player, F.main(Plugin.getName(),
C.cRed + "Cannot create snapshot, you have reached the limit."));
}
});
}
else
{
UtilPlayer.message(player, F.main(Plugin.getName(), C.cRed + "Invalid Usage: " + F.elem("/" + _aliasUsed)));
}
}
}

View File

@ -1,4 +1,4 @@
package mineplex.core.chatsnap.command;
package mineplex.core.chatsnap.redis;
import java.util.Set;

View File

@ -1,4 +1,4 @@
package mineplex.core.chatsnap.command;
package mineplex.core.chatsnap.redis;
import java.util.Set;
import mineplex.core.chatsnap.SnapshotMessage;
@ -35,7 +35,7 @@ public class PushSnapshotsHandler implements CommandCallback
if (messages.size() > 0)
{
_reportManager.getReportRepository().getReport(reportId).thenAccept(reportOptional ->
_reportManager.getRepository().getReport(reportId).thenAccept(reportOptional ->
{
if (reportOptional.isPresent())
{

View File

@ -38,7 +38,7 @@ public class ReportHandlerTask extends BukkitRunnable
private CompletableFuture<Optional<Report>> getReport()
{
return _reportManager.getReportRepository().getReport(_reportId);
return _reportManager.getRepository().getReport(_reportId);
}
public void start(JavaPlugin plugin)
@ -70,7 +70,7 @@ public class ReportHandlerTask extends BukkitRunnable
{
if (isActive)
{
_reportManager.getReportRepository().getAccountName(report.getSuspectId())
_reportManager.getRepository().getAccountName(report.getSuspectId())
.thenAccept(suspectName ->
{
String prefix = F.main(ReportManager.getReportPrefix(reportId), "");
@ -121,7 +121,7 @@ public class ReportHandlerTask extends BukkitRunnable
int handlerId = handlerIdOptional.get();
JsonMessage finalJsonMessage = jsonMessage;
_reportManager.getReportRepository().getAccountUUID(handlerId).thenAccept(handlerUUID ->
_reportManager.getRepository().getAccountUUID(handlerId).thenAccept(handlerUUID ->
{
if (handlerUUID != null)
{
@ -165,7 +165,7 @@ public class ReportHandlerTask extends BukkitRunnable
for (ReportMessage reportMessage : reportMessages)
{
// this is blocking, but that's okay as it will only be called asynchronously
String reporterName = _reportManager.getReportRepository().getAccountName(reportMessage.getReporterId()).join();
String reporterName = _reportManager.getRepository().getAccountName(reportMessage.getReporterId()).join();
// triple backslashes so this translates to valid JSON
output[count++] = String.format("%4$s(%d%4$s) %s%s%s - \\\"%s%s%4$s\\\"", count, C.cGold, reporterName, C.cGray, C.cPurple, reportMessage.getMessage());

View File

@ -20,8 +20,8 @@ import mineplex.core.account.CoreClient;
import mineplex.core.account.CoreClientManager;
import mineplex.core.chatsnap.SnapshotManager;
import mineplex.core.chatsnap.SnapshotMetadata;
import mineplex.core.chatsnap.command.PushSnapshotsCommand;
import mineplex.core.chatsnap.command.PushSnapshotsHandler;
import mineplex.core.chatsnap.redis.PushSnapshotsCommand;
import mineplex.core.chatsnap.redis.PushSnapshotsHandler;
import mineplex.core.command.CommandCenter;
import mineplex.core.common.jsonchat.ChildJsonMessage;
import mineplex.core.common.jsonchat.JsonMessage;
@ -34,6 +34,7 @@ import mineplex.core.portal.Portal;
import mineplex.core.punish.Category;
import mineplex.core.punish.Punish;
import mineplex.core.punish.PunishClient;
import mineplex.core.report.data.metrics.ReportMetricsRepository;
import mineplex.core.report.redis.HandlerNotification;
import mineplex.core.report.data.Report;
import mineplex.core.report.data.ReportMessage;
@ -41,6 +42,7 @@ import mineplex.core.report.data.ReportUser;
import mineplex.core.report.data.ReportUserRepository;
import mineplex.core.report.data.ReportRepository;
import mineplex.core.report.redis.ReportersNotification;
import mineplex.serverdata.Region;
import mineplex.serverdata.commands.ServerCommandManager;
import static com.google.common.base.Preconditions.checkNotNull;
@ -52,32 +54,36 @@ public class ReportManager
private static final String NAME = "Report";
private static final int INITIAL_PRIORITY = 15;
private static final int ABUSE_BAN_THRESHOLD = 3;
public static final int MAXIMUM_REPORTS = 5;
private final JavaPlugin _plugin;
private final SnapshotManager _snapshotManager;
private final CoreClientManager _clientManager;
private final IncognitoManager _incognitoManager;
private final Punish _punish;
private final Region _region;
private final String _serverName;
private final int _serverWeight;
private final ReportRepository _reportRepository;
private final ReportUserRepository _reportUserRepository;
private final ReportUserRepository _userRepository;
private final ReportMetricsRepository _metricsRepository;
public ReportManager(JavaPlugin plugin, SnapshotManager snapshotManager, CoreClientManager clientManager,
IncognitoManager incognitoManager, Punish punish, String serverName, int serverWeight)
IncognitoManager incognitoManager, Punish punish, Region region, String serverName, int serverWeight)
{
_plugin = plugin;
_snapshotManager = snapshotManager;
_clientManager = clientManager;
_incognitoManager = incognitoManager;
_punish = punish;
_region = region;
_serverName = serverName;
_serverWeight = serverWeight;
_reportRepository = new ReportRepository(this, _plugin.getLogger());
_reportUserRepository = new ReportUserRepository(plugin);
_reportRepository = new ReportRepository(this, region, _plugin.getLogger());
_userRepository = new ReportUserRepository(plugin);
_metricsRepository = new ReportMetricsRepository(_plugin.getLogger());
ServerCommandManager commandManager = ServerCommandManager.getInstance();
ReportRedisManager notificationCallback = new ReportRedisManager(this, _serverName);
@ -93,15 +99,35 @@ public class ReportManager
}
/**
* Gets the {@link ReportRepository} we are using.
* Gets the {@link ReportRepository} this instance is using.
*
* @return the repository
*/
public ReportRepository getReportRepository()
public ReportRepository getRepository()
{
return _reportRepository;
}
/**
* Gets the {@link ReportUserRepository} this instance is using.
*
* @return the repository
*/
public ReportUserRepository getUserRepository()
{
return _userRepository;
}
/**
* Gets the {@link ReportMetricsRepository} this instance is using.
*
* @return the repository
*/
public ReportMetricsRepository getMetricsRepository()
{
return _metricsRepository;
}
/**
* Creates a new report or adds to an existing one.
*
@ -149,7 +175,7 @@ public class ReportManager
// create snapshot id ahead of time
if (category == ReportCategory.CHAT_ABUSE)
{
SnapshotMetadata snapshotMetadata = _snapshotManager.getSnapshotRepository().createSnapshot(null).join();
SnapshotMetadata snapshotMetadata = _snapshotManager.getRepository().createSnapshot(null).join();
report.setSnapshotMetadata(snapshotMetadata);
}
@ -168,12 +194,12 @@ public class ReportManager
reportOptional.orElseGet(() ->
{
_plugin.getLogger().log(Level.WARNING, "Report #%d couldn't be fetched, opening new report.");
return new Report(suspectId, category);
return new Report(suspectId, category, _region);
}));
}
else
{
return CompletableFuture.completedFuture(new Report(suspectId, category));
return CompletableFuture.completedFuture(new Report(suspectId, category, _region));
}
});
}
@ -219,15 +245,18 @@ public class ReportManager
new ReportHandlerTask(this, reportId).start(_plugin);
});
if (!_incognitoManager.Get(reportHandler).Status)
if (report.getCategory() != ReportCategory.CHAT_ABUSE)
{
_incognitoManager.toggle(reportHandler);
}
if (!_incognitoManager.Get(reportHandler).Status)
{
_incognitoManager.toggle(reportHandler);
}
String lastServer = report.getLatestMessage().getServer();
if (!_serverName.equals(lastServer))
{
Portal.transferPlayer(reportHandler.getName(), lastServer);
String lastServer = report.getLatestMessage().getServer();
if (!_serverName.equals(lastServer))
{
Portal.transferPlayer(reportHandler.getName(), lastServer);
}
}
reportHandler.sendMessage(
@ -314,12 +343,10 @@ public class ReportManager
}
jsonMessage = jsonMessage.add(F.main(prefix, "Reason: " + F.elem(reason)));
new ReportersNotification(ids, jsonMessage).publish();
_reportRepository.clearCache(reportId);
});
});
_reportRepository.clearCache(reportId);
}
catch (Throwable throwable)
{
@ -446,7 +473,7 @@ public class ReportManager
*/
public CompletableFuture<Boolean> isActiveReport(long reportId)
{
return getReportRepository().getReport(reportId).thenCompose(reportOptional ->
return getRepository().getReport(reportId).thenCompose(reportOptional ->
{
if (reportOptional.isPresent())
{
@ -546,26 +573,41 @@ public class ReportManager
.thenApply(UtilCollections::unboxPresent)
.thenApply(reports ->
{
Report report = null;
int size = reports.size();
if (size == 1)
if (size == 0)
{
report = reports.get(0);
return Optional.empty();
}
else if (size > 1)
else if (size == 1)
{
return Optional.of(reports.get(0));
}
else
{
throw new IllegalStateException("Account is handling multiple reports.");
}
return Optional.ofNullable(report);
});
future.exceptionally(throwable -> Optional.empty());
future.exceptionally(throwable ->
{
_plugin.getLogger().log(Level.SEVERE, "Error whilst fetching report being handled by: " + accountId + ".");
return Optional.empty();
});
return future;
}
public CompletableFuture<List<Report>> getOpenReports(int reporterId)
{
return _reportRepository.getOpenReports(reporterId)
.thenApply(reportIds ->
reportIds.stream().map(_reportRepository::getReport).collect(Collectors.toList()))
.thenCompose(UtilFuture::sequence)
.thenApply(UtilCollections::unboxPresent)
.thenCompose(reports -> UtilFuture.filter(reports, this::isActiveReport));
}
/**
* Calculates the priority of a report.
* This takes many parameters into account including:
@ -587,7 +629,7 @@ public class ReportManager
for (Map.Entry<Integer, ReportMessage> entry : report.getMessages().entrySet())
{
int accountId = entry.getKey();
ReportUser user = _reportUserRepository.getUser(accountId).join();
ReportUser user = _userRepository.getUser(accountId).join();
int categoryReputation = user.getReputation(report.getCategory());
ReportMessage message = entry.getValue();

View File

@ -10,24 +10,25 @@ import mineplex.core.report.command.ReportCloseCommand;
import mineplex.core.report.command.ReportCommand;
import mineplex.core.report.command.ReportHandleCommand;
import mineplex.core.report.command.ReportInfoCommand;
import mineplex.core.report.command.ReportStatsCommand;
import mineplex.core.report.command.ReportHistoryCommand;
import mineplex.core.report.command.ReportMetricsCommand;
/**
* Main class for this module, handles initialization and disabling of the module.
*/
public class ReportPlugin extends MiniPlugin
{
private final ReportManager _reportManager;
private final ReportManager _manager;
public ReportPlugin(JavaPlugin plugin, ReportManager reportManager)
public ReportPlugin(JavaPlugin plugin, ReportManager manager)
{
super("Report", plugin);
_reportManager = reportManager;
_manager = manager;
}
public ReportManager getReportManager()
public ReportManager getManager()
{
return _reportManager;
return _manager;
}
@Override
@ -36,19 +37,20 @@ public class ReportPlugin extends MiniPlugin
addCommand(new ReportCommand(this));
addCommand(new ReportHandleCommand(this));
addCommand(new ReportCloseCommand(this));
addCommand(new ReportStatsCommand(this));
addCommand(new ReportHistoryCommand(this));
addCommand(new ReportInfoCommand(this));
addCommand(new ReportMetricsCommand(this));
}
@EventHandler
public void onPlayerJoin(PlayerJoinEvent e)
{
_reportManager.onPlayerJoin(e.getPlayer());
_manager.onPlayerJoin(e.getPlayer());
}
@EventHandler
public void onPlayerQuit(PlayerQuitEvent e)
{
_reportManager.onPlayerQuit(e.getPlayer());
_manager.onPlayerQuit(e.getPlayer());
}
}

View File

@ -33,13 +33,13 @@ public class ReportRedisManager implements CommandCallback
{
HandlerNotification reportNotification = (HandlerNotification) command;
_reportManager.getReportRepository().getReport(reportNotification.getReportId()).thenAccept(report ->
_reportManager.getRepository().getReport(reportNotification.getReportId()).thenAccept(report ->
{
if (report != null)
{
int handlerId = reportNotification.getHandlerId();
_reportManager.getReportRepository().getAccountUUID(handlerId).thenAccept(handlerUUID ->
_reportManager.getRepository().getAccountUUID(handlerId).thenAccept(handlerUUID ->
{
if (handlerUUID != null)
{

View File

@ -7,23 +7,23 @@ import org.apache.commons.lang3.text.WordUtils;
*/
public enum ReportResultType
{
ACCEPTED(0, false),
DENIED(1, false),
ABUSIVE(2, true),
EXPIRED(3, true);
ACCEPTED((short) 0, false),
DENIED((short) 1, false),
ABUSIVE((short) 2, true),
EXPIRED((short) 3, true);
private final int _id;
private final short _id;
private final boolean _globalStat;
private final String _name;
ReportResultType(int id, boolean globalStat)
ReportResultType(short id, boolean globalStat)
{
_id = id;
_globalStat = globalStat;
_name = WordUtils.capitalizeFully(name().replace('_', ' '));
}
public int getId()
public short getId()
{
return _id;
}

View File

@ -35,7 +35,7 @@ public class ReportCloseCommand extends CommandBase<ReportPlugin>
else
{
String reason = F.combine(args, 0, null, false);
ReportManager reportManager = Plugin.getReportManager();
ReportManager reportManager = Plugin.getManager();
reportManager.getReportHandling(player).whenComplete((reportOptional, throwable) ->
{
@ -45,7 +45,7 @@ public class ReportCloseCommand extends CommandBase<ReportPlugin>
{
Report report = reportOptional.get();
reportManager.getReportRepository().getAccountName(report.getSuspectId()).thenCompose(BukkitFuture.accept(suspectName ->
reportManager.getRepository().getAccountName(report.getSuspectId()).thenCompose(BukkitFuture.accept(suspectName ->
{
ReportResultPage reportResultPage = new ReportResultPage(Plugin, report, player, suspectName, reason);
reportResultPage.openInventory(); // report is closed when player selects the result

View File

@ -1,5 +1,7 @@
package mineplex.core.report.command;
import java.util.logging.Level;
import org.bukkit.entity.Player;
import mineplex.core.account.CoreClient;
@ -35,7 +37,7 @@ public class ReportCommand extends CommandBase<ReportPlugin>
}
else
{
ReportManager reportManager = Plugin.getReportManager();
ReportManager reportManager = Plugin.getManager();
boolean canReport = reportManager.canReport(reporter);
if (canReport)
@ -51,34 +53,55 @@ public class ReportCommand extends CommandBase<ReportPlugin>
Player suspect = UtilPlayer.searchOnline(reporter, playerName, false);
String reason = F.combine(args, 1, null, false);
if (suspect != null)
reportManager.getOpenReports(reporterId).whenComplete((reports, throwable) ->
{
// allow developer (iKeirNez) to report himself (for easy testing reasons)
if (suspect == reporter && !reportManager.isDevMode(reporter.getUniqueId()))
if (throwable == null)
{
UtilPlayer.message(reporter, F.main(Plugin.getName(), C.cRed + "You cannot report yourself."));
}
else
{
CoreClient suspectClient = clientManager.Get(suspect);
new ReportCreatePage(Plugin, reporter, reporterId, suspectClient, reason).openInventory();
}
}
else
{
clientManager.loadClientByName(playerName, suspectClient ->
{
if (suspectClient != null)
if (reports.size() < ReportManager.MAXIMUM_REPORTS)
{
new ReportCreatePage(Plugin, reporter, reporterId, suspectClient, reason).openInventory();
if (suspect != null)
{
// allow developer (iKeirNez) to report himself (for easy testing reasons)
if (suspect == reporter && !reportManager.isDevMode(reporter.getUniqueId()))
{
UtilPlayer.message(reporter, F.main(Plugin.getName(),
C.cRed + "You cannot report yourself."));
}
else
{
CoreClient suspectClient = clientManager.Get(suspect);
new ReportCreatePage(Plugin, reporter, reporterId, suspectClient, reason).openInventory();
}
}
else
{
clientManager.loadClientByName(playerName, suspectClient ->
{
if (suspectClient != null)
{
new ReportCreatePage(Plugin, reporter, reporterId, suspectClient, reason).openInventory();
}
else
{
UtilPlayer.message(reporter, F.main(Plugin.getName(),
C.cRed + "Unable to find player '" + playerName + "'!"));
}
});
}
}
else
{
UtilPlayer.message(reporter, F.main(Plugin.getName(), C.cRed + "Unable to find player '"
+ playerName + "'!"));
UtilPlayer.message(reporter, F.main(Plugin.getName(),
C.cRed + "Cannot create report, you have reached the limit."));
}
});
}
}
else
{
UtilPlayer.message(reporter, F.main(Plugin.getName(),
C.cRed + "An error occurred, please try again."));
Plugin.getPlugin().getLogger().log(Level.SEVERE, "Error whilst fetching open reports.", throwable);
}
});
}
}
else

View File

@ -18,11 +18,11 @@ import mineplex.core.report.ReportRole;
/**
* A staff command for viewing report related statistics of a player.
*/
public class ReportStatsCommand extends CommandBase<ReportPlugin>
public class ReportHistoryCommand extends CommandBase<ReportPlugin>
{
public ReportStatsCommand(ReportPlugin reportPlugin)
public ReportHistoryCommand(ReportPlugin reportPlugin)
{
super(reportPlugin, Rank.MODERATOR, "reportstats", "rs");
super(reportPlugin, Rank.MODERATOR, "reporthistory", "rhis");
}
@Override
@ -32,15 +32,15 @@ public class ReportStatsCommand extends CommandBase<ReportPlugin>
{
String playerName = args[0];
Plugin.getReportManager().getReportRepository().getAccountId(playerName).thenAccept(accountIdOptional ->
Plugin.getManager().getRepository().getAccountId(playerName).thenAccept(accountIdOptional ->
{
if (accountIdOptional.isPresent())
{
int accountId = accountIdOptional.get();
Plugin.getReportManager().getReportRepository().getAccountStatistics(accountId).thenCompose(BukkitFuture.accept(stats ->
Plugin.getManager().getRepository().getAccountStatistics(accountId).thenCompose(BukkitFuture.accept(stats ->
{
UtilPlayer.message(player, F.main(Plugin.getName(), "Report Statistics for " + F.elem(playerName)));
UtilPlayer.message(player, F.main(Plugin.getName(), "Report History for " + F.elem(playerName)));
for (ReportRole role : ReportRole.values())
{

View File

@ -42,8 +42,8 @@ public class ReportInfoCommand extends CommandBase<ReportPlugin>
{
long reportId = Long.parseLong(args[0]);
ReportManager reportManager = Plugin.getReportManager();
ReportRepository repository = reportManager.getReportRepository();
ReportManager reportManager = Plugin.getManager();
ReportRepository repository = reportManager.getRepository();
repository.getReport(reportId).thenAccept(reportOptional ->
{

View File

@ -0,0 +1,123 @@
package mineplex.core.report.command;
import org.bukkit.entity.Player;
import mineplex.core.command.CommandBase;
import mineplex.core.common.Rank;
import mineplex.core.common.util.BukkitFuture;
import mineplex.core.common.util.C;
import mineplex.core.common.util.F;
import mineplex.core.common.util.UtilPlayer;
import mineplex.core.report.ReportPlugin;
import mineplex.core.report.data.metrics.ReportMetrics;
/**
* Displays various report-related metrics on a player.
*/
public class ReportMetricsCommand extends CommandBase<ReportPlugin>
{
private static final String PREFIX = "Report Metrics";
private static final int MAX_DAYS = 30;
public ReportMetricsCommand(ReportPlugin plugin)
{
super(plugin, Rank.MODERATOR, "reportmetrics");
}
@Override
public void Execute(Player player, String[] args)
{
if (args.length > 0 && args.length <= 2)
{
Integer days = parseDaysArgument(player, args[0]);
if (days != null)
{
if (args.length >= 2) // has target argument
{
String targetName = args[1];
Plugin.getManager().getRepository().getAccountId(targetName).thenAccept(targetIdOptional ->
{
if (targetIdOptional.isPresent())
{
int targetId = targetIdOptional.get();
displayUserMetrics(player, targetName, targetId, days);
}
else
{
UtilPlayer.message(player, F.main(Plugin.getName(), C.cRed + "Player not found."));
}
});
}
else // display global metrics
{
UtilPlayer.message(player, F.main("Report Metrics", F.elem("Global Metrics") + " (" + F.elem(days + " days") + ")"));
displayGlobalMetrics(player, days);
}
}
}
else
{
UtilPlayer.message(player,
F.main(Plugin.getName(), C.cRed + "Invalid Usage: "
+ F.elem("/" + _aliasUsed + " <days> [player]")));
}
}
public Integer parseDaysArgument(Player sender, String daysString)
{
Integer days;
try
{
days = Integer.parseInt(daysString);
}
catch (NumberFormatException e)
{
UtilPlayer.message(sender, F.main(PREFIX, F.elem(daysString) + C.cRed + " is not a valid integer."));
return null;
}
if (days > MAX_DAYS)
{
UtilPlayer.message(sender, F.main(PREFIX,
C.cRed + "Cannot view metrics for longer than " + F.elem(MAX_DAYS + " days") + C.cRed + "."));
return null;
}
return days;
}
public void displayGlobalMetrics(Player player, int days)
{
Plugin.getManager().getMetricsRepository().getGlobalMetrics(days).thenCompose(
BukkitFuture.accept(globalMetrics ->
{
UtilPlayer.message(player, F.main("Report Metrics", "Submitted: " + F.elem(globalMetrics.getSubmitted())));
UtilPlayer.message(player, F.main("Report Metrics", "Expired: " + F.elem(globalMetrics.getExpired())));
displayMetrics(player, globalMetrics);
}));
}
public void displayUserMetrics(Player player, String targetName, int targetId, int days)
{
Plugin.getManager().getMetricsRepository().getUserMetrics(targetId, days).thenCompose(
BukkitFuture.accept(userMetrics ->
{
UtilPlayer.message(player,
F.main("Report Metrics",
F.elem(targetName) + " (" + F.elem(days + " days") + ")"));
displayMetrics(player, userMetrics);
}));
}
public void displayMetrics(Player player, ReportMetrics reportMetrics)
{
UtilPlayer.message(player, F.main("Report Metrics", "Accepted: " + F.elem(reportMetrics.getAccepted())));
UtilPlayer.message(player, F.main("Report Metrics", "Denied: " + F.elem(reportMetrics.getDenied())));
UtilPlayer.message(player, F.main("Report Metrics", "Flagged Abusive: " + F.elem(reportMetrics.getFlagged())));
}
}

View File

@ -10,6 +10,7 @@ import mineplex.core.report.ReportCategory;
import mineplex.core.report.ReportHandlerTask;
import mineplex.core.report.ReportResult;
import mineplex.core.report.ReportTeam;
import mineplex.serverdata.Region;
/**
* Holds data for a Report.
@ -19,6 +20,7 @@ public class Report
protected Long _reportId;
private final int _suspectId;
private final ReportCategory _category;
private final Region _region;
// set of player account ids and the reason they reported this player
private final Map<Integer, ReportMessage> _reportMessages = new HashMap<>();
private Integer _handlerId = null;
@ -28,16 +30,17 @@ public class Report
private ReportHandlerTask _handlerTask = null;
public Report(int suspectId, ReportCategory category)
public Report(int suspectId, ReportCategory category, Region region)
{
this(null, suspectId, category);
this(null, suspectId, category, region);
}
protected Report(Long reportId, int suspectId, ReportCategory category)
protected Report(Long reportId, int suspectId, ReportCategory category, Region region)
{
_reportId = reportId;
_suspectId = suspectId;
_category = category;
_region = region;
}
public Optional<Long> getId()
@ -55,6 +58,11 @@ public class Report
return _category;
}
public Optional<Region> getRegion()
{
return Optional.ofNullable(_region);
}
public Map<Integer, ReportMessage> getMessages()
{
return _reportMessages;

View File

@ -38,6 +38,7 @@ import mineplex.core.report.ReportResult;
import mineplex.core.report.ReportResultType;
import mineplex.core.report.ReportRole;
import mineplex.core.report.ReportTeam;
import mineplex.serverdata.Region;
import mineplex.serverdata.database.DBPool;
import org.apache.commons.lang3.StringUtils;
@ -46,8 +47,8 @@ import org.apache.commons.lang3.StringUtils;
*/
public class ReportRepository
{
private static final String INSERT_REPORT = "INSERT INTO reports (suspectId, categoryId, snapshotId, assignedTeam)\n" +
"VALUES (?, ?, ?, ?);";
private static final String INSERT_REPORT = "INSERT INTO reports (suspectId, categoryId, snapshotId, assignedTeam, region)\n" +
"VALUES (?, ?, ?, ?, ?);";
private static final String UPDATE_REPORT = "UPDATE reports SET snapshotId = ?, assignedTeam = ? WHERE id = ?;";
@ -79,6 +80,7 @@ public class ReportRepository
" LEFT JOIN reportHandlers ON reports.id = reportHandlers.reportId\n" +
" LEFT JOIN reportReasons ON reports.id = reportReasons.reportId\n" +
"WHERE reports.categoryId = ?\n" +
" AND (reports.region IS NULL OR reports.region = ?)\n" +
" AND reportResults.reportId IS NULL\n" +
" /* Bypass for testing purposes or check player isn't suspect */\n" +
" AND (? IS TRUE OR reports.suspectId != ?)\n" +
@ -92,23 +94,32 @@ public class ReportRepository
" /* Bypass for testing purposes or check player isn't a reporter */\n" +
" AND (? IS TRUE OR SUM(IF(reportReasons.reporterId != ?, 1, 0)) = COUNT(reportReasons.reporterId));";
private static final String GET_ONGOING_REPORT = "SELECT reports.id FROM reports" +
" LEFT JOIN reportResults ON reports.id = reportResults.reportId" +
" WHERE reportResults.reportId IS NULL" +
" AND reports.suspectId = ?;";
private static final String GET_ONGOING_REPORT = "SELECT reports.id FROM reports\n" +
" LEFT JOIN reportResults ON reports.id = reportResults.reportId\n" +
"WHERE reportResults.reportId IS NULL\n" +
" AND reports.suspectId = ?\n" +
" AND (reports.region IS NULL OR reports.region = ?);";
private static final String GET_ONGOING_REPORT_CATEGORY = "SELECT reports.id FROM reports" +
" LEFT JOIN reportResults ON reports.id = reportResults.reportId" +
" WHERE reportResults.reportId IS NULL" +
" AND reports.suspectId = ?" +
" AND reports.categoryId = ?;";
private static final String GET_ONGOING_REPORT_CATEGORY = "SELECT reports.id FROM reports\n" +
" LEFT JOIN reportResults ON reports.id = reportResults.reportId\n" +
"WHERE reportResults.reportId IS NULL\n" +
" AND reports.suspectId = ?\n" +
" AND reports.categoryId = ?\n" +
" AND (reports.region IS NULL OR reports.region = ?);";
private static final String GET_REPORTS_HANDLING = "SELECT reports.id FROM reports" +
" LEFT JOIN reportResults ON reports.id = reportResults.reportId" +
" INNER JOIN reportHandlers ON reports.id = reportHandlers.reportId" +
" AND reportHandlers.aborted IS FALSE" +
" WHERE reportResults.reportId IS NULL" +
" AND reportHandlers.handlerId = ?;";
private static final String GET_REPORTS_HANDLING = "SELECT reports.id FROM reports\n" +
" LEFT JOIN reportResults ON reports.id = reportResults.reportId\n" +
" INNER JOIN reportHandlers ON reports.id = reportHandlers.reportId\n" +
"WHERE reportResults.reportId IS NULL\n" +
" AND reportHandlers.handlerId = ?\n" +
" AND reportHandlers.aborted IS FALSE\n" +
" AND (reports.region IS NULL OR reports.region = ?);";
private static final String GET_USER_OPEN_REPORTS = "SELECT reports.id FROM reports\n" +
" INNER JOIN reportReasons ON reports.id = reportReasons.reportId\n" +
" LEFT JOIN reportResults ON reports.id = reportResults.reportId\n" +
"WHERE reportResults.reportId IS NULL\n" +
" AND reportReasons.reporterId = ?;";
private static final String GET_USER_RESULT_COUNT = "SELECT COUNT(reports.id) AS resultCount" +
" FROM reports, reportReasons, reportResults" +
@ -117,16 +128,23 @@ public class ReportRepository
" AND reportReasons.reporterId = ?" +
" AND reportResults.resultId = ?;";
private static final String GET_ACCOUNT_NAME = "SELECT id, `name` FROM accounts" +
" WHERE id = ?" +
" LIMIT 1;";
// We order by lastLogin in the below queries to resolve cases whereby two account
// entries exist with the same name (online and offline mode versions), to get
// around this, we simply picked the one that logged in most recently
private static final String GET_ACCOUNT_ID = "SELECT id, `name` FROM accounts\n" +
"WHERE `name` = ?\n" +
"ORDER BY lastLogin DESC\n" +
"LIMIT 1;";
private static final String GET_ACCOUNT_NAME = "SELECT id, `name` FROM accounts\n" +
"WHERE id = ?\n" +
"ORDER BY lastLogin DESC\n" +
"LIMIT 1;";
private static final String GET_ACCOUNT_UUID = "SELECT id, uuid FROM accounts" +
" WHERE id IN (%s);";
private static final String GET_ACCOUNT_ID = "SELECT id, `name` FROM accounts\n" +
"WHERE `name` = ?;";
/** STATISTICS **/
private static final String STATISTICS_GET_REPORTS_MADE = "SELECT reports.id FROM reports, reportReasons\n" +
@ -142,6 +160,7 @@ public class ReportRepository
"WHERE reports.suspectId = ?;";
private final ReportManager _reportManager;
private final Region _region;
private final Logger _logger;
private final Cache<Long, Report> _cachedReports = CacheBuilder.newBuilder()
@ -149,20 +168,21 @@ public class ReportRepository
.expireAfterAccess(5, TimeUnit.MINUTES)
.build();
public ReportRepository(ReportManager reportManager, Logger logger)
public ReportRepository(ReportManager reportManager, Region region, Logger logger)
{
_reportManager = reportManager;
_region = region;
_logger = logger;
}
/**
* Gets the ids of unhandled reports that the supplied user is allowed to handle.
*
* @param accountId the id of the account carrying out the search
* @param handlerId the id of the account carrying out the search
* @param devMode if true, allows various restrictions to be bypassed
* @return the ids of unhandled reports the supplied account is allowed to handle
*/
public CompletableFuture<List<Long>> getUnhandledReports(int accountId, ReportCategory category, boolean devMode)
public CompletableFuture<List<Long>> getUnhandledReports(int handlerId, ReportCategory category, boolean devMode)
{
CompletableFuture<List<Long>> future = CompletableFuture.supplyAsync(() ->
{
@ -172,12 +192,13 @@ public class ReportRepository
{
PreparedStatement preparedStatement = connection.prepareStatement(GET_UNHANDLED_REPORTS);
preparedStatement.setShort(1, category.getId());
preparedStatement.setBoolean(2, devMode);
preparedStatement.setInt(3, accountId);
preparedStatement.setInt(4, accountId);
preparedStatement.setInt(5, accountId);
preparedStatement.setBoolean(6, devMode);
preparedStatement.setInt(7, accountId);
preparedStatement.setString(2, _region.name());
preparedStatement.setBoolean(3, devMode);
preparedStatement.setInt(4, handlerId);
preparedStatement.setInt(5, handlerId);
preparedStatement.setInt(6, handlerId);
preparedStatement.setBoolean(7, devMode);
preparedStatement.setInt(8, handlerId);
ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next())
@ -204,10 +225,10 @@ public class ReportRepository
/**
* Gets a list containing the ids of reports the account is handling
* @param accountId the id of the account
* @param handlerId the id of the account
* @return a list containing the ids of reports being handled
*/
public CompletableFuture<List<Long>> getReportsHandling(int accountId)
public CompletableFuture<List<Long>> getReportsHandling(int handlerId)
{
CompletableFuture<List<Long>> future = CompletableFuture.supplyAsync(() ->
{
@ -216,7 +237,8 @@ public class ReportRepository
try (Connection connection = DBPool.getAccount().getConnection())
{
PreparedStatement preparedStatement = connection.prepareStatement(GET_REPORTS_HANDLING);
preparedStatement.setInt(1, accountId);
preparedStatement.setInt(1, handlerId);
preparedStatement.setString(2, _region.name());
ResultSet resultSet = preparedStatement.executeQuery();
@ -255,14 +277,22 @@ public class ReportRepository
for (long reportId : reportIds)
{
preparedStatement.setLong(1, reportId);
ResultSet resultSet = preparedStatement.executeQuery();
Report report = loadReport(connection, resultSet);
Report report = _cachedReports.getIfPresent(reportId);
if (report != null)
if (report == null)
{
reports.add(report);
preparedStatement.setLong(1, reportId);
ResultSet resultSet = preparedStatement.executeQuery();
report = loadReport(connection, resultSet);
if (report == null)
{
continue;
}
}
reports.add(report);
}
return reports;
@ -333,13 +363,15 @@ public class ReportRepository
long reportId = resultSet.getLong("id");
int suspectId = resultSet.getInt("suspectId");
ReportCategory reportCategory = ReportCategory.getById(resultSet.getInt("categoryId"));
String regionName = resultSet.getString("region");
Region region = !resultSet.wasNull() ? Region.valueOf(regionName) : null;
Report report = new Report(reportId, suspectId, reportCategory);
Report report = new Report(reportId, suspectId, reportCategory, region);
int snapshotId = resultSet.getInt("snapshotId");
if (!resultSet.wasNull())
{
SnapshotMetadata snapshotMetadata = _reportManager.getSnapshotManager().getSnapshotRepository()
SnapshotMetadata snapshotMetadata = _reportManager.getSnapshotManager().getRepository()
.getSnapshotMetadata(snapshotId).join();
report.setSnapshotMetadata(snapshotMetadata);
@ -392,7 +424,7 @@ public class ReportRepository
return null;
}
public CompletableFuture<List<Report>> getOngoingReports(int accountId)
public CompletableFuture<List<Report>> getOngoingReports(int suspectId)
{
CompletableFuture<List<Report>> future = CompletableFuture.supplyAsync(() ->
{
@ -400,7 +432,8 @@ public class ReportRepository
{
List<Report> reports = new ArrayList<>();
PreparedStatement preparedStatement = connection.prepareStatement(GET_ONGOING_REPORT);
preparedStatement.setInt(1, accountId);
preparedStatement.setInt(1, suspectId);
preparedStatement.setString(2, _region.name());
ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next())
@ -436,22 +469,23 @@ public class ReportRepository
future.exceptionally(throwable ->
{
_logger.log(Level.SEVERE, "Error getting ongoing report for account: " + accountId + ".", throwable);
_logger.log(Level.SEVERE, "Error getting ongoing report for account: " + suspectId + ".", throwable);
return null;
});
return future;
}
public CompletableFuture<List<Long>> getOngoingReports(int accountId, ReportCategory category)
public CompletableFuture<List<Long>> getOngoingReports(int suspectId, ReportCategory category)
{
CompletableFuture<List<Long>> future = CompletableFuture.supplyAsync(() ->
{
try (Connection connection = DBPool.getAccount().getConnection())
{
PreparedStatement preparedStatement = connection.prepareStatement(GET_ONGOING_REPORT_CATEGORY);
preparedStatement.setInt(1, accountId);
preparedStatement.setInt(1, suspectId);
preparedStatement.setInt(2, category.getId());
preparedStatement.setString(3, _region.name());
ResultSet resultSet = preparedStatement.executeQuery();
List<Long> reports = new ArrayList<>();
@ -470,13 +504,39 @@ public class ReportRepository
future.exceptionally(throwable ->
{
_logger.log(Level.SEVERE, "Error fetching ongoing report for account: " + accountId + ", category: " + category + ".", throwable);
_logger.log(Level.SEVERE, "Error fetching ongoing report for account: " + suspectId + ", category: " + category + ".", throwable);
return new ArrayList<>();
});
return future;
}
public CompletableFuture<List<Long>> getOpenReports(int reporterId)
{
return CompletableFuture.supplyAsync(() ->
{
try (Connection connection = DBPool.getAccount().getConnection())
{
PreparedStatement preparedStatement = connection.prepareStatement(GET_USER_OPEN_REPORTS);
preparedStatement.setInt(1, reporterId);
ResultSet resultSet = preparedStatement.executeQuery();
List<Long> reports = new ArrayList<>();
while (resultSet.next())
{
reports.add(resultSet.getLong("id"));
}
return reports;
}
catch (SQLException e)
{
throw new RuntimeException(e);
}
});
}
private Set<ReportMessage> getReportReasons(Connection connection, long reportId)
{
Set<ReportMessage> reportMessages = new HashSet<>();
@ -507,13 +567,13 @@ public class ReportRepository
return reportMessages;
}
public CompletableFuture<Integer> getResultCount(int accountId, ReportResultType resultType)
public CompletableFuture<Integer> getResultCount(int reporterId, ReportResultType resultType)
{
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
try (Connection connection = DBPool.getAccount().getConnection())
{
PreparedStatement preparedStatement = connection.prepareStatement(GET_USER_RESULT_COUNT);
preparedStatement.setInt(1, accountId);
preparedStatement.setInt(1, reporterId);
preparedStatement.setInt(2, resultType.getId());
ResultSet resultSet = preparedStatement.executeQuery();
@ -532,7 +592,7 @@ public class ReportRepository
future.exceptionally(throwable ->
{
_logger.log(Level.SEVERE, "Error fetching result count for account: " + accountId + ", type: " + resultType + ".", throwable);
_logger.log(Level.SEVERE, "Error fetching result count for account: " + reporterId + ", type: " + resultType + ".", throwable);
return 0;
});
@ -546,6 +606,7 @@ public class ReportRepository
try (Connection connection = DBPool.getAccount().getConnection())
{
Optional<Long> reportIdOptional = report.getId();
Optional<Region> regionOptional = report.getRegion();
Optional<Integer> snapshotIdOptional = report.getSnapshotMetadata().map(SnapshotMetadata::getId);
Optional<ReportTeam> teamOptional = report.getAssignedTeam();
long reportId;
@ -603,6 +664,15 @@ public class ReportRepository
insertReportStatement.setNull(4, Types.TINYINT);
}
if (regionOptional.isPresent())
{
insertReportStatement.setString(5, regionOptional.get().name());
}
else
{
insertReportStatement.setNull(5, Types.VARCHAR);
}
insertReportStatement.executeUpdate();
ResultSet resultSet = insertReportStatement.getGeneratedKeys();
@ -618,6 +688,8 @@ public class ReportRepository
}
}
_cachedReports.put(reportId, report); // cache the report
PreparedStatement setReportMessageStatement = connection.prepareStatement(SET_REPORT_MESSAGE);
for (Map.Entry<Integer, ReportMessage> entry : report.getMessages().entrySet())

View File

@ -0,0 +1,37 @@
package mineplex.core.report.data.metrics;
/**
* Extends the standard report metrics class to hold global-scope metrics.
*/
public class ReportGlobalMetrics extends ReportMetrics
{
private final long _submitted;
private final long _expired;
public ReportGlobalMetrics(long submitted, long expired, long accepted, long denied, long flagged)
{
super(accepted, denied, flagged);
_submitted = submitted;
_expired = expired;
}
/**
* Gets the amount of reports submitted.
*
* @return the amount
*/
public long getSubmitted()
{
return _submitted;
}
/**
* Gets the amount of reports expired.
*
* @return the amount
*/
public long getExpired()
{
return _expired;
}
}

View File

@ -0,0 +1,48 @@
package mineplex.core.report.data.metrics;
/**
* Holds report-related metrics which can be applied to a user or global scope.
*/
public class ReportMetrics
{
private final long _accepted;
private final long _denied;
private final long _flagged;
public ReportMetrics(long accepted, long denied, long flagged)
{
_accepted = accepted;
_denied = denied;
_flagged = flagged;
}
/**
* Gets the amount of reports accepted.
*
* @return the amount
*/
public long getAccepted()
{
return _accepted;
}
/**
* Gets the amount of reports denied.
*
* @return the amount
*/
public long getDenied()
{
return _denied;
}
/**
* Gets the amount of reports flagged (marked as spam).
*
* @return the amount
*/
public long getFlagged()
{
return _flagged;
}
}

View File

@ -0,0 +1,156 @@
package mineplex.core.report.data.metrics;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Level;
import java.util.logging.Logger;
import mineplex.core.report.ReportResultType;
import mineplex.serverdata.database.DBPool;
/**
* Handles all fetching of report-related metrics.
*/
public class ReportMetricsRepository
{
private static final String GET_GLOBAL_SUBMITTED = "SELECT COUNT(DISTINCT reportReasons.reportId) AS submitted FROM reportReasons\n" +
" WHERE reportReasons.time BETWEEN NOW() - INTERVAL ? DAY AND NOW();";
private static final String GET_GLOBAL_RESULT = "SELECT COUNT(reportResults.reportId) AS amount FROM reportResults\n" +
" WHERE reportResults.resultId = ?\n" +
" AND reportResults.closedTime BETWEEN NOW() - INTERVAL ? DAY AND NOW();";
private static final String GET_USER_RESULT = "SELECT COUNT(reportResults.reportId) AS amount FROM reportResults\n" +
" LEFT JOIN reportHandlers ON reportResults.reportId = reportHandlers.reportId AND reportHandlers.aborted IS FALSE\n" +
" WHERE reportHandlers.handlerId = ?\n" +
" AND reportResults.resultId = ?\n" +
" AND reportResults.closedTime BETWEEN NOW() - INTERVAL ? DAY AND NOW();";
private final Logger _logger;
public ReportMetricsRepository(Logger logger)
{
_logger = logger;
}
public CompletableFuture<ReportGlobalMetrics> getGlobalMetrics(int days)
{
CompletableFuture<ReportGlobalMetrics> future = CompletableFuture.supplyAsync(() ->
{
try (Connection connection = DBPool.getAccount().getConnection())
{
long submitted = getGlobalSubmitted(connection, days);
try (PreparedStatement preparedStatement = connection.prepareStatement(GET_GLOBAL_RESULT))
{
long expired = getGlobalResult(preparedStatement, ReportResultType.EXPIRED, days);
long accepted = getGlobalResult(preparedStatement, ReportResultType.ACCEPTED, days);
long denied = getGlobalResult(preparedStatement, ReportResultType.DENIED, days);
long flagged = getGlobalResult(preparedStatement, ReportResultType.ABUSIVE, days);
return new ReportGlobalMetrics(submitted, expired, accepted, denied, flagged);
}
}
catch (SQLException e)
{
throw new RuntimeException(e);
}
});
future.exceptionally(throwable ->
{
_logger.log(Level.SEVERE, "Error fetching global metrics.", throwable);
return null;
});
return future;
}
private long getGlobalSubmitted(Connection connection, int days) throws SQLException
{
PreparedStatement preparedStatement = connection.prepareStatement(GET_GLOBAL_SUBMITTED);
preparedStatement.setInt(1, days);
try (ResultSet resultSet = preparedStatement.executeQuery())
{
if (resultSet.next())
{
return resultSet.getLong("submitted");
}
else
{
return 0;
}
}
}
private long getGlobalResult(PreparedStatement preparedStatement, ReportResultType resultType, int days) throws SQLException
{
preparedStatement.setShort(1, resultType.getId());
preparedStatement.setInt(2, days);
try (ResultSet resultSet = preparedStatement.executeQuery())
{
if (resultSet.next())
{
return resultSet.getLong("amount");
}
else
{
return 0;
}
}
}
public CompletableFuture<ReportMetrics> getUserMetrics(int accountId, int days)
{
CompletableFuture<ReportMetrics> future = CompletableFuture.supplyAsync(() ->
{
try (Connection connection = DBPool.getAccount().getConnection())
{
try (PreparedStatement preparedStatement = connection.prepareStatement(GET_USER_RESULT))
{
long accepted = getUserResult(preparedStatement, accountId, ReportResultType.ACCEPTED, days);
long denied = getUserResult(preparedStatement, accountId, ReportResultType.DENIED, days);
long flagged = getUserResult(preparedStatement, accountId, ReportResultType.ABUSIVE, days);
return new ReportMetrics(accepted, denied, flagged);
}
}
catch (SQLException e)
{
throw new RuntimeException(e);
}
});
future.exceptionally(throwable ->
{
_logger.log(Level.SEVERE, "Error fetching user metrics.", throwable);
return null;
});
return future;
}
private long getUserResult(PreparedStatement preparedStatement, int accountId, ReportResultType resultType, int days) throws SQLException
{
preparedStatement.setInt(1, accountId);
preparedStatement.setShort(2, resultType.getId());
preparedStatement.setInt(3, days);
try (ResultSet resultSet = preparedStatement.executeQuery())
{
if (resultSet.next())
{
return resultSet.getLong("amount");
}
else
{
return 0;
}
}
}
}

View File

@ -69,7 +69,7 @@ public class ReportCreatePage extends SimpleGui implements ReportCategoryCallba
private boolean hasSentMessage(int accountId)
{
SnapshotManager snapshotManager = _plugin.getReportManager().getSnapshotManager();
SnapshotManager snapshotManager = _plugin.getManager().getSnapshotManager();
int suspectId = _suspect.getAccountId();
List<SnapshotMessage> suspectMessages = snapshotManager.getMessagesFrom(accountId).stream()
.filter(message -> message.getSenderId() == suspectId || message.getRecipientIds().contains(suspectId))
@ -80,7 +80,7 @@ public class ReportCreatePage extends SimpleGui implements ReportCategoryCallba
private void createReport(ReportCategory category)
{
_plugin.getReportManager().createReport(_reporterId, _suspect.getAccountId(), category, _reason)
_plugin.getManager().createReport(_reporterId, _suspect.getAccountId(), category, _reason)
.thenAccept(report -> {
boolean error = true;

View File

@ -56,8 +56,8 @@ public class ReportHandlePage extends SimpleGui implements ReportCategoryCallbac
public void handleReport(ReportCategory category)
{
ReportManager reportManager = _plugin.getReportManager();
ReportRepository reportRepository = reportManager.getReportRepository();
ReportManager reportManager = _plugin.getManager();
ReportRepository reportRepository = reportManager.getRepository();
reportManager.isHandlingReport(_handler).whenComplete((handlingReport, throwable) ->
{

View File

@ -31,7 +31,7 @@ public class ReportResultPage extends SimpleGui
{
super(plugin.getPlugin(), reportCloser, "Close Report", 9 * 4);
_plugin = plugin;
_reportManager = plugin.getReportManager();
_reportManager = plugin.getManager();
_report = report;
_suspectName = suspectName;
_reason = reason;

View File

@ -239,7 +239,7 @@ public class Clans extends JavaPlugin
SnapshotManager snapshotManager = new SnapshotManager(this, new SnapshotRepository(serverStatusManager.getCurrentServerName(), getLogger()));
new SnapshotPlugin(this, snapshotManager, _clientManager);
new ReportPlugin(this, new ReportManager(this, snapshotManager, _clientManager, incognito, punish, serverStatusManager.getCurrentServerName(), 1));
new ReportPlugin(this, new ReportManager(this, snapshotManager, _clientManager, incognito, punish, serverStatusManager.getRegion(), serverStatusManager.getCurrentServerName(), 1));
// Enable custom-gear related managers
new CustomTagFix(this, packetHandler);

View File

@ -187,7 +187,7 @@ public class Hub extends JavaPlugin implements IRelation
//new Replay(this, packetHandler);
SnapshotManager snapshotManager = new SnapshotManager(this, new SnapshotRepository(serverStatusManager.getCurrentServerName(), getLogger()));
ReportManager reportManager = new ReportManager(this, snapshotManager, clientManager, incognito, punish, serverStatusManager.getCurrentServerName(), 3);
ReportManager reportManager = new ReportManager(this, snapshotManager, clientManager, incognito, punish, serverStatusManager.getRegion(), serverStatusManager.getCurrentServerName(), 3);
new SnapshotPlugin(this, snapshotManager, clientManager);
new ReportPlugin(this, reportManager);

View File

@ -0,0 +1,3 @@
ALTER TABLE Account.reports ADD region VARCHAR(5) NULL;
ALTER TABLE Account.reports
MODIFY COLUMN region VARCHAR(5) AFTER categoryId;

View File

@ -0,0 +1,71 @@
-- RENAME COLUMN `creator` to `creatorId`
ALTER TABLE Account.snapshots CHANGE creator creatorId INT(11);
-- STORE DATETIME SNAPSHOT CREATED (CLEANUP PURPOSES)
ALTER TABLE Account.snapshots ADD created DATETIME DEFAULT NOW() NOT NULL;
ALTER TABLE Account.snapshots
MODIFY COLUMN creatorId INT(11) AFTER created;
-- CASCADE HANDLERS TABLE
ALTER TABLE Account.reportHandlers DROP FOREIGN KEY reportHandlers_reports_id_fk;
ALTER TABLE Account.reportHandlers
ADD CONSTRAINT reportHandlers_reports_id_fk
FOREIGN KEY (reportId) REFERENCES reports (id) ON DELETE CASCADE;
-- CASCADE REASONS/REPORTERS TABLE
ALTER TABLE Account.reportReasons DROP FOREIGN KEY reportReasons_reports_id_fk;
ALTER TABLE Account.reportReasons
ADD CONSTRAINT reportReasons_reports_id_fk
FOREIGN KEY (reportId) REFERENCES reports (id) ON DELETE CASCADE;
-- CASCADE RESULTS TABLE
ALTER TABLE Account.reportResults DROP FOREIGN KEY reportResults_reports_id_fk;
ALTER TABLE Account.reportResults
ADD CONSTRAINT reportResults_reports_id_fk
FOREIGN KEY (reportId) REFERENCES reports (id) ON DELETE CASCADE;
-- CASCADE SNAPSHOT MESSAGE MAP
ALTER TABLE Account.snapshotMessageMap DROP FOREIGN KEY snapshotMessageMap_snapshots_id_fk;
ALTER TABLE Account.snapshotMessageMap
ADD CONSTRAINT snapshotMessageMap_snapshots_id_fk
FOREIGN KEY (snapshotId) REFERENCES snapshots (id) ON DELETE CASCADE;
ALTER TABLE Account.snapshotMessageMap DROP FOREIGN KEY snapshotMessageMap_snapshotMessages_id_fk;
ALTER TABLE Account.snapshotMessageMap
ADD CONSTRAINT snapshotMessageMap_snapshotMessages_id_fk
FOREIGN KEY (messageId) REFERENCES snapshotMessages (id) ON DELETE CASCADE;
-- CASCADE SNAPSHOT RECIPIENTS TABLE
ALTER TABLE Account.snapshotRecipients DROP FOREIGN KEY snapshotRecipients_snapshotMessages_id_fk;
ALTER TABLE Account.snapshotRecipients
ADD CONSTRAINT snapshotRecipients_snapshotMessages_id_fk
FOREIGN KEY (messageId) REFERENCES snapshotMessages (id) ON DELETE CASCADE;
-- CREATE CLEANUP TASK
DELIMITER //
-- CREATE EVENT TO RUN EVERY DAY AT 00:00
CREATE EVENT `report_cleanup`
ON SCHEDULE
EVERY 1 DAY
-- FORCE TASK TO RUN AT 00:00 DAILY
STARTS (TIMESTAMP(CURRENT_DATE) + INTERVAL 1 DAY)
ON COMPLETION PRESERVE
COMMENT 'Cleans up old report and snapshot data.'
DO BEGIN
-- DELETE REPORTS (AND ASSOCIATED SNAPSHOT IF ANY) CLOSED > 30 DAYS AGO
DELETE reports, snapshots FROM reports
LEFT JOIN reportResults ON reports.id = reportResults.reportId
LEFT JOIN snapshots ON reports.snapshotId = snapshots.id
WHERE reportResults.closedTime NOT BETWEEN NOW() - INTERVAL 30 DAY AND NOW();
-- DELETE SNAPSHOTS NOT LINKED TO REPORT AND OLDER THAN 30 DAYS
DELETE snapshots FROM snapshots
LEFT JOIN reports ON snapshots.id = reports.snapshotId
WHERE reports.id IS NULL
AND snapshots.created NOT BETWEEN NOW() - INTERVAL 30 DAY AND NOW();
-- DELETE ORPHANED SNAPSHOT MESSAGES
DELETE snapshotMessages FROM snapshotMessages
LEFT JOIN snapshotMessageMap ON snapshotMessages.id = snapshotMessageMap.messageId
WHERE snapshotMessageMap.snapshotId IS NULL;
END//

View File

@ -1,5 +1,4 @@
<?php
require_once('snapshot.php');
require_once('report.php');
require_once('message.php');
@ -126,7 +125,7 @@
{
$connection = getConnection('ACCOUNT');
$statement = $connection->prepare('SELECT id FROM snapshots WHERE token = ?;');
$statement->bind_param('s', $token); // TODO: correct data type
$statement->bind_param('s', $token);
$statement->execute();
$statement->bind_result($snapshotId);
$statement->store_result();
@ -286,13 +285,11 @@ WHERE snapshotMessageMap.snapshotId = snapshots.id
}
/**
* @param Snapshot $snapshot
* @param Report $report
* @return User[]
*/
function getInvolvedUsers($snapshot, $report)
function getInvolvedUsers($report)
{
$involvedUsers = $snapshot->getPlayers();
$involvedUsers[$report->getSuspect()->getId()] = $report->getSuspect();
foreach ($report->getReporters() as $reporterReason) {
@ -384,6 +381,7 @@ WHERE snapshotMessageMap.snapshotId = snapshots.id
$validToken = isset($_GET['token']);
$errorMsg = "";
$title = 'Report & Snapshot System';
$token = null;
$expanded = null;
$report = null;
@ -401,15 +399,16 @@ WHERE snapshotMessageMap.snapshotId = snapshots.id
$snapshot = getSnapshot($snapshotId);
$messages = $snapshot->getMessages();
$reportId = getSnapshotReportId($snapshotId);
$report = null;
if ($reportId)
{
$report = getReport($reportId);
$title = "Report #$reportId";
}
else
{
$validToken = false;
$errorMsg = 'Associated report not found.'; // TODO: Allow snapshots without reports in future
$title = "Snapshot #$snapshotId";
}
}
else
@ -428,13 +427,7 @@ WHERE snapshotMessageMap.snapshotId = snapshots.id
<link href='https://fonts.googleapis.com/css?family=Crete+Round' rel='stylesheet' type='text/css'>
<link href='https://fonts.googleapis.com/css?family=Oswald' rel='stylesheet' type='text/css'>
<title>
<?php if ($validToken): ?>
Report #<?= $report->getId() ?>
<?php else: ?>
Report System
<?php endif; ?>
&middot; Mineplex
<?= $title ?> &middot; Mineplex
</title>
</head>
<body>
@ -471,7 +464,7 @@ WHERE snapshotMessageMap.snapshotId = snapshots.id
<div>
<hr>
<h2 style="font-family: 'Oswald', sans-serif; text-align: center;">
Report #<?= $report->getId() ?>
<?= $title ?>
</h2>
<hr>
</div>
@ -481,21 +474,23 @@ WHERE snapshotMessageMap.snapshotId = snapshots.id
<hr>
<div id="log">
<?php
// INITIALIZE
$messageCount = count($messages);
$displayAmount = $expanded || $messageCount <= collapsedMessageCount ? $messageCount : collapsedMessageCount;
$involvedUsers = null;
// Put all reporter usernames in array for easy access later
$reporterUsernames = array();
foreach ($report->getReporters() as $reporterReason)
if ($report != null)
{
$reporterUsernames[count($reporterUsernames)] = $reporterReason->getUser()->getUsername();
$involvedUsers = getInvolvedUsers($report);
}
else
{
$involvedUsers = array();
}
$involvedUsers = getInvolvedUsers($snapshot, $report);
$reportCreationTime = $report->getTimeCreated();
$age = approximateHumanInterval($reportCreationTime->diff(new DateTime('now', $reportCreationTime->getTimezone())));
foreach ($snapshot->getPlayers() as $player)
{
$involvedUsers[$player->getId()] = $player;
}
if($displayAmount == 0): ?>
<span class="black">No chat log available for this report.</span>
@ -517,7 +512,7 @@ WHERE snapshotMessageMap.snapshotId = snapshots.id
<?php endif; ?>
<span class="remove-whitespace">
<span class="<?= ($message->getSender() == $report->getSuspect() ? 'suspect' : 'black') ?>"><?= $message->getSender()->getUsername() ?></span>
<span class="<?= ($report != null && $message->getSender() == $report->getSuspect() ? 'suspect' : 'black') ?>"><?= $message->getSender()->getUsername() ?></span>
<?php if ($isPM): ?>
&nbsp;->&nbsp;<?= $message->getRecipients()[0]->getUsername() ?>
@ -540,39 +535,51 @@ WHERE snapshotMessageMap.snapshotId = snapshots.id
<?php endif; ?>
</div>
<div id="users" class="col-lg-5">
<h4><i class="fa fa-info-circle"></i>&nbsp;&nbsp;&nbsp;Information</h4>
<hr>
<div class="row">
<div class="col-lg-12">
<i class="fa fa-clock-o fa-fw"></i>
<span class="label label-pill label-default" title="Last Report: <?= $reportCreationTime->format('Y/m/d H:i:s T') ?>"><?= $age . ' ago' ?></span>
<br>
<?php if ($report != null): ?>
<h4><i class="fa fa-info-circle"></i>&nbsp;&nbsp;&nbsp;Information</h4>
<hr>
<div class="row">
<div class="col-lg-12">
<?php
// Put all reporter usernames in array for easy access later
$reporterUsernames = array();
foreach ($report->getReporters() as $reporterReason)
{
$reporterUsernames[count($reporterUsernames)] = $reporterReason->getUser()->getUsername();
}
<i class="fa fa-sitemap fa-fw"></i>
<span class="label label-pill label-primary"><?= $categories[$report->getCategory()] ?></span>
<br>
$reportCreationTime = $report->getTimeCreated();
$age = approximateHumanInterval($reportCreationTime->diff(new DateTime('now', $reportCreationTime->getTimezone())));
?>
<i class="fa fa-clock-o fa-fw"></i>
<span class="label label-pill label-default" title="Last Report: <?= $reportCreationTime->format('Y/m/d H:i:s T') ?>"><?= $age . ' ago' ?></span>
<br>
<i class="fa fa-user-plus fa-fw"></i>
<span class="label label-pill label-success">Reported by <?= implode(", ", $reporterUsernames) ?></span>
<br>
<i class="fa fa-sitemap fa-fw"></i>
<span class="label label-pill label-primary"><?= $categories[$report->getCategory()] ?></span>
<br>
<i class="fa fa-user-times fa-fw"></i>
<span class="label label-pill label-danger">Suspect is <?= $report->getSuspect()->getUsername() ?></span>
<br>
<i class="fa fa-user-plus fa-fw"></i>
<span class="label label-pill label-success">Reported by <?= implode(", ", $reporterUsernames) ?></span>
<br>
<i class="fa fa-gavel fa-fw"></i>
<span class="label label-pill label-warning">
<?php if ($report->getHandler() != null): ?>
Staff Member assigned is <?= $report->getHandler()->getUsername() ?>
<?php else: ?>
No Staff Member assigned
<?php endif; ?>
</span>
<br>
<i class="fa fa-user-times fa-fw"></i>
<span class="label label-pill label-danger">Suspect is <?= $report->getSuspect()->getUsername() ?></span>
<br>
<i class="fa fa-gavel fa-fw"></i>
<span class="label label-pill label-warning">
<?php if ($report->getHandler() != null): ?>
Staff Member assigned is <?= $report->getHandler()->getUsername() ?>
<?php else: ?>
No Staff Member assigned
<?php endif; ?>
</span>
<br>
</div>
</div>
</div>
<br>
<br>
<?php endif; ?>
<h4><i class="fa fa-users"></i>&nbsp;&nbsp;&nbsp;Users</h4>
<hr>
<?php foreach($involvedUsers as $user): ?>

View File

@ -161,7 +161,7 @@ public class Arcade extends JavaPlugin
new MessageManager(this, incognito, _clientManager, preferenceManager, ignoreManager, punish, friendManager, chat);
SnapshotManager snapshotManager = new SnapshotManager(this, new SnapshotRepository(serverStatusManager.getCurrentServerName(), getLogger()));
ReportManager reportManager = new ReportManager(this, snapshotManager, _clientManager, incognito, punish, serverStatusManager.getCurrentServerName(), 1);
ReportManager reportManager = new ReportManager(this, snapshotManager, _clientManager, incognito, punish, serverStatusManager.getRegion(), serverStatusManager.getCurrentServerName(), 1);
new SnapshotPlugin(this, snapshotManager, _clientManager);
new ReportPlugin(this, reportManager);