Merge pull request #216 from Fuzzlemann/master

PR for 3.6.1 (Fuzzlemann) (1)
This commit is contained in:
Rsl1122 2017-08-03 09:30:46 +03:00 committed by GitHub
commit 6ddd7a212c
27 changed files with 525 additions and 303 deletions

View File

@ -1,13 +1,13 @@
<component name="libraryTable">
<library name="Maven: com.djrapitops:PlanPluginBridge:3.5.0">
<library name="Maven: com.djrapitops:PlanPluginBridge:3.6.0">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/com/djrapitops/PlanPluginBridge/3.5.0/PlanPluginBridge-3.5.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/djrapitops/PlanPluginBridge/3.6.0/PlanPluginBridge-3.6.0.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/com/djrapitops/PlanPluginBridge/3.5.0/PlanPluginBridge-3.5.0-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/djrapitops/PlanPluginBridge/3.6.0/PlanPluginBridge-3.6.0-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/com/djrapitops/PlanPluginBridge/3.5.0/PlanPluginBridge-3.5.0-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/djrapitops/PlanPluginBridge/3.6.0/PlanPluginBridge-3.6.0-sources.jar!/" />
</SOURCES>
</library>
</component>

View File

@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.djrapitops</groupId>
<artifactId>Plan</artifactId>
<version>3.5.5</version>
<version>3.6.0</version>
<build>
<sourceDirectory>${basedir}/src</sourceDirectory>
<defaultGoal>clean package install</defaultGoal>
@ -12,6 +12,8 @@
<targetPath>.</targetPath>
<directory>${basedir}/src/main/resources</directory>
<includes>
<include>*.keystore</include>
<include>*.js</include>
<include>*.yml</include>
<include>*.html</include>
</includes>
@ -178,6 +180,7 @@
</dependency>
</dependencies>
<properties>
<sonar.language>java</sonar.language>
<maven.compiler.source>1.8</maven.compiler.source>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.target>1.8</maven.compiler.target>

View File

