[Merge] Version 4.5.1 (#780)

This commit is contained in:
Risto Lahtela 2018-11-04 20:17:03 +02:00 committed by GitHub
commit b1da4ba18b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
70 changed files with 2188 additions and 1378 deletions

View File

@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.djrapitops</groupId>
<artifactId>Plan</artifactId>
<version>4.5.0-SNAPSHOT</version>
<version>4.5.0-SNAPSHOT</version>
<build>
<sourceDirectory>${basedir}/src/main/java</sourceDirectory>
<testSourceDirectory>${basedir}/src/test/java</testSourceDirectory>
@ -20,7 +20,7 @@
<include>**/*.js</include>
<include>**/*.css</include>
<include>locale/*.txt</include>
<include>**/*.ico</include>
<include>**/*.ico</include>
</includes>
</resource>
</resources>
@ -32,28 +32,28 @@
<configuration>
<source>1.8</source>
<target>1.8</target>
<annotationProcessorPaths>
<path>
<groupId>com.google.dagger</groupId>
<artifactId>dagger-compiler</artifactId>
<version>2.17</version>
</path>
<path>
<groupId>com.velocitypowered</groupId>
<artifactId>velocity-api</artifactId>
<version>1.0-SNAPSHOT</version>
</path>
<path>
<groupId>org.spongepowered</groupId>
<artifactId>spongeapi</artifactId>
<version>7.1.0</version>
</path>
</annotationProcessorPaths>
<annotationProcessorPaths>
<path>
<groupId>com.google.dagger</groupId>
<artifactId>dagger-compiler</artifactId>
<version>2.17</version>
</path>
<path>
<groupId>com.velocitypowered</groupId>
<artifactId>velocity-api</artifactId>
<version>1.0-SNAPSHOT</version>
</path>
<path>
<groupId>org.spongepowered</groupId>
<artifactId>spongeapi</artifactId>
<version>7.1.0</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.1</version>
<version>3.2.0</version>
<executions>
<execution>
<phase>package</phase>
@ -76,7 +76,6 @@
<exclude>org.mockito:*</exclude>
<exclude>org.easymock:*</exclude>
<exclude>junit:*</exclude>
<exclued>org.slf4j:*</exclued>
</excludes>
</artifactSet>
<relocations>
@ -99,6 +98,14 @@
<pattern>com.zaxxer</pattern>
<shadedPattern>plan.com.zaxxer</shadedPattern>
</relocation>
<relocation>
<pattern>org.h2</pattern>
<shadedPattern>plan.org.h2</shadedPattern>
</relocation>
<relocation>
<pattern>org.slf4j</pattern>
<shadedPattern>plan.org.slf4j</shadedPattern>
</relocation>
<relocation>
<pattern>org.bstats</pattern>
<shadedPattern>com.djrapitops.plan.utilities.metrics</shadedPattern>
@ -162,13 +169,13 @@
<url>https://repo.spongepowered.org/maven</url>
</repository>
<repository>
<id>velocity-repo</id>
<url>https://repo.velocitypowered.com/snapshots/</url>
<id>velocity-repo</id>
<url>https://repo.velocitypowered.com/snapshots/</url>
</repository>
<repository>
<id>md_5-snapshots</id>
<url>http://repo.md-5.net/content/repositories/snapshots/</url>
</repository>
<repository>
<id>md_5-snapshots</id>
<url>http://repo.md-5.net/content/repositories/snapshots/</url>
</repository>
<repository>
<id>bstats-repo</id>
<url>http://repo.bstats.org/content/repositories/releases/</url>
@ -312,109 +319,103 @@
</exclusions>
</dependency>
<dependency>
<groupId>com.velocitypowered</groupId>
<artifactId>velocity-api</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<artifactId>text</artifactId>
<groupId>net.kyori</groupId>
</exclusion>
<exclusion>
<artifactId>toml4j</artifactId>
<groupId>com.moandjiezana.toml</groupId>
</exclusion>
<exclusion>
<artifactId>guice</artifactId>
<groupId>com.google.inject</groupId>
</exclusion>
<exclusion>
<artifactId>checker-qual</artifactId>
<groupId>org.checkerframework</groupId>
</exclusion>
<exclusion>
<artifactId>gson</artifactId>
<groupId>com.google.code.gson</groupId>
</exclusion>
<exclusion>
<artifactId>guava</artifactId>
<groupId>com.google.guava</groupId>
</exclusion>
</exclusions>
<groupId>com.velocitypowered</groupId>
<artifactId>velocity-api</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<artifactId>text</artifactId>
<groupId>net.kyori</groupId>
</exclusion>
<exclusion>
<artifactId>toml4j</artifactId>
<groupId>com.moandjiezana.toml</groupId>
</exclusion>
<exclusion>
<artifactId>guice</artifactId>
<groupId>com.google.inject</groupId>
</exclusion>
<exclusion>
<artifactId>checker-qual</artifactId>
<groupId>org.checkerframework</groupId>
</exclusion>
<exclusion>
<artifactId>gson</artifactId>
<groupId>com.google.code.gson</groupId>
</exclusion>
<exclusion>
<artifactId>guava</artifactId>
<groupId>com.google.guava</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<dependency>
<groupId>org.spongepowered</groupId>
<artifactId>spongeapi</artifactId>
<version>7.1.0</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<artifactId>error_prone_annotations</artifactId>
<groupId>com.google.errorprone</groupId>
</exclusion>
<exclusion>
<artifactId>guava</artifactId>
<groupId>com.github.ben-manes.caffeine</groupId>
</exclusion>
<exclusion>
<artifactId>plugin-meta</artifactId>
<groupId>org.spongepowered</groupId>
<artifactId>spongeapi</artifactId>
<version>7.1.0</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<artifactId>error_prone_annotations</artifactId>
<groupId>com.google.errorprone</groupId>
</exclusion>
<exclusion>
<artifactId>guava</artifactId>
<groupId>com.github.ben-manes.caffeine</groupId>
</exclusion>
<exclusion>
<artifactId>plugin-meta</artifactId>
<groupId>org.spongepowered</groupId>
</exclusion>
<exclusion>
<artifactId>configurate-hocon</artifactId>
<groupId>org.spongepowered</groupId>
</exclusion>
<exclusion>
<artifactId>configurate-gson</artifactId>
<groupId>org.spongepowered</groupId>
</exclusion>
<exclusion>
<artifactId>configurate-yaml</artifactId>
<groupId>org.spongepowered</groupId>
</exclusion>
<exclusion>
<artifactId>flow-math</artifactId>
<groupId>com.flowpowered</groupId>
</exclusion>
<exclusion>
<artifactId>flow-noise</artifactId>
<groupId>com.flowpowered</groupId>
</exclusion>
<exclusion>
<artifactId>jsr305</artifactId>
<groupId>com.google.code.findbugs</groupId>
</exclusion>
<exclusion>
<artifactId>guice</artifactId>
<groupId>com.google.inject</groupId>
</exclusion>
<exclusion>
<artifactId>asm</artifactId>
<groupId>org.ow2.asm</groupId>
</exclusion>
<exclusion>
<artifactId>gson</artifactId>
<groupId>com.google.code.gson</groupId>
</exclusion>
<exclusion>
<artifactId>guava</artifactId>
<groupId>com.google.guava</groupId>
</exclusion>
</exclusions>
</dependency>
</exclusion>
<exclusion>
<artifactId>configurate-hocon</artifactId>
<groupId>org.spongepowered</groupId>
</exclusion>
<exclusion>
<artifactId>configurate-gson</artifactId>
<groupId>org.spongepowered</groupId>
</exclusion>
<exclusion>
<artifactId>configurate-yaml</artifactId>
<groupId>org.spongepowered</groupId>
</exclusion>
<exclusion>
<artifactId>flow-math</artifactId>
<groupId>com.flowpowered</groupId>
</exclusion>
<exclusion>
<artifactId>flow-noise</artifactId>
<groupId>com.flowpowered</groupId>
</exclusion>
<exclusion>
<artifactId>jsr305</artifactId>
<groupId>com.google.code.findbugs</groupId>
</exclusion>
<exclusion>
<artifactId>guice</artifactId>
<groupId>com.google.inject</groupId>
</exclusion>
<exclusion>
<artifactId>asm</artifactId>
<groupId>org.ow2.asm</groupId>
</exclusion>
<exclusion>
<artifactId>gson</artifactId>
<groupId>com.google.code.gson</groupId>
</exclusion>
<exclusion>
<artifactId>guava</artifactId>
<groupId>com.google.guava</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.imaginarycode.minecraft</groupId>
<artifactId>RedisBungee</artifactId>
<version>0.3.8-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-nop</artifactId>
<version>1.7.25</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>

View File

@ -171,6 +171,13 @@
<version>1.5.2</version>
</dependency>
<!-- H2 -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.196</version> <!-- This version is necessary so Sponge will not crash -->
</dependency>
<!-- Mockito (Test Dependency) -->
<dependency>
<groupId>org.mockito</groupId>
@ -179,6 +186,7 @@
<type>jar</type>
<scope>test</scope>
</dependency>
<!-- SQLite (Test Dependency) -->
<dependency>
<groupId>org.xerial</groupId>
@ -256,7 +264,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.1</version>
<version>3.2.0</version>
<executions>
<execution>
<phase>package</phase>
@ -302,6 +310,10 @@
<pattern>com.zaxxer</pattern>
<shadedPattern>plan.com.zaxxer</shadedPattern>
</relocation>
<relocation>
<pattern>org.h2</pattern>
<shadedPattern>plan.org.h2</shadedPattern>
</relocation>
<!--<relocation>-->
<!--<pattern>org.slf4j</pattern>-->
<!--<shadedPattern>plan.org.slf4j</shadedPattern>-->

View File

@ -43,7 +43,7 @@ import java.io.InputStream;
@Plugin(
id = "plan",
name = "Plan",
version = "4.5.0",
version = "4.5.1",
description = "Player Analytics Plugin by Rsl1122",
authors = {"Rsl1122"},
dependencies = {

View File

@ -46,7 +46,7 @@ import java.nio.file.Path;
@Plugin(
id = "plan",
name = "Plan",
version = "4.5.0",
version = "4.5.1",
description = "Player Analytics Plugin by Rsl1122",
authors = {"Rsl1122"}
)

View File

@ -26,6 +26,6 @@ import com.djrapitops.plan.system.database.databases.Database;
public class UnsupportedTransferDatabaseException extends WebException {
public UnsupportedTransferDatabaseException(Database db) {
super(db.getName() + " does not support Transfer operations!");
super(db.getType().getName() + " does not support Transfer operations!");
}
}

View File

@ -76,7 +76,7 @@ public class InfoCommand extends CommandNode {
"",
locale.getString(CommandLang.INFO_VERSION, plugin.getVersion()),
locale.getString(CommandLang.INFO_UPDATE, updateAvailable),
locale.getString(CommandLang.INFO_DATABASE, dbSystem.getDatabase().getName()),
locale.getString(CommandLang.INFO_DATABASE, dbSystem.getDatabase().getType().getName()),
locale.getString(CommandLang.INFO_BUNGEE_CONNECTION, connectedToBungee),
"",
">"

View File

@ -19,6 +19,7 @@ package com.djrapitops.plan.command.commands.manage;
import com.djrapitops.plan.api.exceptions.database.DBException;
import com.djrapitops.plan.api.exceptions.database.DBInitException;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.database.databases.DBType;
import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plan.system.database.databases.sql.SQLiteDB;
import com.djrapitops.plan.system.locale.Locale;
@ -93,7 +94,7 @@ public class ManageBackupCommand extends CommandNode {
String dbName = args[0].toLowerCase();
boolean isCorrectDB = Verify.equalsOne(dbName, "sqlite", "mysql");
boolean isCorrectDB = DBType.exists(dbName);
Verify.isTrue(isCorrectDB,
() -> new IllegalArgumentException(locale.getString(ManageLang.FAIL_INCORRECT_DB, dbName)));

View File

@ -19,6 +19,7 @@ package com.djrapitops.plan.command.commands.manage;
import com.djrapitops.plan.api.exceptions.database.DBInitException;
import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.database.databases.DBType;
import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plan.system.locale.Locale;
import com.djrapitops.plan.system.locale.lang.CmdHelpLang;
@ -78,7 +79,7 @@ public class ManageClearCommand extends CommandNode {
String dbName = args[0].toLowerCase();
boolean isCorrectDB = "sqlite".equals(dbName) || "mysql".equals(dbName);
boolean isCorrectDB = DBType.exists(dbName);
Verify.isTrue(isCorrectDB,
() -> new IllegalArgumentException(locale.getString(ManageLang.FAIL_INCORRECT_DB, dbName)));

View File

@ -18,6 +18,7 @@ package com.djrapitops.plan.command.commands.manage;
import com.djrapitops.plan.PlanPlugin;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.database.databases.DBType;
import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plan.system.locale.Locale;
import com.djrapitops.plan.system.locale.lang.CmdHelpLang;
@ -73,11 +74,11 @@ public class ManageHotSwapCommand extends CommandNode {
String dbName = args[0].toLowerCase();
boolean isCorrectDB = Verify.equalsOne(dbName, "sqlite", "mysql");
boolean isCorrectDB = DBType.exists(dbName);
Verify.isTrue(isCorrectDB,
() -> new IllegalArgumentException(locale.getString(ManageLang.FAIL_INCORRECT_DB, dbName)));
Verify.isFalse(dbName.equals(dbSystem.getDatabase().getConfigName()),
Verify.isFalse(dbName.equals(dbSystem.getDatabase().getType().getConfigName()),
() -> new IllegalArgumentException(locale.getString(ManageLang.FAIL_SAME_DB)));
try {

View File

@ -17,6 +17,7 @@
package com.djrapitops.plan.command.commands.manage;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.database.databases.DBType;
import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plan.system.locale.Locale;
import com.djrapitops.plan.system.locale.lang.CmdHelpLang;
@ -77,12 +78,12 @@ public class ManageMoveCommand extends CommandNode {
() -> new IllegalArgumentException(locale.getString(CommandLang.FAIL_REQ_ARGS, 2, Arrays.toString(this.getArguments()))));
String fromDB = args[0].toLowerCase();
boolean isCorrectDB = Verify.equalsOne(fromDB, "sqlite", "mysql");
boolean isCorrectDB = DBType.exists(fromDB);
Verify.isTrue(isCorrectDB,
() -> new IllegalArgumentException(locale.getString(ManageLang.FAIL_INCORRECT_DB, fromDB)));
String toDB = args[1].toLowerCase();
isCorrectDB = Verify.equalsOne(toDB, "sqlite", "mysql");
isCorrectDB = DBType.exists(toDB);
Verify.isTrue(isCorrectDB,
() -> new IllegalArgumentException(locale.getString(ManageLang.FAIL_INCORRECT_DB, toDB)));
@ -115,9 +116,9 @@ public class ManageMoveCommand extends CommandNode {
sender.sendMessage(locale.getString(ManageLang.PROGRESS_SUCCESS));
boolean movingToCurrentDB = toDatabase.getConfigName().equalsIgnoreCase(dbSystem.getDatabase().getConfigName());
boolean movingToCurrentDB = toDatabase.getType() == dbSystem.getDatabase().getType();
if (movingToCurrentDB) {
sender.sendMessage(locale.getString(ManageLang.HOTSWAP_REMINDER, toDatabase.getConfigName()));
sender.sendMessage(locale.getString(ManageLang.HOTSWAP_REMINDER, toDatabase.getType().getConfigName()));
}
} catch (Exception e) {
errorHandler.log(L.ERROR, this.getClass(), e);

View File

@ -110,7 +110,7 @@ public class ManageRemoveCommand extends CommandNode {
if (!Verify.contains("-a", args)) {
sender.sendMessage(
locale.getString(ManageLang.CONFIRMATION,
locale.getString(ManageLang.CONFIRM_REMOVAL, db.getName())
locale.getString(ManageLang.CONFIRM_REMOVAL, db.getType().getName())
)
);
return;

View File

@ -17,6 +17,7 @@
package com.djrapitops.plan.command.commands.manage;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.database.databases.DBType;
import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plan.system.database.databases.sql.SQLiteDB;
import com.djrapitops.plan.system.file.PlanFiles;
@ -84,7 +85,7 @@ public class ManageRestoreCommand extends CommandNode {
String backupDbName = args[0];
String dbName = args[1].toLowerCase();
boolean isCorrectDB = Verify.equalsOne(dbName, "sqlite", "mysql");
boolean isCorrectDB = DBType.exists(dbName);
Verify.isTrue(isCorrectDB,
() -> new IllegalArgumentException(locale.getString(ManageLang.FAIL_INCORRECT_DB, dbName)));
@ -96,7 +97,7 @@ public class ManageRestoreCommand extends CommandNode {
database.init();
if (!Verify.contains("-a", args)) {
sender.sendMessage(locale.getString(ManageLang.CONFIRMATION, locale.getString(ManageLang.CONFIRM_OVERWRITE, database.getName())));
sender.sendMessage(locale.getString(ManageLang.CONFIRMATION, locale.getString(ManageLang.CONFIRM_OVERWRITE, database.getType().getName())));
return;
}

View File

@ -160,7 +160,7 @@ public class SessionsMutator {
public long toMedianSessionLength() {
List<Long> sessionLengths = sessions.stream().map(Session::getLength).collect(Collectors.toList());
return (long) Median.forLong(sessionLengths).calculate();
return (long) Median.forList(sessionLengths).calculate();
}
public int toAverageUniqueJoinsPerDay() {

View File

@ -17,6 +17,7 @@
package com.djrapitops.plan.system.database;
import com.djrapitops.plan.api.exceptions.EnableException;
import com.djrapitops.plan.system.database.databases.sql.H2DB;
import com.djrapitops.plan.system.database.databases.sql.MySQLDB;
import com.djrapitops.plan.system.database.databases.sql.SQLiteDB;
import com.djrapitops.plan.system.locale.Locale;
@ -44,15 +45,17 @@ public class BukkitDBSystem extends DBSystem {
Locale locale,
MySQLDB mySQLDB,
SQLiteDB.Factory sqLiteDB,
H2DB.Factory h2DB,
PlanConfig config,
PluginLogger logger,
Timings timings,
ErrorHandler errorHandler
) {
super(locale, sqLiteDB, logger, timings, errorHandler);
super(locale, sqLiteDB, h2DB, logger, timings, errorHandler);
this.config = config;
databases.add(mySQLDB);
databases.add(h2DB.usingDefaultFile());
databases.add(sqLiteDB.usingDefaultFile());
}

View File

@ -21,6 +21,7 @@ import com.djrapitops.plan.api.exceptions.database.DBException;
import com.djrapitops.plan.api.exceptions.database.DBInitException;
import com.djrapitops.plan.system.SubSystem;
import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plan.system.database.databases.sql.H2DB;
import com.djrapitops.plan.system.database.databases.sql.SQLiteDB;
import com.djrapitops.plan.system.locale.Locale;
import com.djrapitops.plan.system.locale.lang.PluginLang;
@ -44,6 +45,7 @@ public abstract class DBSystem implements SubSystem {
protected final Locale locale;
private final SQLiteDB.Factory sqLiteFactory;
private final H2DB.Factory h2Factory;
protected final PluginLogger logger;
protected final Timings timings;
protected final ErrorHandler errorHandler;
@ -54,12 +56,14 @@ public abstract class DBSystem implements SubSystem {
public DBSystem(
Locale locale,
SQLiteDB.Factory sqLiteDB,
H2DB.Factory h2Factory,
PluginLogger logger,
Timings timings,
ErrorHandler errorHandler
) {
this.locale = locale;
sqLiteFactory = sqLiteDB;
this.sqLiteFactory = sqLiteDB;
this.h2Factory = h2Factory;
this.logger = logger;
this.timings = timings;
this.errorHandler = errorHandler;
@ -68,7 +72,7 @@ public abstract class DBSystem implements SubSystem {
public Database getActiveDatabaseByName(String dbName) {
for (Database database : getDatabases()) {
String dbConfigName = database.getConfigName();
String dbConfigName = database.getType().getConfigName();
if (Verify.equalsIgnoreCase(dbName, dbConfigName)) {
return database;
}
@ -100,11 +104,11 @@ public abstract class DBSystem implements SubSystem {
try {
db.init();
db.scheduleClean(20L);
logger.info(locale.getString(PluginLang.ENABLED_DATABASE, db.getName()));
logger.info(locale.getString(PluginLang.ENABLED_DATABASE, db.getType().getName()));
} catch (DBInitException e) {
Throwable cause = e.getCause();
String message = cause == null ? e.getMessage() : cause.getMessage();
throw new EnableException((db != null ? db.getName() : "Database") + " init failure: " + message, cause);
throw new EnableException((db != null ? db.getType().getName() : "Database") + " init failure: " + message, cause);
}
}
@ -116,4 +120,8 @@ public abstract class DBSystem implements SubSystem {
public SQLiteDB.Factory getSqLiteFactory() {
return sqLiteFactory;
}
public H2DB.Factory getH2Factory() {
return h2Factory;
}
}

View File

@ -16,6 +16,7 @@
*/
package com.djrapitops.plan.system.database;
import com.djrapitops.plan.system.database.databases.sql.H2DB;
import com.djrapitops.plan.system.database.databases.sql.MySQLDB;
import com.djrapitops.plan.system.database.databases.sql.SQLiteDB;
import com.djrapitops.plan.system.locale.Locale;
@ -39,11 +40,12 @@ public class ProxyDBSystem extends DBSystem {
Locale locale,
MySQLDB mySQLDB,
SQLiteDB.Factory sqLiteDB,
H2DB.Factory h2DB,
PluginLogger logger,
Timings timings,
ErrorHandler errorHandler
) {
super(locale, sqLiteDB, logger, timings, errorHandler);
super(locale, sqLiteDB, h2DB, logger, timings, errorHandler);
databases.add(mySQLDB);
db = mySQLDB;
}

View File

@ -17,6 +17,7 @@
package com.djrapitops.plan.system.database;
import com.djrapitops.plan.api.exceptions.EnableException;
import com.djrapitops.plan.system.database.databases.sql.H2DB;
import com.djrapitops.plan.system.database.databases.sql.SQLiteDB;
import com.djrapitops.plan.system.database.databases.sql.SpongeMySQLDB;
import com.djrapitops.plan.system.locale.Locale;
@ -44,16 +45,18 @@ public class SpongeDBSystem extends DBSystem {
Locale locale,
SpongeMySQLDB spongeMySQLDB,
SQLiteDB.Factory sqLiteDB,
H2DB.Factory h2DB,
PlanConfig config,
PluginLogger logger,
Timings timings,
ErrorHandler errorHandler
) {
super(locale, sqLiteDB, logger, timings, errorHandler);
super(locale, sqLiteDB, h2DB, logger, timings, errorHandler);
this.config = config;
databases.add(spongeMySQLDB);
databases.add(sqLiteDB.usingDefaultFile());
databases.add(h2DB.usingDefaultFile());
}
@Override

View File

@ -0,0 +1,106 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the LGNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* LGNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.system.database.databases;
import java.util.Optional;
/**
* An enum which stores the name, the config name and if the Database supports MySQL Queries
*
* @author Fuzzlemann
* @since 4.5.1
*/
public enum DBType {
MySQL("MySQL", true),
SQLite("SQLite", false),
H2("H2", true);
private final String name;
private final String configName;
private final boolean supportingMySQLQueries;
DBType(String name, boolean supportingMySQLQueries) {
this.name = name;
this.configName = name.toLowerCase().trim();
this.supportingMySQLQueries = supportingMySQLQueries;
}
/**
* Gets the name of the {@code DBType}
*
* @return the name
*/
public String getName() {
return name;
}
/**
* Gets the config name of the {@code DBType}
*
* @return the config name
*/
public String getConfigName() {
return configName;
}
/**
* Used to check if the {@code DBType} supports <b>most</b> MySQL Queries.<p>
* When specific Statements are not compatible, the {@code DBType} should be checked.
*
* @return if the database supports MySQL Queries
*/
public boolean supportsMySQLQueries() {
return supportingMySQLQueries;
}
/**
* Gets an {@code Optional<DBType>} which matches {@code name}.<p>
* This method is case-insensitive.<p>
* The {@code Optional<DBType>} is empty when no {@code DBType} is found.
*
* @param name the name of the {@code DBType}
* @return an {@code Optional<DBType>}
*/
public static Optional<DBType> getForName(String name) {
for (DBType dbType : DBType.values()) {
if (dbType.getName().equalsIgnoreCase(name)) {
return Optional.of(dbType);
}
}
return Optional.empty();
}
/**
* Checks if the name of a {@code DBType} corresponds to {@code name}.<p>
* This method is case-insensitive.
*
* @param name the name of the {@code DBType}
* @return if the {@code DBType} exists
* @see DBType#getForName(String)
*/
public static boolean exists(String name) {
for (DBType dbType : DBType.values()) {
if (dbType.getName().equalsIgnoreCase(name)) {
return true;
}
}
return false;
}
}

View File

@ -29,7 +29,7 @@ import com.djrapitops.plan.system.database.databases.operation.*;
*/
public abstract class Database {
protected boolean open = false;
protected volatile boolean open = false;
public abstract void init() throws DBInitException;
@ -47,18 +47,13 @@ public abstract class Database {
public abstract SaveOperations save();
public abstract String getName();
/**
* Used to get the config name of the database type.
* <p>
* Thread safe.
* Used to get the {@code DBType} of the Database
*
* @return sqlite/mysql
* @return the {@code DBType}
* @see DBType
*/
public String getConfigName() {
return getName().toLowerCase().trim();
}
public abstract DBType getType();
public abstract void close() throws DBException;

View File

@ -0,0 +1,240 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the LGNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* LGNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.system.database.databases.sql;
import com.djrapitops.plan.api.exceptions.database.DBInitException;
import com.djrapitops.plan.data.store.containers.NetworkContainer;
import com.djrapitops.plan.system.database.databases.DBType;
import com.djrapitops.plan.system.file.PlanFiles;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.locale.Locale;
import com.djrapitops.plan.system.settings.Settings;
import com.djrapitops.plan.system.settings.config.PlanConfig;
import com.djrapitops.plan.utilities.MiscUtils;
import com.djrapitops.plugin.benchmarking.Timings;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.logging.console.PluginLogger;
import com.djrapitops.plugin.logging.error.ErrorHandler;
import com.djrapitops.plugin.task.PluginTask;
import com.djrapitops.plugin.task.RunnableFactory;
import dagger.Lazy;
import org.h2.jdbcx.JdbcDataSource;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.io.File;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Objects;
/**
* Implementation of the H2 database
*
* @author Fuzzlemann
* @since 4.5.1
*/
public class H2DB extends SQLDB {
private final File databaseFile;
private final String dbName;
private Connection connection;
private PluginTask connectionPingTask;
private H2DB(
File databaseFile,
Locale locale,
PlanConfig config,
Lazy<ServerInfo> serverInfo,
NetworkContainer.Factory networkContainerFactory,
RunnableFactory runnableFactory,
PluginLogger logger,
Timings timings,
ErrorHandler errorHandler
) {
super(() -> serverInfo.get().getServerUUID(), locale, config, networkContainerFactory, runnableFactory, logger, timings, errorHandler);
dbName = databaseFile.getName();
this.databaseFile = databaseFile;
}
@Override
public void setupDataSource() throws DBInitException {
try {
Class.forName("org.h2.Driver");
} catch (ClassNotFoundException e) {
errorHandler.log(L.CRITICAL, this.getClass(), e);
}
try {
connection = getNewConnection(databaseFile);
} catch (SQLException e) {
throw new DBInitException(e);
}
execute("SET REFERENTIAL_INTEGRITY FALSE");
startConnectionPingTask();
}
public Connection getNewConnection(File dbFile) throws SQLException {
String dbFilePath = dbFile.getAbsolutePath();
Connection newConnection = getConnectionFor(dbFilePath);
logger.debug("H2 " + dbName + ": Opened a new Connection");
newConnection.setAutoCommit(false);
return newConnection;
}
private Connection getConnectionFor(String dbFilePath) throws SQLException {
String username = config.getString(Settings.DB_USER);
String password = config.getString(Settings.DB_PASS);
JdbcDataSource jdbcDataSource = new JdbcDataSource();
jdbcDataSource.setURL("jdbc:h2:file:" + dbFilePath + ";mode=MySQL");
jdbcDataSource.setUser(username);
jdbcDataSource.setPassword(password);
return jdbcDataSource.getConnection();
}
private void startConnectionPingTask() {
stopConnectionPingTask();
try {
// Maintains Connection.
connectionPingTask = runnableFactory.create("DBConnectionPingTask " + getType().getName(),
new KeepAliveTask(connection, () -> getNewConnection(databaseFile), logger, errorHandler)
).runTaskTimerAsynchronously(60L * 20L, 60L * 20L);
} catch (Exception ignored) {
}
}
private void stopConnectionPingTask() {
if (connectionPingTask != null) {
try {
connectionPingTask.cancel();
} catch (Exception ignored) {
}
}
}
@Override
public DBType getType() {
return DBType.H2;
}
@Override
public Connection getConnection() throws SQLException {
if (connection == null) {
connection = getNewConnection(databaseFile);
}
return connection;
}
@Override
public void close() {
super.close();
stopConnectionPingTask();
if (connection != null) {
logger.debug("H2DB " + dbName + ": Closed Connection");
MiscUtils.close(connection);
}
}
@Override
public void commit(Connection connection) {
try {
connection.commit();
} catch (SQLException e) {
if (!e.getMessage().contains("cannot commit")) {
errorHandler.log(L.ERROR, this.getClass(), e);
}
}
}
@Override
public void returnToPool(Connection connection) {
// Connection pool not in use, no action required.
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
H2DB h2DB = (H2DB) o;
return Objects.equals(dbName, h2DB.dbName);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), dbName);
}
@Singleton
public static class Factory {
private final Locale locale;
private final PlanConfig config;
private final Lazy<ServerInfo> serverInfo;
private final NetworkContainer.Factory networkContainerFactory;
private final RunnableFactory runnableFactory;
private final PluginLogger logger;
private final Timings timings;
private final ErrorHandler errorHandler;
private PlanFiles files;
@Inject
public Factory(
Locale locale,
PlanConfig config,
PlanFiles files,
Lazy<ServerInfo> serverInfo,
NetworkContainer.Factory networkContainerFactory,
RunnableFactory runnableFactory,
PluginLogger logger,
Timings timings,
ErrorHandler errorHandler
) {
this.locale = locale;
this.config = config;
this.files = files;
this.serverInfo = serverInfo;
this.networkContainerFactory = networkContainerFactory;
this.runnableFactory = runnableFactory;
this.logger = logger;
this.timings = timings;
this.errorHandler = errorHandler;
}
public H2DB usingDefaultFile() {
return usingFileCalled("h2database");
}
public H2DB usingFileCalled(String fileName) {
return usingFile(files.getFileFromPluginFolder(fileName));
}
public H2DB usingFile(File databaseFile) {
return new H2DB(databaseFile,
locale, config, serverInfo,
networkContainerFactory,
runnableFactory, logger, timings, errorHandler
);
}
}
}

View File

@ -0,0 +1,59 @@
package com.djrapitops.plan.system.database.databases.sql;
import com.djrapitops.plan.utilities.MiscUtils;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.logging.console.PluginLogger;
import com.djrapitops.plugin.logging.error.ErrorHandler;
import com.djrapitops.plugin.task.AbsRunnable;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
/**
* The task which handles the upkeep of the {@code Connection}
*
* @author Fuzzlemann
* @since 4.5.1
*/
public class KeepAliveTask extends AbsRunnable {
private Connection connection;
private final IReconnect iReconnect;
private final PluginLogger logger;
private final ErrorHandler errorHandler;
public KeepAliveTask(Connection connection, IReconnect iReconnect, PluginLogger logger, ErrorHandler errorHandler) {
this.connection = connection;
this.iReconnect = iReconnect;
this.logger = logger;
this.errorHandler = errorHandler;
}
@Override
public void run() {
Statement statement = null;
ResultSet resultSet = null;
try {
if (connection != null && !connection.isClosed()) {
statement = connection.createStatement();
resultSet = statement.executeQuery("/* ping */ SELECT 1");
}
} catch (SQLException e) {
logger.debug("Something went wrong during SQL Connection upkeep task.");
try {
connection = iReconnect.reconnect();
} catch (SQLException e1) {
errorHandler.log(L.ERROR, this.getClass(), e1);
logger.error("SQL connection maintaining task had to be closed due to exception.");
this.cancel();
}
} finally {
MiscUtils.close(statement, resultSet);
}
}
public interface IReconnect {
Connection reconnect() throws SQLException;
}
}

View File

@ -19,6 +19,7 @@ package com.djrapitops.plan.system.database.databases.sql;
import com.djrapitops.plan.api.exceptions.database.DBInitException;
import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.data.store.containers.NetworkContainer;
import com.djrapitops.plan.system.database.databases.DBType;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.locale.Locale;
import com.djrapitops.plan.system.locale.lang.PluginLang;
@ -67,12 +68,9 @@ public class MySQLDB extends SQLDB {
increment++;
}
/**
* @return the name of the Database
*/
@Override
public String getName() {
return "MySQL";
public DBType getType() {
return DBType.MySQL;
}
/**
@ -136,10 +134,11 @@ public class MySQLDB extends SQLDB {
@Override
public void close() {
super.close();
if (dataSource instanceof HikariDataSource) {
((HikariDataSource) dataSource).close();
}
super.close();
}
@Override
@ -158,11 +157,6 @@ public class MySQLDB extends SQLDB {
returnToPool(connection);
}
@Override
public boolean isUsingMySQL() {
return true;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;

View File

@ -288,6 +288,10 @@ public abstract class SQLDB extends Database {
public abstract void returnToPool(Connection connection);
public boolean execute(ExecStatement statement) {
if (!isOpen()) {
throw new DBOpException("SQL Statement tried to execute while connection closed");
}
Connection connection = null;
try {
connection = getConnection();
@ -328,6 +332,10 @@ public abstract class SQLDB extends Database {
}
public void executeBatch(ExecStatement statement) {
if (!isOpen()) {
throw new DBOpException("SQL Batch tried to execute while connection closed");
}
Connection connection = null;
try {
connection = getConnection();
@ -346,6 +354,10 @@ public abstract class SQLDB extends Database {
}
public <T> T query(QueryStatement<T> statement) {
if (!isOpen()) {
throw new DBOpException("SQL Query tried to execute while connection closed");
}
Connection connection = null;
try {
connection = getConnection();
@ -419,10 +431,6 @@ public abstract class SQLDB extends Database {
return pingTable;
}
public boolean isUsingMySQL() {
return false;
}
@Override
public BackupOperations backup() {
return backupOps;
@ -468,12 +476,12 @@ public abstract class SQLDB extends Database {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SQLDB sqldb = (SQLDB) o;
return getName().equals(sqldb.getName());
return getType() == sqldb.getType();
}
@Override
public int hashCode() {
return Objects.hash(getName());
return Objects.hash(getType().getName());
}
public Supplier<UUID> getServerUUIDSupplier() {

View File

@ -18,6 +18,7 @@ package com.djrapitops.plan.system.database.databases.sql;
import com.djrapitops.plan.api.exceptions.database.DBInitException;
import com.djrapitops.plan.data.store.containers.NetworkContainer;
import com.djrapitops.plan.system.database.databases.DBType;
import com.djrapitops.plan.system.file.PlanFiles;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.locale.Locale;
@ -28,7 +29,6 @@ import com.djrapitops.plugin.benchmarking.Timings;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.logging.console.PluginLogger;
import com.djrapitops.plugin.logging.error.ErrorHandler;
import com.djrapitops.plugin.task.AbsRunnable;
import com.djrapitops.plugin.task.PluginTask;
import com.djrapitops.plugin.task.RunnableFactory;
import dagger.Lazy;
@ -36,7 +36,9 @@ import dagger.Lazy;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.io.File;
import java.sql.*;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Objects;
/**
@ -104,30 +106,9 @@ public class SQLiteDB extends SQLDB {
stopConnectionPingTask();
try {
// Maintains Connection.
connectionPingTask = runnableFactory.create("DBConnectionPingTask " + getName(), new AbsRunnable() {
@Override
public void run() {
Statement statement = null;
ResultSet resultSet = null;
try {
if (connection != null && !connection.isClosed()) {
statement = connection.createStatement();
resultSet = statement.executeQuery("/* ping */ SELECT 1");
}
} catch (SQLException e) {
logger.debug("Something went wrong during SQLite Connection upkeep task.");
try {
connection = getNewConnection(databaseFile);
} catch (SQLException e1) {
errorHandler.log(L.ERROR, this.getClass(), e1);
logger.error("SQLite connection maintaining task had to be closed due to exception.");
this.cancel();
}
} finally {
MiscUtils.close(statement, resultSet);
}
}
}).runTaskTimerAsynchronously(60L * 20L, 60L * 20L);
connectionPingTask = runnableFactory.create("DBConnectionPingTask " + getType().getName(),
new KeepAliveTask(connection, () -> getNewConnection(databaseFile), logger, errorHandler)
).runTaskTimerAsynchronously(60L * 20L, 60L * 20L);
} catch (Exception ignored) {
}
}
@ -141,12 +122,9 @@ public class SQLiteDB extends SQLDB {
}
}
/**
* @return the name of the Database
*/
@Override
public String getName() {
return "SQLite";
public DBType getType() {
return DBType.SQLite;
}
@Override
@ -159,12 +137,13 @@ public class SQLiteDB extends SQLDB {
@Override
public void close() {
super.close();
stopConnectionPingTask();
if (connection != null) {
logger.debug("SQLite " + dbName + ": Closed Connection");
MiscUtils.close(connection);
}
super.close();
}
@Override
@ -250,4 +229,5 @@ public class SQLiteDB extends SQLDB {
}
}
}