@ -25,6 +25,7 @@ public class PluginConfigSectionHandler {
if (!section.contains(pluginName)) {
return false;
}
ConfigurationSection pluginSection = section.getConfigurationSection(pluginName);
return pluginSection.contains(dataSource.getPlaceholder(""));
}
@ -38,8 +39,10 @@ public class PluginConfigSectionHandler {
ConfigurationSection section = getPluginsSection();
String pluginName = dataSource.getSourcePlugin();
String source = dataSource.placeholder;
section.addDefault(pluginName + ".Enabled", true);
section.addDefault(pluginName + ".Data." + source, true);
FileConfiguration config = plan.getConfig();
config.set("Customization.Plugins", section);
plan.saveConfig();
@ -47,9 +50,11 @@ public class PluginConfigSectionHandler {
public boolean isEnabled(PluginData dataSource) {
ConfigurationSection section = getPluginsSection();
String pluginName = dataSource.getSourcePlugin();
if (!section.getBoolean(pluginName + ".Enabled")) {
return false;
}
String source = dataSource.placeholder;
return section.getBoolean(pluginName + ".Data." + source);

View File

@ -16,7 +16,7 @@ import java.util.Map;
* <p>
* It caches all IPs with their matching country.
* <p>
* This cache uses the Google Guava {@link Cache} and has a capacity of 10.000 entries.
* This cache uses the Google Guava {@link Cache}.
*
* @author Fuzzlemann
* @since 3.5.5
@ -31,7 +31,6 @@ public class GeolocationCacheHandler {
}
private static final Cache<String, String> geolocationCache = CacheBuilder.newBuilder()
.maximumSize(10000)
.build();
/**

View File

@ -44,17 +44,14 @@ public class InspectCacheHandler {
* @param uuid UUID of the player.
*/
public void cache(UUID uuid) {
DBCallableProcessor cacher = new DBCallableProcessor() {
@Override
public void process(UserData data) {
cache.put(uuid, new UserData(data));
cacheTimes.put(uuid, MiscUtils.getTime());
PageCacheHandler.cachePage("inspectPage: " + uuid.toString(), () -> new InspectPageResponse(Plan.getInstance().getUiServer().getDataReqHandler(), uuid));
try {
ExportUtility.writeInspectHtml(data, ExportUtility.getPlayersFolder(ExportUtility.getFolder()));
} catch (IOException ex) {
Log.toLog(this.getClass().getName(), ex);
}
DBCallableProcessor cacher = data -> {
cache.put(uuid, new UserData(data));
cacheTimes.put(uuid, MiscUtils.getTime());
PageCacheHandler.cachePage("inspectPage: " + uuid.toString(), () -> new InspectPageResponse(Plan.getInstance().getUiServer().getDataReqHandler(), uuid));
try {
ExportUtility.writeInspectHtml(data, ExportUtility.getPlayersFolder(ExportUtility.getFolder()));
} catch (IOException ex) {
Log.toLog(this.getClass().getName(), ex);
}
};

View File

@ -2,6 +2,7 @@ package main.java.com.djrapitops.plan.data.handling.importing;
import com.djrapitops.plugin.utilities.player.Fetch;
import com.djrapitops.plugin.utilities.player.IOfflinePlayer;
import com.djrapitops.plugin.utilities.status.ProcessStatus;
import main.java.com.djrapitops.plan.Log;
import main.java.com.djrapitops.plan.Plan;
import main.java.com.djrapitops.plan.data.UserData;
@ -14,8 +15,10 @@ import main.java.com.djrapitops.plan.utilities.NewPlayerCreator;
import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
/**
* Abstract class used for importing data from other plugins.
@ -58,11 +61,16 @@ public abstract class Importer {
public boolean importData(Collection<UUID> uuids, String... args) {
Plan plan = Plan.getInstance();
plan.getAnalysisCache().disableAnalysisTemporarily();
try {
String processName = "Import, " + getClass().getSimpleName();
plan.processStatus().startExecution(processName);
ProcessStatus<Plan> processStatus = plan.processStatus();
DataCacheHandler handler = plan.getHandler();
Database db = plan.getDB();
processStatus.startExecution(processName);
Set<UUID> saved;
try {
saved = db.getSavedUUIDs();
@ -70,30 +78,58 @@ public abstract class Importer {
Log.toLog(this.getClass().getName(), ex);
return false;
}
List<UUID> unSaved = new ArrayList<>(uuids);
unSaved.removeAll(saved);
String createUserObjects = "Creating new UserData objects for: " + unSaved.size();
plan.processStatus().setStatus(processName, createUserObjects);
int amount = unSaved.size();
String createUserObjects = "Creating " + amount + " new UserData objects";
processStatus.setStatus(processName, createUserObjects);
Map<UUID, IOfflinePlayer> offlinePlayers = Fetch.getIOfflinePlayers().stream().collect(Collectors.toMap(IOfflinePlayer::getUuid, Function.identity()));
Benchmark.start(createUserObjects);
List<IOfflinePlayer> offlineP = unSaved.stream().map(offlinePlayers::get).collect(Collectors.toList());
List<UserData> newUsers = new ArrayList<>();
for (IOfflinePlayer p : offlineP) {
UserData newPlayer = NewPlayerCreator.createNewOfflinePlayer(p);
newPlayer.setLastPlayed(newPlayer.getRegistered());
newUsers.add(newPlayer);
plan.processStatus().setStatus(processName, "Creating new UserData objects: " + newUsers.size() + "/" + unSaved.size());
}
List<IOfflinePlayer> offlineP = unSaved
.stream()
.map(offlinePlayers::get)
.collect(Collectors.toList());
AtomicInteger currentUser = new AtomicInteger(0);
AtomicInteger currentPercent = new AtomicInteger(0);
int fivePercent = amount / 20;
//Using Set because of better Collection#contains() performance
Set<Integer> milestones = IntStream.rangeClosed(1, 20)
.mapToObj(i -> i * fivePercent)
.collect(Collectors.toSet());
offlineP.parallelStream()
.map(NewPlayerCreator::createNewOfflinePlayer)
.forEach(newPlayer -> {
newPlayer.setLastPlayed(newPlayer.getRegistered());
newUsers.add(newPlayer);
if (milestones.contains(currentUser.incrementAndGet())) {
processStatus.setStatus(processName, "Creating new UserData objects: " + currentPercent.addAndGet(5) + "%");
}
});
Benchmark.stop(createUserObjects);
plan.processStatus().setStatus(processName, "Save new UserData objects (" + unSaved.size() + ")");
processStatus.setStatus(processName, "Save new UserData objects (" + amount + ")");
try {
plan.getDB().saveMultipleUserData(newUsers);
} catch (SQLException ex) {
Log.toLog(this.getClass().getName(), ex);
}
for (UUID uuid : uuids) {
handler.addToPool(importData(uuid, args));
}
plan.processStatus().finishExecution(processName);
} finally {
plan.getAnalysisCache().enableAnalysis();

View File

@ -60,10 +60,10 @@ public class PlanDeathEventListener implements Listener {
Material itemInHand;
try {
itemInHand = killer.getInventory().getItemInMainHand().getType();
} catch (Exception e) {
} catch (NoSuchMethodError e) {
try {
itemInHand = killer.getInventory().getItemInHand().getType(); // Support for non dual wielding versions.
} catch (Exception e2) {
} catch (Error e2) {
itemInHand = Material.AIR;
}
}

View File

@ -117,26 +117,6 @@ public abstract class TimeKeeper {
return times;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TimeKeeper that = (TimeKeeper) o;
if (lastStateChange != that.lastStateChange) return false;
if (times != null ? !times.equals(that.times) : that.times != null) return false;
return state != null ? state.equals(that.state) : that.state == null;
}
@Override
public int hashCode() {
int result = times != null ? times.hashCode() : 0;
result = 31 * result + (state != null ? state.hashCode() : 0);
result = 31 * result + (int) (lastStateChange ^ (lastStateChange >>> 32));
return result;
}
public void setState(String state) {
this.state = state;
}
@ -153,10 +133,32 @@ public abstract class TimeKeeper {
return lastStateChange;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TimeKeeper that = (TimeKeeper) o;
return lastStateChange == that.lastStateChange &&
times != null ? times.equals(that.times) : that.times == null
&& state != null ? state.equals(that.state) : that.state == null;
}
@Override
public int hashCode() {
int result = times != null ? times.hashCode() : 0;
result = 31 * result + (state != null ? state.hashCode() : 0);
result = 31 * result + (int) (lastStateChange ^ (lastStateChange >>> 32));
return result;
}
@Override
public String toString() {
return "times:" + times +
",state:" + state +
",lastStateChange:" + lastStateChange;
return "TimeKeeper{" +
"times=" + times +
", state='" + state + '\'' +
", lastStateChange=" + lastStateChange +
'}';
}
}

View File

@ -72,6 +72,7 @@ public class DBUtils {
if (wrappedBatches.size() <= j) {
wrappedBatches.add(new ArrayList<>());
}
wrappedBatches.get(j).add(new Container<>(object, entry.getKey()));
i++;
if (i % BATCH_SIZE == 0) {
@ -81,4 +82,25 @@ public class DBUtils {
}
return wrappedBatches;
}
public static <T> List<List<Container<T>>> splitIntoBatchesWithID(Map<Integer, T> objects) {
List<List<Container<T>>> wrappedBatches = new ArrayList<>();
int i = 0;
int j = 0;
for (Entry<Integer, T> entry : objects.entrySet()) {
T object = entry.getValue();
if (wrappedBatches.size() <= j) {
wrappedBatches.add(new ArrayList<>());
}
wrappedBatches.get(j).add(new Container<>(object, entry.getKey()));
i++;
if (i % BATCH_SIZE == 0) {
j++;
}
}
return wrappedBatches;
}
}

View File

@ -35,7 +35,9 @@ public class MySQLDB extends SQLDB {
try {
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://" + config.getString("mysql.host") + ":" + config.getString("mysql.port") + "/" + config.getString("mysql.database");
String url = "jdbc:mysql://" + config.getString("mysql.host") + ":" + config.getString("mysql.port") + "/"
+ config.getString("mysql.database")
+ "?rewriteBatchedStatements=true";
return DriverManager.getConnection(url, config.getString("mysql.user"), config.getString("mysql.password"));
} catch (ClassNotFoundException | SQLException e) {

View File

@ -424,19 +424,24 @@ public abstract class SQLDB extends Database {
*/
@Override
public void saveMultipleUserData(Collection<UserData> data) throws SQLException {
Benchmark.start("Database: Save multiple Userdata");
checkConnection();
if (data.isEmpty()) {
if (data == null || data.isEmpty()) {
return;
}
Benchmark.start("Database: Save multiple Userdata");
data.removeIf(Objects::isNull);
checkConnection();
setStatus("Save userdata (multiple) for " + data.size());
usersTable.saveUserDataInformationBatch(data);
// Transform to map
Map<UUID, UserData> userDatas = data.stream().collect(Collectors.toMap(UserData::getUuid, Function.identity()));
Map<UUID, UserData> userDatas = data.stream()
.collect(Collectors.toMap(UserData::getUuid, Function.identity()));
// Get UserIDs
Map<UUID, Integer> userIds = usersTable.getAllUserIds();
// Empty dataset
// Create empty data sets
Map<Integer, Set<String>> nicknames = new HashMap<>();
Map<Integer, String> lastNicks = new HashMap<>();
Map<Integer, Set<InetAddress>> ips = new HashMap<>();
@ -445,14 +450,16 @@ public abstract class SQLDB extends Database {
Map<Integer, List<SessionData>> sessions = new HashMap<>();
Map<Integer, Map<String, Long>> gmTimes = new HashMap<>();
Map<Integer, Map<String, Long>> worldTimes = new HashMap<>();
// Put to dataset
// Put in data set
List<String> worldNames = data.stream()
.map(UserData::getWorldTimes)
.map(WorldTimes::getTimes)
.map(Map::keySet)
.flatMap(keySet -> keySet.stream())
.flatMap(Collection::stream)
.distinct()
.collect(Collectors.toList());
for (Map.Entry<UUID, UserData> entrySet : userDatas.entrySet()) {
UUID uuid = entrySet.getKey();
UserData uData = entrySet.getValue();
@ -472,6 +479,7 @@ public abstract class SQLDB extends Database {
gmTimes.put(id, new HashMap<>(uData.getGmTimes().getTimes()));
worldTimes.put(id, new HashMap<>(uData.getWorldTimes().getTimes()));
}
// Save
nicknamesTable.saveNickLists(nicknames, lastNicks);
ipsTable.saveIPList(ips);

View File

@ -83,6 +83,7 @@ public class CommandUseTable extends Table {
if (data.isEmpty()) {
return;
}
Benchmark.start("Database: Save Commanduse");
Map<String, Integer> newData = new HashMap<>(data);
Map<String, Integer> saved = getCommandUse();

View File

@ -2,6 +2,9 @@ package main.java.com.djrapitops.plan.database.tables;
import com.djrapitops.plugin.utilities.Verify;
import main.java.com.djrapitops.plan.Log;
import main.java.com.djrapitops.plan.data.time.GMTimes;
import main.java.com.djrapitops.plan.database.Container;
import main.java.com.djrapitops.plan.database.DBUtils;
import main.java.com.djrapitops.plan.database.databases.SQLDB;
import main.java.com.djrapitops.plan.utilities.Benchmark;
@ -93,12 +96,14 @@ public class GMTimesTable extends Table {
statement.setInt(1, userId);
set = statement.executeQuery();
HashMap<String, Long> times = new HashMap<>();
while (set.next()) {
times.put("SURVIVAL", set.getLong(columnSurvivalTime));
times.put("CREATIVE", set.getLong(columnCreativeTime));
times.put("ADVENTURE", set.getLong(columnAdventureTime));
times.put("SPECTATOR", set.getLong(columnSpectatorTime));
}
return times;
} finally {
close(set);
@ -113,18 +118,21 @@ public class GMTimesTable extends Table {
try {
statement = prepareStatement("SELECT * FROM " + tableName);
set = statement.executeQuery();
while (set.next()) {
Map<String, Long> gmTimes = new HashMap<>();
int id = set.getInt(columnUserID);
if (!userIds.contains(id)) {
continue;
}
gmTimes.put("SURVIVAL", set.getLong(columnSurvivalTime));
gmTimes.put("CREATIVE", set.getLong(columnCreativeTime));
gmTimes.put("ADVENTURE", set.getLong(columnAdventureTime));
gmTimes.put("SPECTATOR", set.getLong(columnSpectatorTime));
times.put(id, gmTimes);
}
return times;
} finally {
close(set);
@ -141,8 +149,10 @@ public class GMTimesTable extends Table {
if (Verify.isEmpty(gamemodeTimes)) {
return;
}
PreparedStatement statement = null;
String[] gms = getGMKeyArray();
int update;
try {
statement = prepareStatement(
@ -153,22 +163,17 @@ public class GMTimesTable extends Table {
+ columnSpectatorTime + "=? "
+ " WHERE (" + columnUserID + "=?)");
statement.setInt(5, userId);
for (int i = 0; i < gms.length; i++) {
try {
Long time = gamemodeTimes.get(gms[i]);
if (time != null) {
statement.setLong(i + 1, time);
} else {
statement.setLong(i + 1, 0);
}
} catch (NoSuchFieldError e) {
statement.setLong(i + 1, 0);
}
Long time = gamemodeTimes.get(gms[i]);
statement.setLong(i + 1, time != null ? time : 0);
}
update = statement.executeUpdate();
} finally {
close(statement);
}
if (update == 0) {
addNewGMTimesRow(userId, gamemodeTimes);
}
@ -195,11 +200,52 @@ public class GMTimesTable extends Table {
if (Verify.isEmpty(gamemodeTimes)) {
return;
}
Benchmark.start("Database: Save GMTimes");
PreparedStatement statement = null;
String[] gms = getGMKeyArray();
Set<Integer> savedIDs = getSavedIDs();
Map<Integer, GMTimes> gmTimes = new HashMap<>();
for (Map.Entry<Integer, Map<String, Long>> entrySet : gamemodeTimes.entrySet()) {
int userID = entrySet.getKey();
if (!savedIDs.contains(userID)) {
continue;
}
Map<String, Long> gmTimesMap = entrySet.getValue();
gmTimes.put(userID, new GMTimes(gmTimesMap));
}
List<List<Container<GMTimes>>> batches = DBUtils.splitIntoBatchesWithID(gmTimes);
batches.parallelStream().forEach(batch -> {
try {
saveGMTimesBatch(batch);
} catch (SQLException e) {
Log.toLog("GMTimesTable.saveGMTimes", e);
}
});
gamemodeTimes.keySet().removeAll(savedIDs);
addNewGMTimesRows(gamemodeTimes);
Benchmark.stop("Database: Save GMTimes");
}
private void saveGMTimesBatch(List<Container<GMTimes>> batch) throws SQLException {
if (batch.isEmpty()) {
return;
}
int batchSize = batch.size();
Log.debug("Preparing insertion of GM Times... Batch Size: " + batchSize);
String[] gms = getGMKeyArray();
Set<Integer> savedIDs = getSavedIDs();
PreparedStatement statement = null;
try {
statement = prepareStatement(
"UPDATE " + tableName + " SET "
@ -208,48 +254,72 @@ public class GMTimesTable extends Table {
+ columnAdventureTime + "=?, "
+ columnSpectatorTime + "=? "
+ " WHERE (" + columnUserID + "=?)");
boolean commitRequired = false;
for (Map.Entry<Integer, Map<String, Long>> entrySet : gamemodeTimes.entrySet()) {
Integer id = entrySet.getKey();
for (Container<GMTimes> data : batch) {
int id = data.getId();
if (!savedIDs.contains(id)) {
continue;
}
statement.setInt(5, id);
for (int i = 0; i < gms.length; i++) {
try {
Map<String, Long> times = entrySet.getValue();
Long time = times.get(gms[i]);
Map<String, Long> times = data.getObject().getTimes();
Long time = times.get(gms[i]);
statement.setLong(i + 1, time != null ? time : 0);
} catch (NoSuchFieldError e) {
statement.setLong(i + 1, 0);
}
statement.setLong(i + 1, time != null ? time : 0);
}
statement.addBatch();
commitRequired = true;
}
if (commitRequired) {
statement.executeBatch();
}
gamemodeTimes.keySet().removeAll(savedIDs);
Log.debug("Executing GM Times batch: " + batchSize);
statement.executeBatch();
} finally {
close(statement);
}
addNewGMTimesRows(gamemodeTimes);
Benchmark.stop("Database: Save GMTimes");
}
private void addNewGMTimesRows(Map<Integer, Map<String, Long>> gamemodeTimes) throws SQLException {
if (Verify.isEmpty(gamemodeTimes)) {
return;
}
PreparedStatement statement = null;
Benchmark.start("Database: Add GMTimes Rows");
Map<Integer, GMTimes> gmTimes = new HashMap<>();
for (Map.Entry<Integer, Map<String, Long>> entrySet : gamemodeTimes.entrySet()) {
int userID = entrySet.getKey();
Map<String, Long> gmTimesMap = entrySet.getValue();
gmTimes.put(userID, new GMTimes(gmTimesMap));
}
List<List<Container<GMTimes>>> batches = DBUtils.splitIntoBatchesWithID(gmTimes);
batches.parallelStream().forEach(batch -> {
try {
addNewGMTimesBatch(batch);
} catch (SQLException e) {
e.printStackTrace();
}
});
Benchmark.stop("Database: Add GMTimes Rows");
}
private void addNewGMTimesBatch(List<Container<GMTimes>> batch) throws SQLException {
if (batch.isEmpty()) {
return;
}
int batchSize = batch.size();
Log.debug("Preparing insertion of GM Times... Batch Size: " + batchSize);
String[] gms = getGMKeyArray();
PreparedStatement statement = null;
try {
statement = prepareStatement(
"INSERT INTO " + tableName + " ("
@ -259,28 +329,22 @@ public class GMTimesTable extends Table {
+ columnAdventureTime + ", "
+ columnSpectatorTime
+ ") VALUES (?, ?, ?, ?, ?)");
boolean commitRequired = false;
for (Map.Entry<Integer, Map<String, Long>> entry : gamemodeTimes.entrySet()) {
Integer id = entry.getKey();
statement.setInt(1, id);
for (Container<GMTimes> data : batch) {
statement.setInt(1, data.getId());
for (int i = 0; i < gms.length; i++) {
try {
Map<String, Long> times = entry.getValue();
Long time = times.get(gms[i]);
Map<String, Long> times = data.getObject().getTimes();
Long time = times.get(gms[i]);
statement.setLong(i + 2, time != null ? time : 0);
} catch (NoSuchFieldError e) {
statement.setLong(i + 2, 0);
}
statement.setLong(i + 2, time != null ? time : 0);
}
statement.addBatch();
commitRequired = true;
}
if (commitRequired) {
statement.executeBatch();
}
Log.debug("Executing GM Times batch: " + batchSize);
statement.executeBatch();
} finally {
close(statement);
}
@ -290,6 +354,7 @@ public class GMTimesTable extends Table {
if (Verify.isEmpty(gamemodeTimes)) {
return;
}
PreparedStatement statement = null;
String[] gms = getGMKeyArray();
try {
@ -302,14 +367,10 @@ public class GMTimesTable extends Table {
+ ") VALUES (?, ?, ?, ?, ?)");
statement.setInt(1, userId);
for (int i = 0; i < gms.length; i++) {
try {
Long time = gamemodeTimes.get(gms[i]);
statement.setLong(i + 2, time != null ? time : 0);
} catch (NoSuchFieldError e) {
statement.setLong(i + 2, 0);
}
for (int i = 0; i < gms.length; i++) {
Long time = gamemodeTimes.get(gms[i]);
statement.setLong(i + 2, time != null ? time : 0);
}
statement.execute();

View File

@ -90,6 +90,7 @@ public class IPsTable extends Table {
Log.error("Host not found at getIPAddresses: " + ipAddressName); //Shouldn't ever happen
}
}
return ips;
} finally {
close(set);

View File

@ -107,36 +107,38 @@ public class SessionsTable extends Table {
if (sessions == null) {
return;
}
Benchmark.start("Database: Save Sessions");
sessions.removeAll(getSessionData(userId));
if (sessions.isEmpty()) {
Benchmark.stop("Database: Save Sessions");
return;
}
PreparedStatement statement = null;
try {
PreparedStatement statement = null;
try {
statement = prepareStatement("INSERT INTO " + tableName + " ("
+ columnUserID + ", "
+ columnSessionStart + ", "
+ columnSessionEnd
+ ") VALUES (?, ?, ?)");
boolean commitRequired = false;
for (SessionData session : sessions) {
long end = session.getSessionEnd();
long start = session.getSessionStart();
if (end < start) {
continue;
}
statement.setInt(1, userId);
statement.setLong(2, start);
statement.setLong(3, end);
statement.addBatch();
commitRequired = true;
}
if (commitRequired) {
statement.executeBatch();
}
statement.executeBatch();
} finally {
close(statement);
Benchmark.stop("Database: Save Sessions");
@ -152,25 +154,31 @@ public class SessionsTable extends Table {
if (ids == null || ids.isEmpty()) {
return new HashMap<>();
}
Benchmark.start("Database: Get Sessions multiple");
PreparedStatement statement = null;
ResultSet set = null;
try {
Map<Integer, List<SessionData>> sessions = new HashMap<>();
statement = prepareStatement("SELECT * FROM " + tableName);
set = statement.executeQuery();
for (Integer id : ids) {
sessions.put(id, new ArrayList<>());
}
while (set.next()) {
Integer id = set.getInt(columnUserID);
if (!ids.contains(id)) {
continue;
}
sessions.get(id).add(new SessionData(set.getLong(columnSessionStart), set.getLong(columnSessionEnd)));
long sessionStart = set.getLong(columnSessionStart);
long sessionEnd = set.getLong(columnSessionEnd);
sessions.get(id).add(new SessionData(sessionStart, sessionEnd));
}
set.close();
statement.close();
return sessions;
} finally {
@ -195,8 +203,8 @@ public class SessionsTable extends Table {
for (Map.Entry<Integer, List<SessionData>> entrySet : sessions.entrySet()) {
Integer id = entrySet.getKey();
List<SessionData> sessionList = entrySet.getValue();
List<SessionData> s = saved.get(id);
if (s != null) {
sessionList.removeAll(s);
}
@ -207,11 +215,17 @@ public class SessionsTable extends Table {
saved.put(id, sessionList);
}
List<List<Container<SessionData>>> batches = splitIntoBatches(sessions);
for (List<Container<SessionData>> batch : batches) {
saveSessionBatch(batch);
}
batches.parallelStream().forEach(batch -> {
try {
saveSessionBatch(batch);
} catch (SQLException e) {
e.printStackTrace();
}
});
Benchmark.stop("Database: Save Sessions multiple");
}
@ -219,6 +233,10 @@ public class SessionsTable extends Table {
if (batch.isEmpty()) {
return;
}
int batchSize = batch.size();
Log.debug("Preparing insertion of sessions... Batch Size: " + batchSize);
PreparedStatement statement = null;
try {
statement = prepareStatement("INSERT INTO " + tableName + " ("
@ -227,25 +245,21 @@ public class SessionsTable extends Table {
+ columnSessionEnd
+ ") VALUES (?, ?, ?)");
boolean commitRequired = false;
int i = 0;
for (Container<SessionData> data : batch) {
SessionData session = data.getObject();
int id = data.getId();
if (!session.isValid()) {
continue;
}
statement.setInt(1, id);
statement.setLong(2, session.getSessionStart());
statement.setLong(3, session.getSessionEnd());
statement.addBatch();
commitRequired = true;
i++;
}
if (commitRequired) {
Log.debug("Executing session batch: " + i);
statement.executeBatch();
}
Log.debug("Executing session batch: " + batchSize);
statement.executeBatch();
} finally {
close(statement);
}

View File

@ -119,12 +119,24 @@ public class TPSTable extends Table {
*/
public void saveTPSData(List<TPS> data) throws SQLException {
List<List<TPS>> batches = DBUtils.splitIntoBatches(data);
for (List<TPS> batch : batches) {
saveTPSBatch(batch);
}
batches.parallelStream()
.forEach(batch -> {
try {
saveTPSBatch(batch);
} catch (SQLException e) {
Log.toLog("UsersTable.saveUserDataInformationBatch", e);
}
});
}
private void saveTPSBatch(List<TPS> batch) throws SQLException {
if (batch.isEmpty()) {
return;
}
int batchSize = batch.size();
Log.debug("Preparing insertion of TPS... Batch Size: " + batchSize);
PreparedStatement statement = null;
try {
statement = prepareStatement("INSERT INTO " + tableName + " ("
@ -137,8 +149,6 @@ public class TPSTable extends Table {
+ columnChunksLoaded
+ ") VALUES (?, ?, ?, ?, ?, ?, ?)");
boolean commitRequired = false;
int i = 0;
for (TPS tps : batch) {
statement.setLong(1, tps.getDate());
statement.setDouble(2, tps.getTps());
@ -148,13 +158,10 @@ public class TPSTable extends Table {
statement.setDouble(6, tps.getEntityCount());
statement.setDouble(7, tps.getChunksLoaded());
statement.addBatch();
commitRequired = true;
i++;
}
if (commitRequired) {
Log.debug("Executing tps batch: " + i);
statement.executeBatch();
}
Log.debug("Executing tps batch: " + batchSize);
statement.executeBatch();
} finally {
close(statement);
}

View File

@ -680,10 +680,18 @@ public class UsersTable extends Table {
try {
List<UserData> newUserdata = updateExistingUserData(data);
Benchmark.start("Database: Insert new UserInfo multiple");
List<List<UserData>> batches = DBUtils.splitIntoBatches(newUserdata);
for (List<UserData> batch : batches) {
insertNewUserData(batch);
}
batches.parallelStream()
.forEach(batch -> {
try {
insertNewUserData(batch);
} catch (SQLException e) {
Log.toLog("UsersTable.saveUserDataInformationBatch", e);
}
});
Benchmark.stop("Database: Insert new UserInfo multiple");
} finally {
Benchmark.stop("Database: Save UserInfo multiple");
@ -691,18 +699,27 @@ public class UsersTable extends Table {
}
private void insertNewUserData(Collection<UserData> data) throws SQLException {
if (data.isEmpty()) {
return;
}
int batchSize = data.size();
Log.debug("Preparing insertion of new users... Batch Size: " + batchSize);
PreparedStatement statement = null;
try {
statement = prepareStatement(getInsertStatement());
boolean commitRequired = false;
int i = 0;
for (UserData uData : data) {
UUID uuid = uData.getUuid();
statement.setString(1, uuid.toString());
statement.setString(2, uData.getGeolocation());
GMTimes gmTimes = uData.getGmTimes();
statement.setString(3, gmTimes.getState());
statement.setLong(4, gmTimes.getLastStateChange());
statement.setLong(5, uData.getPlayTime());
statement.setInt(6, uData.getLoginTimes());
statement.setLong(7, uData.getLastPlayed());
@ -713,17 +730,16 @@ public class UsersTable extends Table {
statement.setBoolean(12, uData.isBanned());
statement.setString(13, uData.getName());
statement.setLong(14, uData.getRegistered());
WorldTimes worldTimes = uData.getWorldTimes();
statement.setString(15, worldTimes.getState());
statement.setLong(16, worldTimes.getLastStateChange());
statement.addBatch();
commitRequired = true;
i++;
}
if (commitRequired) {
Log.debug("Executing session batch: " + i);
statement.executeBatch();
}
Log.debug("Executing users batch: " + batchSize);
statement.executeBatch();
} finally {
close(statement);
}

View File

@ -15,13 +15,16 @@ public class WorldPieCreator {
for (Map.Entry<String, Long> world : worldTimes.entrySet()) {
arrayBuilder.append("{name:'").append(world.getKey())
.append("',y:").append(world.getValue());
if (i == 1) {
arrayBuilder.append(", sliced: true, selected: true");
}
arrayBuilder.append("}");
if (i < size - 1) {
arrayBuilder.append(",");
}
i++;
}
arrayBuilder.append("]");
return arrayBuilder.toString();

View File

@ -6,7 +6,9 @@ import main.java.com.djrapitops.plan.data.KillData;
import main.java.com.djrapitops.plan.ui.html.Html;
import main.java.com.djrapitops.plan.utilities.FormatUtils;
import main.java.com.djrapitops.plan.utilities.HtmlUtils;
import main.java.com.djrapitops.plan.utilities.comparators.KillDataComparator;
import java.util.Collections;
import java.util.List;
/**
@ -32,6 +34,10 @@ public class KillsTableCreator {
html.append(Html.TABLELINE_3.parse(Html.KILLDATA_NONE.parse(), "", ""));
} else {
int i = 0;
killData.sort(new KillDataComparator());
Collections.reverse(killData);
for (KillData kill : killData) {
if (i >= 20) {
break;

View File

@ -162,18 +162,19 @@ public class AnalysisUtils {
* @return
*/
public static String getBooleanPercentage(AnalysisType analysisType, PluginData source, List<UUID> uuids) {
if (analysisType == AnalysisType.BOOLEAN_PERCENTAGE) {
try {
List<Boolean> tempList = getCorrectValues(uuids, source)
.map(value -> (boolean) value)
.collect(Collectors.toList());
long count = tempList.stream().filter(value -> value).count();
return source.parseContainer(analysisType.getModifier(), (((double) count / tempList.size()) * 100) + "%");
} catch (Exception | NoClassDefFoundError | NoSuchFieldError e) {
return logPluginDataCausedError(source, e);
}
if (analysisType != AnalysisType.BOOLEAN_PERCENTAGE) {
return source.parseContainer("Err ", "Wrong Analysistype specified: " + analysisType.name());
}
try {
List<Boolean> tempList = getCorrectValues(uuids, source)
.map(value -> (boolean) value)
.collect(Collectors.toList());
long count = tempList.stream().filter(value -> value).count();
return source.parseContainer(analysisType.getModifier(), (((double) count / tempList.size()) * 100) + "%");
} catch (Exception | NoClassDefFoundError | NoSuchFieldError e) {
return logPluginDataCausedError(source, e);
}
return source.parseContainer("Err ", "Wrong Analysistype specified: " + analysisType.name());
}
/**
@ -183,18 +184,19 @@ public class AnalysisUtils {
* @return
*/
public static String getBooleanTotal(AnalysisType analysisType, PluginData source, List<UUID> uuids) {
if (analysisType == AnalysisType.BOOLEAN_TOTAL) {
try {
List<Boolean> tempList = getCorrectValues(uuids, source)
.map(value -> (boolean) value)
.collect(Collectors.toList());
long count = tempList.stream().filter(value -> value).count();
return source.parseContainer(analysisType.getModifier(), count + " / " + tempList.size());
} catch (Exception e) {
return logPluginDataCausedError(source, e);
}
if (analysisType != AnalysisType.BOOLEAN_TOTAL) {
return source.parseContainer("Err ", "Wrong Analysistype specified: " + analysisType.name());
}
try {
List<Boolean> tempList = getCorrectValues(uuids, source)
.map(value -> (boolean) value)
.collect(Collectors.toList());
long count = tempList.stream().filter(value -> value).count();
return source.parseContainer(analysisType.getModifier(), count + " / " + tempList.size());
} catch (Exception e) {
return logPluginDataCausedError(source, e);
}
return source.parseContainer("Err ", "Wrong Analysistype specified: " + analysisType.name());
}
private static String logPluginDataCausedError(PluginData source, Throwable e) {
@ -214,6 +216,7 @@ public class AnalysisUtils {
public static int getUniqueJoins(Map<UUID, List<SessionData>> sessions, long scale) {
long now = MiscUtils.getTime();
long nowMinusScale = now - scale;
Set<UUID> uniqueJoins = new HashSet<>();
sessions.forEach((uuid, s) ->
s.stream()
@ -221,6 +224,7 @@ public class AnalysisUtils {
.map(session -> uuid)
.forEach(uniqueJoins::add)
);
return uniqueJoins.size();
}

View File

@ -0,0 +1,17 @@
package main.java.com.djrapitops.plan.utilities.comparators;
import main.java.com.djrapitops.plan.data.KillData;
import java.util.Comparator;
/**
* @author Fuzzlemann
*/
public class KillDataComparator implements Comparator<KillData> {
@Override
public int compare(KillData o1, KillData o2) {
return Long.compare(o1.getDate(), o2.getDate());
}
}

View File

@ -1,107 +1 @@
Player Analytics (Plan) Licence v.1.0.1
Copyright (C) Risto Lahtela / Rsl1122
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
0. Definitions
With 'software' this licence refers to the following:
Player Analytics (Plan) Bukkit plugin
Player Analytics Lite (PlanLite) Bukkit plugin
(https://github.com/Rsl1122/Plan-PlayerAnalytics)
Player Demographics (Plade) Bukkit plugin
(https://github.com/Rsl1122/Plan-Player-Demographics)
& the source code of these that is publicly available.
'visual aspect of software'
The messages sent with Bukkit API to Player/Console
Html pages & the included styles
Images & Videos found on the plugin page
Additional 'resources'
Provided .yml .html .txt and image files inside the software.
'code modifyer'
A person modifying the code/resources of the software.
'user'
A person running the software
1. Use & Distribution
This licence grants the end user the rights to run the software & modify it to a certain extent. (detailed in section 3. Modification)
All versions downloaded from Spigot (https://www.spigotmc.org/resources/authors/rsl1122.122894/) & .jar packages compiled
from the source code can be used as a dependency in another project, commercial or not.
You may not sell the software or modified versions of this software.
Modified software can only be distributed if ALL of the following conditions apply:
a. the original author and the original work are clearly mentioned & linked in the distribution method or when using the software
b. Modified software does not contain harmful code to the user.
c. Modified software is free & the modified source code is publicly available.
d. Modified software is published under this licence
e. The end user is provided a copy of this licence
When distributing a modified copy of the software you grant the right for the original author to post a takedown notice, upon which
the modified source code and modified software must be removed from distribution.
2. Commercial use
When using parts of the software with 'Minecraft Server software', the use must comply with EULA provided by Mojang (https://help.mojang.com/customer/en/portal/articles/1590522-minecraft-commercial-use)
If a project requires end user to obtain a copy of the software, a link must be provided to obtain the software from the author / Spigot.
3. Modification
The code & resources can be modified & run if the following conditions apply:
a. The modified version is used only by the modifyer (Person modifying the resources/software) & not distributed/sold.
The modifyer may transfer his right to use the software to another person in following cases:
a1: Modifyer was paid to modify part of the software for customization of visual appearance of the software or fix Exceptions & Errors caused
by updates to Minecraft Server software.
4. Usage of part of code in another software
This part (4) of the licence does not include the usage of the API of the plugin,
or adding the compiled code as provided dependency. Such use is allowed.
Usage of a part of code in another software is allowed if following conditions apply:
a. The software falls under this licence
b. This licence is accepted by the person using the code inside their software
c. The use is not a clear copyright infringement
OR
a. It can be proven that it is possible to come to the same solution independently in a resonable time frame of one hour
with no prior access to the software.
5. Revised Versions of this Licence
The author may publish revised and/or new versions of
this License from time to time. Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
6. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
7. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
Moved to https://github.com/Rsl1122/Plan-PlayerAnalytics/edit/master/Plan/src/main/resources/license.yml

View File

@ -0,0 +1,124 @@
Player Analytics (Plan) License v.2.0.0
Copyright (C) Risto Lahtela / Rsl1122
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
0. Definitions
With 'Software' this license refers to the following:
Player Analytics (Plan) Bukkit plugin
(https://github.com/Rsl1122/Plan-PlayerAnalytics),
Modified versions the software
& the source code that is publicly available on Plans GitHub repository.
This does not include the Markdown (.md) files available in the repository.
Java Runtime Environment
A Runtime Environment which allows Java Bytecode to run, mostly Oracle JRE.
But this also includes other Runtime Environments like OpenJDK / JRE (http://openjdk.java.net)
Java Virtual Machine
The virtual machine included in the Java Runtime Environment.
GitHub
(https://www.github.com)
Minecraft
A game by Mojang AB (https://www.mojang.com).
(https://www.minecraft.net)
Minecraft Server software
The software to operate a server for Minecraft, mostly Bukkit or its forks.
Original Minecraft Server independent software like Glowstone (https://www.glowstone.net) are falling under this definition as well.
Plans Spigot plugin page
(https://www.spigotmc.org/resources/plan-player-analytics.32536)
Minecraft EULA
(https://help.mojang.com/customer/en/portal/articles/1590522-minecraft-commercial-use)
Plans GitHub repository
(https://www.github.com/Rsl1122/Plan-PlayerAnalytics)
Fork
Refers to a copy made of Plans GitHub repository which is using the Fork System of GitHub.
'End User'
A person running the software on a Java Virtual Machine environment.
Original Source Code
Refers to the source code that is available on Plans GitHub repository.
Original Author
Rsl1122 / Risto Lahtela
1. Use & Distribution
This license grants the end user the rights to run the software & make modifications to its HTML components, Java Keystore, locale files
& configuration files in the scope that the plugin allows without decompiling the .class files inside the jar.
Distribution and modification of source code is allowed under the circumstances that such code is publicly available, the original
author is mentioned & the original author is made aware of the modified code. A fork counts as a notification about modified code.
A removal of the Fork display is not allowed under any circumstances except if the original author grants a written permission for
this exact action.
All versions downloaded from the Plans Spigot plugin page & .jar packages compiled
from the original source code can be used as a dependency and only as a dependency in another project, commercial or not.
You may not distribute or sell modified, or unmodified versions of compiled source code without a direct written permission of
the original author.
Such permissions are granted separately for Distribution & Selling.
When distributing a modified, or unmodified copy of the software you grant the right for the original author to post a takedown
notice, upon which the modified software must be removed from distribution.
If the conditions of the license are breached, the software breaking the license must be removed from distribution.
2. Commercial use
When using parts of the software with Minecraft Server software, the use must comply with EULA provided by Mojang.
If a project requires the end user to obtain a copy of the software, a link must be provided to obtain the software from the author
or Spigot.
3. Modification
Modification on the code is allowed, as long as the section 1 of this license is not violated.
4. Usage of part of the code in another program
This part of the license is about copying code directly from the source code or decompiled .class files.
Usage of a part of code in another non-commercial program is allowed if following conditions apply:
a. The program falls under this license
b. This license is accepted by the person using the code inside their program
c. The use does not break the conditions of this license
d. The use is not a clear copyright infringement
OR
a. It can be proven that it is possible to come to the same solution independently in a reasonable time frame of one hour with
no prior access to the software.
OR
a. The original author grants written permission for the use.
5. Revised versions of this License
The author may publish revised and/or new versions of this License from time to time. Such new versions will be similar in spirit
to the old version, but may differ in detail to address new problems or concerns.
6. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE SOFTWARE "AS IS"
WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH YOU.
SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
7. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE SOFTWARE AS PERMITTED ABOVE, BE LIABLE TO YOU FOR
DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE SOFTWARE
(INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE
OF THE SOFTWARE TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.

View File

@ -235,13 +235,10 @@ public class DatabaseTest {
db.saveUserData(data);
data.addNickname("TestUpdateForSave");
db.saveUserData(data);
DBCallableProcessor process = new DBCallableProcessor() {
@Override
public void process(UserData d) {
System.out.println("\nOriginal: " + data);
System.out.println("Database: " + d);
assertTrue("Not Equals", data.equals(d));
}
DBCallableProcessor process = d -> {
System.out.println("\nOriginal: " + data);
System.out.println("Database: " + d);
assertTrue("Not Equals", data.equals(d));
};
db.giveUserDataToProcessors(data.getUuid(), process);
}

View File

@ -9,6 +9,7 @@ import main.java.com.djrapitops.plan.data.handling.info.InfoType;
import main.java.com.djrapitops.plan.database.tables.GMTimesTable;
import main.java.com.djrapitops.plan.utilities.PassEncryptUtil;
import main.java.com.djrapitops.plan.utilities.analysis.Point;
import org.apache.commons.lang.RandomStringUtils;
import java.util.ArrayList;
import java.util.List;
@ -34,8 +35,7 @@ public class RandomData {
* Random enough.
*/
public static String randomString(int size) {
String randomString = UUID.randomUUID().toString().replaceAll("-", "").substring(0, size);
return randomString;
return RandomStringUtils.random(size);
}
public static List<WebUser> randomWebUsers() throws PassEncryptUtil.CannotPerformOperationException {
@ -75,7 +75,7 @@ public class RandomData {
public static List<HandlingInfo> randomHandlingInfo() {
List<HandlingInfo> test = new ArrayList<>();
for (int i = 0; i < 20; i++) {
test.add(new HandlingInfo(UUID.randomUUID(), InfoType.OTHER, r.nextLong()) {
test.add(new HandlingInfo(UUID.randomUUID(), randomEnum(InfoType.class), r.nextLong()) {
@Override
public boolean process(UserData uData) {
return false;
@ -85,5 +85,8 @@ public class RandomData {
return test;
}
public static <T extends Enum> T randomEnum(Class<T> clazz) {
int x = r.nextInt(clazz.getEnumConstants().length);
return clazz.getEnumConstants()[x];
}
}

View File

@ -5,7 +5,7 @@ Originally the plugin only displayed data of other plugins, but now it gathers i
### Links
- [Spigot, Resource page](https://www.spigotmc.org/resources/plan-player-analytics.32536/)
- [Issues & Suggestions](https://github.com/Rsl1122/Plan-PlayerAnalytics/issues)
- [Licence](https://github.com/Rsl1122/Plan-PlayerAnalytics/blob/master/Plan/src/main/resources/licence.yml)
- [License](https://github.com/Rsl1122/Plan-PlayerAnalytics/blob/master/Plan/src/main/resources/license.yml)
## Documentation
- [Javadocs](https://rsl1122.github.io/Plan-PlayerAnalytics/) (OutDated)

View File

@ -62,7 +62,7 @@ Requirements:
- A cloudflare account
- A domain with full access
If you wish to bypass the security warning (not seeing that the connection isnt private), you can use ![Cloudflare](https://www.cloudflare.com)
If you wish to bypass the security warning (not seeing that the connection isnt private), you can use [Cloudflare](https://www.cloudflare.com)
- Connect your domain to Cloudflare
- Add a DNS record of the type “A” which points to your Server IP
@ -70,7 +70,7 @@ If you wish to bypass the security warning (not seeing that the connection isn
Notes:
If you only want to use HTTPS on the Analytics site, you can use the “Page Rules”
![Page Rules Tutorial](https://support.cloudflare.com/hc/en-us/articles/218411427-Page-Rules-Tutorial)
[Page Rules Tutorial](https://support.cloudflare.com/hc/en-us/articles/218411427-Page-Rules-Tutorial)
It is recommend to activate “Automatic HTTPS Rewrites” under “Crypto” to be able to use http://LINK.TLD as well.
This removes the need to write “https://” at the beginning of the address.