View File

@ -24,6 +24,7 @@ import com.djrapitops.plan.data.store.mutators.PerServerMutator;
import com.djrapitops.plan.data.store.mutators.PlayersMutator;
import com.djrapitops.plan.data.store.mutators.SessionsMutator;
import com.djrapitops.plan.data.store.objects.DateObj;
import com.djrapitops.plan.data.store.objects.Nickname;
import com.djrapitops.plan.data.time.WorldTimes;
import com.djrapitops.plan.system.cache.SessionCache;
import com.djrapitops.plan.system.database.databases.operation.FetchOperations;
@ -123,6 +124,7 @@ public class SQLFetchOps extends SQLOps implements FetchOperations {
Map<UUID, Integer> timesKicked = usersTable.getAllTimesKicked();
Map<UUID, List<GeoInfo>> geoInfo = geoInfoTable.getAllGeoInfo();
Map<UUID, List<Ping>> allPings = pingTable.getAllPings();
Map<UUID, List<Nickname>> allNicknames = nicknamesTable.getAllNicknamesUnmapped();
Map<UUID, List<Session>> sessions = sessionsTable.getSessionInfoOfServer(serverUUID);
Map<UUID, Map<UUID, List<Session>>> map = new HashMap<>();
@ -144,7 +146,7 @@ public class SQLFetchOps extends SQLOps implements FetchOperations {
container.putRawData(PlayerKeys.KICK_COUNT, timesKicked.get(uuid));
container.putRawData(PlayerKeys.GEO_INFO, geoInfo.get(uuid));
container.putRawData(PlayerKeys.PING, allPings.get(uuid));
container.putCachingSupplier(PlayerKeys.NICKNAMES, () -> nicknamesTable.getNicknameInformation(uuid));
container.putRawData(PlayerKeys.NICKNAMES, allNicknames.get(uuid));
container.putRawData(PlayerKeys.PER_SERVER, perServerInfo.get(uuid));
container.putRawData(PlayerKeys.BANNED, userInfo.isBanned());
@ -187,6 +189,7 @@ public class SQLFetchOps extends SQLOps implements FetchOperations {
Map<UUID, Integer> timesKicked = usersTable.getAllTimesKicked();
Map<UUID, List<GeoInfo>> geoInfo = geoInfoTable.getAllGeoInfo();
Map<UUID, List<Ping>> allPings = pingTable.getAllPings();
Map<UUID, List<Nickname>> allNicknames = nicknamesTable.getAllNicknamesUnmapped();
Map<UUID, Map<UUID, List<Session>>> sessions = sessionsTable.getAllSessions(false);
Map<UUID, List<UserInfo>> allUserInfo = userInfoTable.getAllUserInfo();
@ -202,7 +205,7 @@ public class SQLFetchOps extends SQLOps implements FetchOperations {
container.putRawData(PlayerKeys.KICK_COUNT, timesKicked.get(uuid));
container.putRawData(PlayerKeys.GEO_INFO, geoInfo.get(uuid));
container.putRawData(PlayerKeys.PING, allPings.get(uuid));
container.putCachingSupplier(PlayerKeys.NICKNAMES, () -> nicknamesTable.getNicknameInformation(uuid));
container.putRawData(PlayerKeys.NICKNAMES, allNicknames.get(uuid));
container.putRawData(PlayerKeys.PER_SERVER, perServerInfo.get(uuid));
container.putCachingSupplier(PlayerKeys.SESSIONS, () -> {

View File

@ -16,6 +16,7 @@
*/
package com.djrapitops.plan.system.database.databases.sql.patches;
import com.djrapitops.plan.system.database.databases.DBType;
import com.djrapitops.plan.system.database.databases.sql.SQLDB;
import com.djrapitops.plan.system.database.databases.sql.processing.QueryAllStatement;
import com.djrapitops.plan.system.database.databases.sql.processing.QueryStatement;
@ -30,11 +31,11 @@ import java.util.UUID;
public abstract class Patch {
protected final SQLDB db;
protected final boolean usingMySQL;
protected final DBType dbType;
public Patch(SQLDB db) {
this.db = db;
usingMySQL = db.isUsingMySQL();
this.dbType = db.getType();
}
public abstract boolean hasBeenApplied();
@ -46,15 +47,25 @@ public abstract class Patch {
}
public boolean hasTable(String tableName) {
String sql = usingMySQL ?
"SELECT * FROM information_schema.TABLES WHERE table_name=? AND TABLE_SCHEMA=? LIMIT 1" :
"SELECT tbl_name FROM sqlite_master WHERE tbl_name=?";
boolean secondParameter;
String sql;
if (dbType == DBType.H2) {
sql = "SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME=?";
secondParameter = false;
} else if (dbType.supportsMySQLQueries()) {
sql = "SELECT * FROM information_schema.TABLES WHERE table_name=? AND TABLE_SCHEMA=? LIMIT 1";
secondParameter = true;
} else {
sql = "SELECT tbl_name FROM sqlite_master WHERE tbl_name=?";
secondParameter = false;
}
return query(new QueryStatement<Boolean>(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, tableName);
if (usingMySQL) {
if (secondParameter) {
statement.setString(2, db.getConfig().getString(Settings.DB_DATABASE));
}
}
@ -67,36 +78,49 @@ public abstract class Patch {
}
protected boolean hasColumn(String tableName, String columnName) {
return usingMySQL ?
query(new QueryStatement<Boolean>("SELECT * FROM information_schema.COLUMNS" +
" WHERE TABLE_NAME=? AND COLUMN_NAME=? AND TABLE_SCHEMA=?") {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, tableName);
statement.setString(2, columnName);
if (dbType.supportsMySQLQueries()) {
String query;
if (dbType == DBType.H2) {
query = "SELECT * FROM INFORMATION_SCHEMA.COLUMNS" +
" WHERE TABLE_NAME=? AND COLUMN_NAME=?";
} else {
query = "SELECT * FROM information_schema.COLUMNS" +
" WHERE TABLE_NAME=? AND COLUMN_NAME=? AND TABLE_SCHEMA=?";
}
return query(new QueryStatement<Boolean>(query) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, tableName);
statement.setString(2, columnName);
if (dbType != DBType.H2) {
statement.setString(3, db.getConfig().getString(Settings.DB_DATABASE));
}
}
@Override
public Boolean processResults(ResultSet set) throws SQLException {
return set.next();
}
}) :
query(new QueryAllStatement<Boolean>("PRAGMA table_info(" + tableName + ")") {
@Override
public Boolean processResults(ResultSet set) throws SQLException {
while (set.next()) {
if (columnName.equals(set.getString("name"))) {
return true;
}
@Override
public Boolean processResults(ResultSet set) throws SQLException {
return set.next();
}
});
} else {
return query(new QueryAllStatement<Boolean>("PRAGMA table_info(" + tableName + ")") {
@Override
public Boolean processResults(ResultSet set) throws SQLException {
while (set.next()) {
if (columnName.equals(set.getString("name"))) {
return true;
}
return false;
}
});
return false;
}
});
}
}
protected void addColumn(String tableName, String columnInfo) {
db.executeUnsafe("ALTER TABLE " + tableName + " ADD " + (usingMySQL ? "" : "COLUMN ") + columnInfo);
db.executeUnsafe("ALTER TABLE " + tableName + " ADD " + (dbType.supportsMySQLQueries() ? "" : "COLUMN ") + columnInfo);
}
protected void dropTable(String name) {
@ -104,7 +128,7 @@ public abstract class Patch {
}
protected void renameTable(String from, String to) {
String sql = usingMySQL ?
String sql = dbType.supportsMySQLQueries() ?
"RENAME TABLE " + from + " TO " + to :
"ALTER TABLE " + from + " RENAME TO " + to;
db.execute(sql);

View File

@ -109,7 +109,7 @@ public class Version10Patch extends Patch {
" FROM " + tempNickTableName;
db.execute(statement);
try {
if (usingMySQL) {
if (dbType.supportsMySQLQueries()) {
db.execute("SET foreign_key_checks = 0");
}
statement = "INSERT INTO plan_kills " +
@ -118,7 +118,7 @@ public class Version10Patch extends Patch {
" FROM " + tempKillsTableName;
db.execute(statement);
} finally {
if (usingMySQL) {
if (dbType.supportsMySQLQueries()) {
db.execute("SET foreign_key_checks = 1");
}
}

View File

@ -62,11 +62,11 @@ public class CommandUseTable extends Table {
public void createTable() throws DBInitException {
ServerTable serverTable = db.getServerTable();
createTable(TableSqlParser.createTable(tableName)
.primaryKeyIDColumn(usingMySQL, Col.COMMAND_ID)
.primaryKeyIDColumn(supportsMySQLQueries, Col.COMMAND_ID)
.column(Col.COMMAND, Sql.varchar(20)).notNull()
.column(Col.TIMES_USED, Sql.INT).notNull()
.column(Col.SERVER_ID, Sql.INT).notNull()
.primaryKey(usingMySQL, Col.COMMAND_ID)
.primaryKey(supportsMySQLQueries, Col.COMMAND_ID)
.foreignKey(Col.SERVER_ID, serverTable.toString(), ServerTable.Col.SERVER_ID)
.toString()
);

View File

@ -81,8 +81,6 @@ public class NicknamesTable extends UserIDTable {
/**
* Get nicknames of the user on a server.
* <p>
* Get's nicknames from other servers as well.
*
* @param uuid UUID of the Player
* @param serverUUID UUID of the server
@ -119,8 +117,6 @@ public class NicknamesTable extends UserIDTable {
/**
* Get nicknames of the user on THIS server.
* <p>
* Get's nicknames from other servers as well.
*
* @param uuid UUID of the Player
* @return The nicknames of the User
@ -166,6 +162,42 @@ public class NicknamesTable extends UserIDTable {
});
}
/**
* Get nicknames of all users but doesn't map them by Server
*
* @return a {@code Map<UUID, List<Nickname>} with all nicknames of all users
* @see NicknamesTable#getAllNicknames();
*/
public Map<UUID, List<Nickname>> getAllNicknamesUnmapped() {
String usersIDColumn = usersTable + "." + UsersTable.Col.ID;
String usersUUIDColumn = usersTable + "." + UsersTable.Col.UUID + " as uuid";
String serverIDColumn = serverTable + "." + ServerTable.Col.SERVER_ID;
String serverUUIDColumn = serverTable + "." + ServerTable.Col.SERVER_UUID + " as s_uuid";
String sql = "SELECT " +
Col.NICKNAME + ", " +
Col.LAST_USED + ", " +
usersUUIDColumn + ", " +
serverUUIDColumn +
" FROM " + tableName +
" INNER JOIN " + usersTable + " on " + usersIDColumn + "=" + Col.USER_ID +
" INNER JOIN " + serverTable + " on " + serverIDColumn + "=" + Col.SERVER_ID;
return query(new QueryAllStatement<Map<UUID, List<Nickname>>>(sql, 5000) {
@Override
public Map<UUID, List<Nickname>> processResults(ResultSet set) throws SQLException {
Map<UUID, List<Nickname>> map = new HashMap<>();
while (set.next()) {
UUID uuid = UUID.fromString(set.getString("uuid"));
UUID serverUUID = UUID.fromString(set.getString("s_uuid"));
List<Nickname> nicknames = map.computeIfAbsent(uuid, x -> new ArrayList<>());
nicknames.add(new Nickname(
set.getString(Col.NICKNAME.get()), set.getLong(Col.LAST_USED.get()), serverUUID
));
}
return map;
}
});
}
public void saveUserName(UUID uuid, Nickname name) {
List<Nickname> saved = getNicknameInformation(uuid);
if (saved.contains(name)) {

View File

@ -56,14 +56,14 @@ public class PingTable extends UserIDTable {
@Override
public void createTable() throws DBInitException {
createTable(TableSqlParser.createTable(TABLE_NAME)
.primaryKeyIDColumn(usingMySQL, Col.ID)
.primaryKeyIDColumn(supportsMySQLQueries, Col.ID)
.column(Col.USER_ID, Sql.INT).notNull()
.column(Col.SERVER_ID, Sql.INT).notNull()
.column(Col.DATE, Sql.LONG).notNull()
.column(Col.MAX_PING, Sql.INT).notNull()
.column(Col.MIN_PING, Sql.INT).notNull()
.column(Col.AVG_PING, Sql.DOUBLE).notNull()
.primaryKey(usingMySQL, Col.ID)
.primaryKey(supportsMySQLQueries, Col.ID)
.foreignKey(Col.USER_ID, usersTable.getTableName(), UsersTable.Col.ID)
.foreignKey(Col.SERVER_ID, ServerTable.TABLE_NAME, ServerTable.Col.SERVER_ID)
.toString());

View File

@ -63,13 +63,13 @@ public class ServerTable extends Table {
@Override
public void createTable() throws DBInitException {
createTable(TableSqlParser.createTable(tableName)
.primaryKeyIDColumn(usingMySQL, Col.SERVER_ID)
.primaryKeyIDColumn(supportsMySQLQueries, Col.SERVER_ID)
.column(Col.SERVER_UUID, Sql.varchar(36)).notNull().unique()
.column(Col.NAME, Sql.varchar(100))
.column(Col.WEBSERVER_ADDRESS, Sql.varchar(100))
.column(Col.INSTALLED, Sql.BOOL).notNull().defaultValue(true)
.column(Col.MAX_PLAYERS, Sql.INT).notNull().defaultValue("-1")
.primaryKey(usingMySQL, Col.SERVER_ID)
.primaryKey(supportsMySQLQueries, Col.SERVER_ID)
.toString()
);
}

View File

@ -66,7 +66,7 @@ public class SessionsTable extends UserIDTable {
@Override
public void createTable() throws DBInitException {
createTable(TableSqlParser.createTable(this.tableName)
.primaryKeyIDColumn(usingMySQL, Col.ID)
.primaryKeyIDColumn(supportsMySQLQueries, Col.ID)
.column(Col.USER_ID, Sql.INT).notNull()
.column(Col.SERVER_ID, Sql.INT).notNull()
.column(Col.SESSION_START, Sql.LONG).notNull()
@ -76,7 +76,7 @@ public class SessionsTable extends UserIDTable {
.column(Col.AFK_TIME, Sql.LONG).notNull()
.foreignKey(Col.USER_ID, usersTable.getTableName(), UsersTable.Col.ID)
.foreignKey(Col.SERVER_ID, serverTable.getTableName(), ServerTable.Col.SERVER_ID)
.primaryKey(usingMySQL, Col.ID)
.primaryKey(supportsMySQLQueries, Col.ID)
.toString()
);
}

View File

@ -37,7 +37,7 @@ public abstract class Table {
protected final String tableName;
protected final SQLDB db;
protected final boolean usingMySQL;
protected final boolean supportsMySQLQueries;
/**
* Constructor.
@ -48,7 +48,7 @@ public abstract class Table {
public Table(String name, SQLDB db) {
this.tableName = name;
this.db = db;
this.usingMySQL = db != null && db.isUsingMySQL();
this.supportsMySQLQueries = db != null && db.getType().supportsMySQLQueries();
}
public abstract void createTable() throws DBInitException;
@ -118,13 +118,13 @@ public abstract class Table {
protected void addColumns(String... columnInfo) {
for (int i = 0; i < columnInfo.length; i++) {
columnInfo[i] = "ALTER TABLE " + tableName + " ADD " + (usingMySQL ? "" : "COLUMN ") + columnInfo[i];
columnInfo[i] = "ALTER TABLE " + tableName + " ADD " + (supportsMySQLQueries ? "" : "COLUMN ") + columnInfo[i];
}
executeUnsafe(columnInfo);
}
protected void removeColumns(String... columnNames) {
if (usingMySQL) {
if (supportsMySQLQueries) {
StringBuilder sqlBuild = new StringBuilder();
sqlBuild.append("ALTER TABLE ").append(tableName);
for (int i = 0; i < columnNames.length; i++) {
@ -147,7 +147,7 @@ public abstract class Table {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Table table = (Table) o;
return usingMySQL == table.usingMySQL &&
return supportsMySQLQueries == table.supportsMySQLQueries &&
Objects.equal(tableName, table.tableName) &&
Objects.equal(db, table.db);
}
@ -162,7 +162,7 @@ public abstract class Table {
@Override
public int hashCode() {
return Objects.hashCode(tableName, db, usingMySQL);
return Objects.hashCode(tableName, db, supportsMySQLQueries);
}
protected boolean execute(ExecStatement statement) {

View File

@ -17,6 +17,7 @@
package com.djrapitops.plan.system.database.databases.sql.tables;
import com.djrapitops.plan.api.exceptions.database.DBInitException;
import com.djrapitops.plan.system.database.databases.DBType;
import com.djrapitops.plan.system.database.databases.sql.SQLDB;
import com.djrapitops.plan.system.database.databases.sql.processing.ExecStatement;
import com.djrapitops.plan.system.database.databases.sql.processing.QueryStatement;
@ -51,15 +52,33 @@ public class TransferTable extends Table {
super(TABLE_NAME, db);
serverTable = db.getServerTable();
insertStatementNoParts = "REPLACE INTO " + tableName + " (" +
Col.SENDER_ID + ", " +
Col.EXPIRY + ", " +
Col.INFO_TYPE + ", " +
Col.EXTRA_VARIABLES + ", " +
Col.CONTENT +
") VALUES (" +
serverTable.statementSelectServerID + ", " +
"?, ?, ?, ?)";
if (db.getType() == DBType.H2) {
insertStatementNoParts = "INSERT INTO " + tableName + " (" +
Col.SENDER_ID + ", " +
Col.EXPIRY + ", " +
Col.INFO_TYPE + ", " +
Col.EXTRA_VARIABLES + ", " +
Col.CONTENT +
") VALUES (" +
serverTable.statementSelectServerID + ", " +
"?, ?, ?, ?)" +
" ON DUPLICATE KEY UPDATE" +
" " + Col.EXPIRY + "=?," +
" " + Col.INFO_TYPE + "=?," +
" " + Col.EXTRA_VARIABLES + "=?," +
" " + Col.CONTENT + "=?";
} else {
insertStatementNoParts = "REPLACE INTO " + tableName + " (" +
Col.SENDER_ID + ", " +
Col.EXPIRY + ", " +
Col.INFO_TYPE + ", " +
Col.EXTRA_VARIABLES + ", " +
Col.CONTENT +
") VALUES (" +
serverTable.statementSelectServerID + ", " +
"?, ?, ?, ?)";
}
selectStatement = "SELECT * FROM " + tableName +
" WHERE " + Col.INFO_TYPE + "= ?" +
@ -75,7 +94,7 @@ public class TransferTable extends Table {
.column(Col.EXPIRY, Sql.LONG).notNull().defaultValue("0")
.column(Col.INFO_TYPE, Sql.varchar(100)).notNull()
.column(Col.EXTRA_VARIABLES, Sql.varchar(255)).defaultValue("''")
.column(Col.CONTENT, usingMySQL ? "MEDIUMTEXT" : Sql.varchar(1)) // SQLite does not enforce varchar limits.
.column(Col.CONTENT, supportsMySQLQueries ? "MEDIUMTEXT" : Sql.varchar(1)) // SQLite does not enforce varchar limits.
.column(Col.PART, Sql.LONG).notNull().defaultValue("0")
.foreignKey(Col.SENDER_ID, serverTable.toString(), ServerTable.Col.SERVER_ID)
.toString()
@ -111,11 +130,21 @@ public class TransferTable extends Table {
execute(new ExecStatement(insertStatementNoParts) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
long expiration = System.currentTimeMillis() + TimeUnit.HOURS.toMillis(1L);
statement.setString(1, getServerUUID().toString());
statement.setLong(2, System.currentTimeMillis() + TimeUnit.HOURS.toMillis(1L));
statement.setLong(2, expiration);
statement.setString(3, "configSettings");
statement.setString(4, null);
statement.setString(5, encodedSettingString);
if (db.getType() == DBType.H2) {
statement.setLong(6, expiration);
statement.setString(7, "configSettings");
statement.setString(8, null);
statement.setString(9, encodedSettingString);
}
}
});
}

View File

@ -64,7 +64,7 @@ public class UserInfoTable extends UserIDTable {
public void registerUserInfo(UUID uuid, long registered) {
if (!usersTable.isRegistered(uuid)) {
usersTable.registerUser(uuid, registered, "Waiting for Update..");
usersTable.registerUser(uuid, registered, "waitingForUpdate");
}
String sql = "INSERT INTO " + tableName + " (" +
@ -187,8 +187,7 @@ public class UserInfoTable extends UserIDTable {
return new ArrayList<>();
}
Map<UUID, String> playerNames = usersTable.getPlayerNames();
Map<Integer, UUID> uuidsByID = usersTable.getUUIDsByID();
Map<Integer, Map.Entry<UUID, String>> uuidsAndNamesByID = usersTable.getUUIDsAndNamesByID();
String sql = "SELECT * FROM " + tableName +
" WHERE " + Col.SERVER_ID + "=?";
@ -207,8 +206,11 @@ public class UserInfoTable extends UserIDTable {
boolean op = set.getBoolean(Col.OP.get());
boolean banned = set.getBoolean(Col.BANNED.get());
int userId = set.getInt(Col.USER_ID.get());
UUID uuid = uuidsByID.get(userId);
String name = playerNames.getOrDefault(uuid, "Unknown");
Map.Entry<UUID, String> uuidNameEntry = uuidsAndNamesByID.get(userId);
UUID uuid = uuidNameEntry.getKey();
String name = uuidNameEntry.getValue();
UserInfo info = new UserInfo(uuid, name, registered, op, banned);
if (!userInfo.contains(info)) {
userInfo.add(info);

View File

@ -59,12 +59,12 @@ public class UsersTable extends UserIDTable {
@Override
public void createTable() throws DBInitException {
createTable(TableSqlParser.createTable(tableName)
.primaryKeyIDColumn(usingMySQL, Col.ID)
.primaryKeyIDColumn(supportsMySQLQueries, Col.ID)
.column(Col.UUID, Sql.varchar(36)).notNull().unique()
.column(Col.REGISTERED, Sql.LONG).notNull()
.column(Col.USER_NAME, Sql.varchar(16)).notNull()
.column(Col.TIMES_KICKED, Sql.INT).notNull().defaultValue("0")
.primaryKey(usingMySQL, Col.ID)
.primaryKey(supportsMySQLQueries, Col.ID)
.toString()
);
}
@ -261,13 +261,13 @@ public class UsersTable extends UserIDTable {
* @return a list of distinct names.
*/
public List<String> getMatchingNames(String name) {
String searchString = "%" + name.toLowerCase() + "%";
String searchString = "%" + name + "%";
NicknamesTable nicknamesTable = db.getNicknamesTable();
String sql = "SELECT DISTINCT " + Col.USER_NAME + " FROM " + tableName +
" WHERE " + Col.USER_NAME + " LIKE ?" +
" WHERE LOWER(" + Col.USER_NAME + ") LIKE LOWER(?)" +
" UNION SELECT DISTINCT " + Col.USER_NAME + " FROM " + tableName +
" INNER JOIN " + nicknamesTable + " on " + Col.ID + "=" + nicknamesTable + "." + NicknamesTable.Col.USER_ID +
" WHERE " + NicknamesTable.Col.NICKNAME + " LIKE ?";
" WHERE LOWER(" + NicknamesTable.Col.NICKNAME + ") LIKE LOWER(?)";
return query(new QueryStatement<List<String>>(sql, 5000) {
@Override
@ -416,6 +416,30 @@ public class UsersTable extends UserIDTable {
});
}
/**
* Gets the {@code UUID} and the name of the player mapped to the user ID
*
* @return a {@code Map<Integer, Map.Entry<UUID, String>>} where the key is the user ID
* and the value is an {@code Map.Entry<UUID, String>>} of the player's {@code UUID} and name
*/
public Map<Integer, Map.Entry<UUID, String>> getUUIDsAndNamesByID() {
String sql = Select.from(tableName, Col.ID, Col.UUID, Col.USER_NAME).toString();
return query(new QueryAllStatement<Map<Integer, Map.Entry<UUID, String>>>(sql, 20000) {
@Override
public Map<Integer, Map.Entry<UUID, String>> processResults(ResultSet set) throws SQLException {
Map<Integer, Map.Entry<UUID, String>> uuidsAndNamesByID = new TreeMap<>();
while (set.next()) {
int id = set.getInt(Col.ID.get());
UUID uuid = UUID.fromString(set.getString(Col.UUID.get()));
String name = set.getString(Col.USER_NAME.get());
uuidsAndNamesByID.put(id, new AbstractMap.SimpleEntry<>(uuid, name));
}
return uuidsAndNamesByID;
}
});
}
public DataContainer getUserInformation(UUID uuid) {
Key<DataContainer> user_data = new Key<>(DataContainer.class, "plan_users_data");
DataContainer returnValue = new DataContainer();

View File

@ -57,10 +57,10 @@ public class WorldTable extends Table {
@Override
public void createTable() throws DBInitException {
createTable(TableSqlParser.createTable(tableName)
.primaryKeyIDColumn(usingMySQL, Col.ID)
.primaryKeyIDColumn(supportsMySQLQueries, Col.ID)
.column(Col.NAME, Sql.varchar(100)).notNull()
.column(Col.SERVER_ID, Sql.INT).notNull()
.primaryKey(usingMySQL, Col.ID)
.primaryKey(supportsMySQLQueries, Col.ID)
.foreignKey(Col.SERVER_ID, ServerTable.TABLE_NAME, ServerTable.Col.SERVER_ID)
.toString()
);

View File

@ -37,7 +37,7 @@ public class TransferTable extends Table {
}
protected void renameTable(String from, String to) {
String sql = usingMySQL ?
String sql = supportsMySQLQueries ?
"RENAME TABLE " + from + " TO " + to :
"ALTER TABLE " + from + " RENAME TO " + to;
execute(sql);

View File

@ -84,6 +84,6 @@ public class PingInsertProcessor implements CriticalRunnable {
}
int getMeanValue(List<DateObj<Integer>> history) {
return (int) Median.forInt(history.stream().map(DateObj::getValue).collect(Collectors.toList())).calculate();
return (int) Median.forList(history.stream().map(DateObj::getValue).collect(Collectors.toList())).calculate();
}
}

View File

@ -17,6 +17,7 @@
package com.djrapitops.plan.system.tasks;
import com.djrapitops.plan.Plan;
import com.djrapitops.plan.ShutdownHook;
import com.djrapitops.plan.system.settings.Settings;
import com.djrapitops.plan.system.settings.config.PlanConfig;
import com.djrapitops.plan.system.tasks.server.BootAnalysisTask;
@ -40,12 +41,14 @@ import java.util.concurrent.TimeUnit;
public class BukkitTaskSystem extends ServerTaskSystem {
private final Plan plugin;
private final ShutdownHook shutdownHook;
private final PingCountTimerBukkit pingCountTimer;
@Inject
public BukkitTaskSystem(
Plan plugin,
PlanConfig config,
ShutdownHook shutdownHook,
RunnableFactory runnableFactory,
PaperTPSCountTimer paperTPSCountTimer,
BukkitTPSCountTimer bukkitTPSCountTimer,
@ -64,6 +67,7 @@ public class BukkitTaskSystem extends ServerTaskSystem {
logsFolderCleanTask,
playersPageRefreshTask);
this.plugin = plugin;
this.shutdownHook = shutdownHook;
this.pingCountTimer = pingCountTimer;
}
@ -78,6 +82,7 @@ public class BukkitTaskSystem extends ServerTaskSystem {
} catch (ExceptionInInitializerError | NoClassDefFoundError ignore) {
// Running CraftBukkit
}
shutdownHook.register();
}
@Override

View File

@ -17,6 +17,7 @@
package com.djrapitops.plan.system.tasks;
import com.djrapitops.plan.PlanSponge;
import com.djrapitops.plan.ShutdownHook;
import com.djrapitops.plan.system.settings.Settings;
import com.djrapitops.plan.system.settings.config.PlanConfig;
import com.djrapitops.plan.system.tasks.server.BootAnalysisTask;
@ -34,12 +35,14 @@ import java.util.concurrent.TimeUnit;
public class SpongeTaskSystem extends ServerTaskSystem {
private final PlanSponge plugin;
private final ShutdownHook shutdownHook;
private final PingCountTimerSponge pingCountTimer;
@Inject
public SpongeTaskSystem(
PlanSponge plugin,
PlanConfig config,
ShutdownHook shutdownHook,
RunnableFactory runnableFactory,
SpongeTPSCountTimer spongeTPSCountTimer,
BootAnalysisTask bootAnalysisTask,
@ -57,6 +60,7 @@ public class SpongeTaskSystem extends ServerTaskSystem {
logsFolderCleanTask,
playersPageRefreshTask);
this.plugin = plugin;
this.shutdownHook = shutdownHook;
this.pingCountTimer = pingCountTimer;
}
@ -68,6 +72,8 @@ public class SpongeTaskSystem extends ServerTaskSystem {
long startDelay = TimeAmount.toTicks(config.getNumber(Settings.PING_SERVER_ENABLE_DELAY), TimeUnit.SECONDS);
runnableFactory.create("PingCountTimer", pingCountTimer)
.runTaskTimer(startDelay, PingCountTimerSponge.PING_INTERVAL);
shutdownHook.register();
}
@Override

View File

@ -116,7 +116,7 @@ public class BukkitTPSCountTimer extends TPSCountTimer {
return TPSBuilder.get()
.date(now)
.tps(0)
.tps(tpsN)
.playersOnline(playersOnline)
.usedCPU(cpuUsage)
.usedMemory(usedMemory)

View File

@ -122,7 +122,7 @@ public class PingCountTimerBukkit extends AbsRunnable implements Listener {
private static boolean isPingMethodAvailable() {
try {
//Only available in Paper
Class.forName("org.bukkit.entity.Player.Spigot").getDeclaredMethod("getPing");
Class.forName("org.bukkit.entity.Player$Spigot").getDeclaredMethod("getPing");
return true;
} catch (ClassNotFoundException | NoSuchMethodException noSuchMethodEx) {
return false;

View File

@ -27,6 +27,12 @@ import com.googlecode.htmlcompressor.compressor.HtmlCompressor;
*/
public class PageResponse extends Response {
private static final HtmlCompressor HTML_COMPRESSOR = new HtmlCompressor();
static {
HTML_COMPRESSOR.setRemoveIntertagSpaces(true);
}
public PageResponse(ResponseType type) {
super(type);
}
@ -36,8 +42,6 @@ public class PageResponse extends Response {
@Override
public void setContent(String content) {
HtmlCompressor compressor = new HtmlCompressor();
compressor.setRemoveIntertagSpaces(true);
super.setContent(compressor.compress(content));
super.setContent(HTML_COMPRESSOR.compress(content));
}
}

View File

@ -16,37 +16,34 @@
*/
package com.djrapitops.plan.utilities.analysis;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
/**
* Math utility for calculating the median from Integer values.
*
* @param <T> a {@code Number} object which implements {@code Comparable} (In general every standard Java number)
* @author Rsl1122
*/
public class Median {
public class Median<T extends Number & Comparable<? super T>> {
private final List<Long> values;
private final List<T> values;
private int size;
private Median(Collection<Integer> values, int b) {
this(values.stream().map(i -> (long) i).collect(Collectors.toList()));
}
private Median(List<Long> values) {
private Median(List<T> values) {
this.values = values;
Collections.sort(values);
size = values.size();
}
public static Median forInt(Collection<Integer> integers) {
return new Median(integers, 0);
}
public static Median forLong(List<Long> longs) {
return new Median(longs);
/**
* Creates a Median instance
*
* @param list the input list
* @return an instance of {@code Median} for the List given
*/
public static <T extends Number & Comparable<? super T>> Median<T> forList(List<T> list) {
return new Median<>(list);
}
public double calculate() {
@ -62,13 +59,13 @@ public class Median {
private double calculateEven() {
int half = size / 2;
double x1 = values.get(half);
double x2 = values.get(half - 1);
double x1 = values.get(half).doubleValue();
double x2 = values.get(half - 1).doubleValue();
return (x1 + x2) / 2;
}
private double calculateOdd() {
int half = size / 2;
return (double) values.get(half);
return values.get(half).doubleValue();
}
}

View File

@ -28,14 +28,14 @@ import java.text.DecimalFormat;
*/
public class DecimalFormatter implements Formatter<Double> {
private final PlanConfig config;
private volatile DecimalFormat decimalFormat;
public DecimalFormatter(PlanConfig config) {
this.config = config;
decimalFormat = new DecimalFormat(config.getString(Settings.FORMAT_DECIMALS));
}
@Override
public String apply(Double value) {
return new DecimalFormat(config.getString(Settings.FORMAT_DECIMALS)).format(value);
return decimalFormat.format(value);
}
}
}

View File

@ -227,7 +227,7 @@ public class DebugPage implements Page {
.append(" (").append(serverProperties.getVersion());
content.append(")<br>");
content.append("**Database:** ").append(database.getName());
content.append("**Database:** ").append(database.getType().getName());
content.append("<br><br>");
Properties properties = System.getProperties();

View File

@ -41,7 +41,7 @@ public class BStatsBukkit {
if ("CraftBukkit".equals(serverType) && Check.isSpigotAvailable()) {
serverType = "Spigot";
}
String databaseType = plugin.getSystem().getDatabaseSystem().getDatabase().getName();
String databaseType = plugin.getSystem().getDatabaseSystem().getDatabase().getType().getName();
addStringSettingPie("server_type", serverType);
addStringSettingPie("database_type", databaseType);

View File

@ -46,7 +46,7 @@ public class BStatsBungee {
private void registerConfigSettingGraphs() {
String serverType = plugin.getProxy().getName();
String databaseType = database.getName();
String databaseType = database.getType().getName();
addStringSettingPie("server_type", serverType);
addStringSettingPie("database_type", databaseType);

View File

@ -39,7 +39,7 @@ public class BStatsSponge {
private void registerConfigSettingGraphs() {
String serverType = "Sponge";
String databaseType = database.getName();
String databaseType = database.getType().getName();
addStringSettingPie("server_type", serverType);
addStringSettingPie("database_type", databaseType);

View File

@ -1,7 +1,7 @@
name: Plan
author: Rsl1122
main: com.djrapitops.plan.PlanBungee
version: 4.5.0
version: 4.5.1
softdepend:
- AdvancedBan
- LiteBans

View File

@ -314,22 +314,22 @@ WebServer FAIL - SSL Context || WebServer: SSL Context Initia
WebServer FAIL - Store Load || WebServer: SSL Zertifikat konnte nicht geladen werden.
Yesterday || 'Gestern'
Today || 'Heute'
Health - Active Playtime Comparison Decrease || Aktive Spieler haben möglicherweise nichts mehr zu tun (Gespielt ${0} vs ${1}. Das zeigt der Vergleich der letzten zwei Wochen zu den vorherigen 2 Wochen)
Health - Active Playtime Comparison Increase || Aktive Spieler haben möglicherweise etwas zu tun (Gespielt ${0} vs ${1}. Das zeigt der Vergleich der letzten zwei Wochen zu den vorherigen 2 Wochen)
Health - Active Playtime Comparison Decrease || Aktive Spieler haben möglicherweise nichts mehr zu tun (Vergleich der letzten zwei Wochen: ${0} zu den vorletzten zwei Wochen: ${1}.
Health - Active Playtime Comparison Increase || Aktive Spieler haben möglicherweise mehr zu tun (Vergleich der letzten zwei Wochen: ${0} ; zu den vorletzten zwei Wochen: ${1}.
Health - Downtime || Gesamte Serverdowntime (Keine Daten) war ${0}
Health - New Player Join Players, No || Neue Spieler haben möglicherweise keine anderen Spielern, mit denen sie spielen können. (${0} im Durchschnitt)
Health - New Player Join Players, Yes || Neue Spieler haben andere Spieler, mit denen Spielen können. (${0} im Durchschnitt)
Health - New Player Stickiness || ${0} von den neuen Spieler sind geblieben (${1}/${2})
Health - No Servers Inaccuracy || Es sind keine Bukkit/Sponge-Server verfügbar um Sessiondaten zu sammeln - Diese Messungen sind ungenau.
Health - Player Play on Network || Spieler spieltn im Netzwerk:
Health - Player Play on Network || Spieler spielten im Netzwerk. Auf den Servern sieht's wie folgt aus:
Health - Player Register Server || Spieler wurden auf dem Server pro Tag/Server im Durchschnitt registriert.
Health - Player Visit Server || Spieler haben den Server am Tag/Server im Durchschnitt besucht.
Health - Regular Activity Change || Anzahl an regelmässigen Spielern beträgt
Health - Regular Activity Change || Anzahl an regelmässigen Spielern
Health - Regular Activity Change Decrease || verringert um (${0})
Health - Regular Activity Change Increase || Erhöht um (+${0})
Health - Regular Activity Change Increase || erhöht um (+${0})
Health - Regular Activity Change Zero || Bleibt gleich (+${0})
Health - Regular Activity Remain || ${0} von den regelmässigen Spielern sind aktiv geblieben (${1}/${2})
Health - Single Servers Inaccuracy || Einzelner Bukkit/Sponge um Sessiondaten zu sammelnm.
Health - Single Servers Inaccuracy || Einzelner Bukkit/Sponge um Sessiondaten zu sammeln.
Health - TPS Above Low Threshold || Durchschnittliche TPS war über der unteren Grenze ${0} in der Zeit.
Health - TPS Low Dips || Durchschnittliche TPS war unter der unteren Grenze. (${0}) ${1} male.
HTML - FREE_DISK_SPACE || Freier Festplattenspeicher

View File

@ -1,7 +1,7 @@
name: Plan
author: Rsl1122
main: com.djrapitops.plan.Plan
version: 4.5.0
version: 4.5.1
softdepend:
- EssentialsX
- Towny

View File

@ -1,7 +1,18 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the LGNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* LGNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.data;

View File

@ -0,0 +1,56 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the LGNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* LGNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.system.database.databases;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
import static org.junit.Assert.assertEquals;
/**
* Test for the H2 database
*
* @author Rsl1122, Fuzzlemann
* @see SQLiteTest
* @since 4.5.1
*/
@RunWith(MockitoJUnitRunner.Silent.class)
public class H2Test extends CommonDBTest {
@BeforeClass
public static void setUpClass() throws Exception {
handleSetup("H2");
}
@AfterClass
public static void tearDownClass() {
system.disable();
}
@Test
public void testH2GetConfigName() {
assertEquals("h2", db.getType().getConfigName());
}
@Test
public void testH2GetName() {
assertEquals("H2", db.getType().getName());
}
}

View File

@ -37,7 +37,7 @@ public class PingInsertProcessorTest {
public void medianCalculation() {
List<Integer> collect = testPing.stream().map(DateObj::getValue).sorted().collect(Collectors.toList());
int expected = (int) Median.forInt(collect).calculate();
int expected = (int) Median.forList(collect).calculate();
int result = new PingInsertProcessor(TestConstants.PLAYER_ONE_UUID, TestConstants.SERVER_UUID, new ArrayList<>(), null)
.getMeanValue(testPing);

View File

@ -1,7 +1,18 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the LGNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* LGNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.utilities;

View File

@ -2,6 +2,7 @@ package com.djrapitops.plan.utilities.analysis;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -20,7 +21,7 @@ public class MedianTest {
List<Integer> testValues = Arrays.asList(1, 3, 3, 6, 7, 8, 9);
Collections.shuffle(testValues);
double expected = 6;
double result = Median.forInt(testValues).calculate();
double result = Median.forList(testValues).calculate();
assertEquals(expected, result, 0.01);
}
@ -30,7 +31,7 @@ public class MedianTest {
List<Integer> testValues = Arrays.asList(1, 2, 3, 4, 5, 6, 8, 9);
Collections.shuffle(testValues);
double expected = 4.5;
double result = Median.forInt(testValues).calculate();
double result = Median.forList(testValues).calculate();
assertEquals(expected, result, 0.01);
}
@ -38,7 +39,7 @@ public class MedianTest {
@Test
public void empty() {
double expected = -1;
double result = Median.forInt(Collections.emptyList()).calculate();
double result = Median.forList(new ArrayList<Integer>()).calculate();
assertEquals(expected, result, 0.01);
}
@ -46,7 +47,7 @@ public class MedianTest {
@Test
public void singleValue() {
double expected = 50;
double result = Median.forInt(Collections.singletonList((int) expected)).calculate();
double result = Median.forList(Collections.singletonList((int) expected)).calculate();
assertEquals(expected, result, 0.01);
}
@ -55,7 +56,7 @@ public class MedianTest {
public void twoValues() {
List<Integer> testValues = Arrays.asList(1, 2);
double expected = 1.5;
double result = Median.forInt(testValues).calculate();
double result = Median.forList(testValues).calculate();
assertEquals(expected, result, 0.01);
}
@ -64,7 +65,7 @@ public class MedianTest {
public void overflowOdd() {
List<Integer> testValues = Arrays.asList(Integer.MIN_VALUE, 2, Integer.MAX_VALUE);
double expected = 2;
double result = Median.forInt(testValues).calculate();
double result = Median.forList(testValues).calculate();
assertEquals(expected, result, 0.01);
}
@ -73,7 +74,7 @@ public class MedianTest {
public void overflowEven() {
List<Integer> testValues = Arrays.asList(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
double expected = -0.5;
double result = Median.forInt(testValues).calculate();
double result = Median.forList(testValues).calculate();
assertEquals(expected, result, 0.01);
}
@ -82,7 +83,7 @@ public class MedianTest {
public void overflowEven2() {
List<Integer> testValues = Arrays.asList(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
double expected = Integer.MAX_VALUE;
double result = Median.forInt(testValues).calculate();
double result = Median.forList(testValues).calculate();
assertEquals(expected, result, 0.01);
}

View File

@ -1,7 +1,18 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the LGNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* LGNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.utilities.html;

View File

@ -1,7 +1,18 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the LGNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* LGNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.utilities.html;

View File

@ -14,7 +14,7 @@ import com.djrapitops.plugin.logging.debug.DebugLogger;
import com.djrapitops.plugin.logging.debug.MemoryDebugLogger;
import com.djrapitops.plugin.logging.error.ConsoleErrorLogger;
import com.djrapitops.plugin.logging.error.ErrorHandler;
import com.djrapitops.plugin.task.thread.ThreadRunnableFactory;
import com.djrapitops.plugin.task.RunnableFactory;
import org.bukkit.Server;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.plugin.InvalidDescriptionException;
@ -23,6 +23,7 @@ import org.bukkit.scheduler.BukkitScheduler;
import org.mockito.Mockito;
import utilities.TestConstants;
import utilities.mocks.objects.TestLogger;
import utilities.mocks.objects.TestRunnableFactory;
import java.io.File;
import java.io.FileInputStream;
@ -54,7 +55,7 @@ public class PlanBukkitMocker extends Mocker {
doReturn("1.0.0").when(planMock).getVersion();
TestLogger testLogger = new TestLogger();
ThreadRunnableFactory runnableFactory = new ThreadRunnableFactory();
RunnableFactory runnableFactory = new TestRunnableFactory();
PluginLogger testPluginLogger = new TestPluginLogger();
DebugLogger debugLogger = new CombineDebugLogger(new MemoryDebugLogger());
ErrorHandler consoleErrorLogger = new ConsoleErrorLogger(testPluginLogger);

View File

@ -0,0 +1,113 @@
/*
* License is provided in the jar as LICENSE also here:
* https://github.com/Rsl1122/Plan-PlayerAnalytics/blob/master/Plan/src/main/resources/LICENSE
*/
package utilities.mocks.objects;
import com.djrapitops.plugin.api.TimeAmount;
import com.djrapitops.plugin.task.AbsRunnable;
import com.djrapitops.plugin.task.PluginRunnable;
import com.djrapitops.plugin.task.PluginTask;
import com.djrapitops.plugin.task.RunnableFactory;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* @author Fuzzlemann
* @since 4.5.1
*/
public class TestRunnableFactory extends RunnableFactory {
private final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
@Override
protected PluginRunnable createNewRunnable(String name, AbsRunnable absRunnable, long l) {
return new PluginRunnable() {
@Override
public String getTaskName() {
return name;
}
@Override
public void cancel() {
absRunnable.cancel();
}
@Override
public int getTaskId() {
return absRunnable.getTaskId();
}
@Override
public PluginTask runTask() {
absRunnable.run();
return createPluginTask(getTaskId(), true, absRunnable::cancel);
}
@Override
public PluginTask runTaskAsynchronously() {
executorService.submit(absRunnable);
return createPluginTask(getTaskId(), false, absRunnable::cancel);
}
@Override
public PluginTask runTaskLater(long l) {
return runTaskLaterAsynchronously(l);
}
@Override
public PluginTask runTaskLaterAsynchronously(long l) {
executorService.schedule(absRunnable, TimeAmount.ticksToMillis(l), TimeUnit.MILLISECONDS);
return createPluginTask(getTaskId(), false, absRunnable::cancel);
}
@Override
public PluginTask runTaskTimer(long l, long l1) {
return runTaskLaterAsynchronously(l);
}
@Override
public PluginTask runTaskTimerAsynchronously(long l, long l1) {
executorService.scheduleAtFixedRate(absRunnable, TimeAmount.ticksToMillis(l), TimeAmount.ticksToMillis(l1), TimeUnit.MILLISECONDS);
return createPluginTask(getTaskId(), false, absRunnable::cancel);
}
@Override
public long getTime() {
return l;
}
};
}
@Override
public void cancelAllKnownTasks() {
executorService.shutdownNow();
}
private PluginTask createPluginTask(int taskID, boolean sync, ICloseTask closeTask) {
return new PluginTask() {
@Override
public int getTaskId() {
return taskID;
}
@Override
public boolean isSync() {
return sync;
}
@Override
public void cancel() {
if (closeTask != null) {
closeTask.close();
}
}
};
}
private interface ICloseTask {
void close();
}
}

View File

@ -33,7 +33,7 @@ import java.util.UUID;
* @author Rsl1122
* @since 4.1.0
*/
class PlayerHackKickListener implements Listener {
public class PlayerHackKickListener implements Listener {
private final HackerTable hackerTable;
private final Processing processing;

View File

@ -52,11 +52,11 @@ public class ReactDataTable extends Table {
@Override
public void createTable() throws DBInitException {
createTable(TableSqlParser.createTable(TABLE_NAME)
.primaryKey(usingMySQL, Col.ID)
.primaryKey(supportsMySQLQueries, Col.ID)
.column(Col.DATE, Sql.LONG)
.column(Col.SAMPLED_TYPE, Sql.varchar(30))
.column(Col.MINUTE_AVERAGE, Sql.DOUBLE)
.primaryKeyIDColumn(usingMySQL, Col.ID)
.primaryKeyIDColumn(supportsMySQLQueries, Col.ID)
.toString());
}

View File

@ -31,7 +31,7 @@ import java.util.UUID;
* @author Rsl1122
* @since 3.5.0
*/
class BukkitPlayerVersionListener implements Listener {
public class BukkitPlayerVersionListener implements Listener {
private final ViaAPI viaAPI;

View File

@ -30,7 +30,7 @@ import java.util.UUID;
* @author Rsl1122
* @since 3.5.0
*/
class BungeePlayerVersionListener implements Listener {
public class BungeePlayerVersionListener implements Listener {
private final ViaAPI viaAPI;

View File

@ -27,3 +27,4 @@ Documentation can be found [On the Wiki](https://github.com/Rsl1122/Plan-PlayerA
- **[jQuery Datatables](https://datatables.net/)** | [MIT License](https://datatables.net/license/mit)
- **[Font Awesome Icons](http://fontawesome.io/icons/)** | [SIL Open Font License](http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL)
- **[MaxMind GeoIP2](https://www.maxmind.com/en/geoip-demo)** | [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/)
- **[H2 Database](http://www.h2database.com)** | [MPL 2.0](http://www.h2database.com/html/license.html#mpl2) or [EPL 1.0](http://www.h2database.com/html/license.html#eclipse_license)