diff --git a/Plan/api/build.gradle b/Plan/api/build.gradle
index bd3e76ee4..465a78ecd 100644
--- a/Plan/api/build.gradle
+++ b/Plan/api/build.gradle
@@ -1,8 +1,8 @@
plugins {
- id "com.jfrog.bintray" version "1.8.1"
+ id "com.jfrog.bintray" version "1.8.4"
}
-ext.apiVersion = '0.0.3'
+ext.apiVersion = '0.0.4'
bintray {
user = System.getenv('BINTRAY_USER')
diff --git a/Plan/api/src/main/java/com/djrapitops/plan/capability/Capability.java b/Plan/api/src/main/java/com/djrapitops/plan/capability/Capability.java
new file mode 100644
index 000000000..0ea9a689f
--- /dev/null
+++ b/Plan/api/src/main/java/com/djrapitops/plan/capability/Capability.java
@@ -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 GNU 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
+ * GNU 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 .
+ */
+package com.djrapitops.plan.capability;
+
+import java.util.Optional;
+
+/**
+ * List of different capabilities current version provides.
+ *
+ * The enum is package private to restrict direct access. This is to avoid NoClassDefFoundError in case
+ * a wanted Capability is not provided in an earlier version.
+ *
+ * Use {@link CapabilityService#hasCapability(String)} with name of a Capability to figure out if an API is available.
+ * Example usage: {@code CapabilityService.getInstance().hasCapability("DATA_EXTENSION_VALUES")}.
+ *
+ * If a capability is not available, attempting to use the capability might lead to exceptions.
+ *
+ * @author Rsl1122
+ */
+enum Capability {
+
+ /**
+ * ExtensionService, DataExtension API base package, PluginInfo, Conditional, Tab, TabInfo, TabOrder and BooleanProvider, DoubleProvider, PercentageProvider, NumberProvider, StringProvider annotations.
+ */
+ DATA_EXTENSION_VALUES,
+ /**
+ * DataExtension API table package, TableProvider, Table and Table.Factory
+ */
+ DATA_EXTENSION_TABLES;
+
+ static Optional getByName(String name) {
+ if (name == null) {
+ return Optional.empty();
+ }
+ try {
+ return Optional.of(valueOf(name));
+ } catch (IllegalArgumentException e) {
+ return Optional.empty();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/Plan/api/src/main/java/com/djrapitops/plan/capability/CapabilityService.java b/Plan/api/src/main/java/com/djrapitops/plan/capability/CapabilityService.java
new file mode 100644
index 000000000..cd5ed969f
--- /dev/null
+++ b/Plan/api/src/main/java/com/djrapitops/plan/capability/CapabilityService.java
@@ -0,0 +1,76 @@
+/*
+ * 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 GNU 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
+ * GNU 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 .
+ */
+package com.djrapitops.plan.capability;
+
+import java.util.Optional;
+import java.util.function.Consumer;
+
+/**
+ * Service for figuring out provided API capabilities.
+ *
+ * {@link CapabilityService#registerEnableListener(Consumer)} to be notified of Plan reloads
+ * {@link CapabilityService#hasCapability(String)} to check if a capability is available.
+ *
+ * See {@link Capability} for list of capabilities provided by the current version.
+ *
+ * @author Rsl1122
+ */
+public interface CapabilityService {
+
+ /**
+ * Obtain instance of CapabilityService.
+ *
+ * @return CapabilityService implementation.
+ * @throws NoClassDefFoundError If Plan is not installed and this class can not be found or if older Plan version is installed.
+ * @throws IllegalStateException If Plan is installed, but not enabled.
+ */
+ static CapabilityService getInstance() {
+ return Optional.ofNullable(CapabilityServiceHolder.service)
+ .orElseThrow(() -> new IllegalStateException("CapabilityService has not been initialised yet."));
+ }
+
+ /**
+ * Register a method to be called when Plan reloads.
+ *
+ * @param isEnabledListener The boolean given to the method tells if Plan has enabled successfully.
+ */
+ void registerEnableListener(Consumer isEnabledListener);
+
+ /**
+ * Check if the API on the current version provides a capability.
+ *
+ * @param capabilityName Name of a capability
+ * @return true if the capability is available.
+ * @see Capability for different capabilityNames.
+ */
+ default boolean hasCapability(String capabilityName) {
+ return Capability.getByName(capabilityName).isPresent();
+ }
+
+ class CapabilityServiceHolder {
+ static CapabilityService service;
+
+ private CapabilityServiceHolder() {
+ /* Static variable holder */
+ }
+
+ static void set(CapabilityService service) {
+ CapabilityServiceHolder.service = service;
+ }
+ }
+
+}
diff --git a/Plan/api/src/main/java/com/djrapitops/plan/extension/ElementOrder.java b/Plan/api/src/main/java/com/djrapitops/plan/extension/ElementOrder.java
index 8e489a6d5..f4d3fd634 100644
--- a/Plan/api/src/main/java/com/djrapitops/plan/extension/ElementOrder.java
+++ b/Plan/api/src/main/java/com/djrapitops/plan/extension/ElementOrder.java
@@ -32,14 +32,14 @@ public enum ElementOrder {
* Represents text - value pair box.
*/
VALUES,
- /**
- * Represents tables.
- */
- TABLE,
/**
* Represents graphs.
*/
- GRAPH;
+ GRAPH,
+ /**
+ * Represents tables.
+ */
+ TABLE;
public static String serialize(ElementOrder[] order) {
StringBuilder builder = new StringBuilder();
diff --git a/Plan/api/src/main/java/com/djrapitops/plan/extension/FormatType.java b/Plan/api/src/main/java/com/djrapitops/plan/extension/FormatType.java
index 8527a136f..ca59cfda9 100644
--- a/Plan/api/src/main/java/com/djrapitops/plan/extension/FormatType.java
+++ b/Plan/api/src/main/java/com/djrapitops/plan/extension/FormatType.java
@@ -43,6 +43,9 @@ public enum FormatType {
NONE;
public static Optional getByName(String name) {
+ if (name == null) {
+ return Optional.empty();
+ }
try {
return Optional.of(valueOf(name));
} catch (IllegalArgumentException e) {
diff --git a/Plan/api/src/main/java/com/djrapitops/plan/extension/annotation/TableProvider.java b/Plan/api/src/main/java/com/djrapitops/plan/extension/annotation/TableProvider.java
new file mode 100644
index 000000000..1a8f69936
--- /dev/null
+++ b/Plan/api/src/main/java/com/djrapitops/plan/extension/annotation/TableProvider.java
@@ -0,0 +1,49 @@
+/*
+ * 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 GNU 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
+ * GNU 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 .
+ */
+package com.djrapitops.plan.extension.annotation;
+
+import com.djrapitops.plan.extension.icon.Color;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Method annotation to provide a Table.
+ *
+ * Usage: {@code @TableProvider Table method(UUID playerUUID)}
+ *
+ * Tables about players can have up to 4 columns.
+ * Tables about server can have up to 5 columns.
+ *
+ * It is recommended to place each table on their own tab with a {@link Tab} annotation on the same method.
+ *
+ * @author Rsl1122
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface TableProvider {
+
+ /**
+ * Determine the color of the table header.
+ *
+ * @return Preferred color. If none are specified defaults are used.
+ */
+ Color tableColor() default Color.NONE;
+
+}
diff --git a/Plan/api/src/main/java/com/djrapitops/plan/extension/extractor/ExtensionExtractor.java b/Plan/api/src/main/java/com/djrapitops/plan/extension/extractor/ExtensionExtractor.java
index 03f372de0..81f164a74 100644
--- a/Plan/api/src/main/java/com/djrapitops/plan/extension/extractor/ExtensionExtractor.java
+++ b/Plan/api/src/main/java/com/djrapitops/plan/extension/extractor/ExtensionExtractor.java
@@ -19,6 +19,7 @@ package com.djrapitops.plan.extension.extractor;
import com.djrapitops.plan.extension.DataExtension;
import com.djrapitops.plan.extension.Group;
import com.djrapitops.plan.extension.annotation.*;
+import com.djrapitops.plan.extension.table.Table;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
@@ -103,6 +104,8 @@ public final class ExtensionExtractor {
MethodAnnotations.get(method, Conditional.class).ifPresent(annotation -> methodAnnotations.put(method, Conditional.class, annotation));
MethodAnnotations.get(method, Tab.class).ifPresent(annotation -> methodAnnotations.put(method, Tab.class, annotation));
+
+ MethodAnnotations.get(method, TableProvider.class).ifPresent(annotation -> methodAnnotations.put(method, TableProvider.class, annotation));
}
if (methodAnnotations.isEmpty()) {
@@ -166,6 +169,7 @@ public final class ExtensionExtractor {
validateDoubleProviderAnnotations();
validatePercentageProviderAnnotations();
validateStringProviderAnnotations();
+ validateTableProviderAnnotations();
}
private void validateBooleanProviderAnnotations() {
@@ -238,6 +242,13 @@ public final class ExtensionExtractor {
}
}
+ private void validateTableProviderAnnotations() {
+ for (Method method : methodAnnotations.getMethodAnnotations(TableProvider.class).keySet()) {
+ validateReturnType(method, Table.class);
+ validateMethodArguments(method, false, UUID.class, String.class, Group.class);
+ }
+ }
+
private void validateConditionals() {
Collection conditionals = methodAnnotations.getAnnotations(Conditional.class);
Collection conditionProviders = methodAnnotations.getAnnotations(BooleanProvider.class);
diff --git a/Plan/api/src/main/java/com/djrapitops/plan/extension/icon/Color.java b/Plan/api/src/main/java/com/djrapitops/plan/extension/icon/Color.java
index 16783c61b..58f9296fb 100644
--- a/Plan/api/src/main/java/com/djrapitops/plan/extension/icon/Color.java
+++ b/Plan/api/src/main/java/com/djrapitops/plan/extension/icon/Color.java
@@ -48,6 +48,9 @@ public enum Color {
NONE;
public static Optional getByName(String name) {
+ if (name == null) {
+ return Optional.empty();
+ }
try {
return Optional.of(valueOf(name));
} catch (IllegalArgumentException e) {
diff --git a/Plan/api/src/main/java/com/djrapitops/plan/extension/icon/Family.java b/Plan/api/src/main/java/com/djrapitops/plan/extension/icon/Family.java
index 204ec2660..0125d6ee9 100644
--- a/Plan/api/src/main/java/com/djrapitops/plan/extension/icon/Family.java
+++ b/Plan/api/src/main/java/com/djrapitops/plan/extension/icon/Family.java
@@ -29,6 +29,9 @@ public enum Family {
BRAND;
public static Optional getByName(String name) {
+ if (name == null) {
+ return Optional.empty();
+ }
try {
return Optional.of(valueOf(name));
} catch (IllegalArgumentException e) {
diff --git a/Plan/api/src/main/java/com/djrapitops/plan/extension/icon/Icon.java b/Plan/api/src/main/java/com/djrapitops/plan/extension/icon/Icon.java
index 277510d6e..8fc722d13 100644
--- a/Plan/api/src/main/java/com/djrapitops/plan/extension/icon/Icon.java
+++ b/Plan/api/src/main/java/com/djrapitops/plan/extension/icon/Icon.java
@@ -64,8 +64,9 @@ public class Icon {
return color;
}
- public void setColor(Color color) {
+ public Icon setColor(Color color) {
this.color = color;
+ return this;
}
@Override
diff --git a/Plan/api/src/main/java/com/djrapitops/plan/extension/table/Table.java b/Plan/api/src/main/java/com/djrapitops/plan/extension/table/Table.java
new file mode 100644
index 000000000..b082fcf1c
--- /dev/null
+++ b/Plan/api/src/main/java/com/djrapitops/plan/extension/table/Table.java
@@ -0,0 +1,213 @@
+/*
+ * 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 GNU 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
+ * GNU 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 .
+ */
+package com.djrapitops.plan.extension.table;
+
+import com.djrapitops.plan.extension.ElementOrder;
+import com.djrapitops.plan.extension.icon.Color;
+import com.djrapitops.plan.extension.icon.Icon;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Object for giving Plan table data.
+ *
+ * Usage: {@code Table.builder().columnOne("columnName", new Icon(...)).addRow("Your", "Row", "Data").build()}
+ *
+ * Tables about players can have up to 4 columns.
+ * Tables about server can have up to 5 columns.
+ *
+ * Icon colors are ignored.
+ *
+ * If a row has more values than the column limit, they are ignored.
+ * If a row has less values than table columns, a '-' is displayed to distinguish a missing value.
+ *
+ * If a table has no columns or rows, it is ignored.
+ *
+ * @author Rsl1122
+ * @see com.djrapitops.plan.extension.annotation.TableProvider for associated annotation.
+ */
+public final class Table {
+
+ private final String[] columns;
+ private final Icon[] icons;
+
+ private final List rows;
+
+ private Table() {
+ /* Tables are constructed with the factory. */
+
+ columns = new String[5];
+ icons = new Icon[5];
+ rows = new ArrayList<>();
+ }
+
+ /**
+ * Create a new Table Factory.
+ *
+ * @return a new Table Factory.
+ */
+ public static Table.Factory builder() {
+ return new Table.Factory();
+ }
+
+ public String[] getColumns() {
+ return columns;
+ }
+
+ public int getMaxColumnSize() {
+ int columnCount = 0;
+ for (String column : columns) {
+ if (column == null) {
+ break; // Prevent having one null column between two columns
+ }
+ columnCount++;
+ }
+ return columnCount;
+ }
+
+ public Icon[] getIcons() {
+ return icons;
+ }
+
+ public List getRows() {
+ return rows;
+ }
+
+ /**
+ * Factory for creating new {@link Table} objects.
+ */
+ public static final class Factory {
+
+ private final Table building;
+
+ // These variable are related to implementation, and is used when the table is being fetched from the database.
+ Color color; // Table color is defined with TableProvider annotation.
+ String tableName; // tableName is defined by method name annotated by TableProvider.
+ String tabName; // Tab name is defined with Tab annotation
+ int tabPriority; // Tab priority is defined with TabOrder annotation
+ ElementOrder[] tabOrder; // Tab element order is defined with TabInfo annotation
+ Icon tabIcon; // Tab icon is defined with TabInfo annotation
+
+ private Factory() {
+ building = new Table();
+ }
+
+ private Factory column(int indx, String columnName, Icon icon) {
+ building.columns[indx] = columnName;
+ building.icons[indx] = icon != null ? Icon.called(icon.getName()).of(icon.getFamily()).build() : Icon.called("question").build();
+ return this;
+ }
+
+ /**
+ * Set first column name and icon.
+ *
+ * @param columnName Name of the column.
+ * @param icon Icon of the column, color is ignored.
+ * @return Factory.
+ */
+ public Factory columnOne(String columnName, Icon icon) {
+ return column(0, columnName, icon);
+ }
+
+ /**
+ * Set second column name and icon.
+ *
+ * @param columnName Name of the column.
+ * @param icon Icon of the column, color is ignored.
+ * @return Factory.
+ */
+ public Factory columnTwo(String columnName, Icon icon) {
+ return column(1, columnName, icon);
+ }
+
+ /**
+ * Set third column name and icon.
+ *
+ * @param columnName Name of the column.
+ * @param icon Icon of the column, color is ignored.
+ * @return Factory.
+ */
+ public Factory columnThree(String columnName, Icon icon) {
+ return column(2, columnName, icon);
+ }
+
+ /**
+ * Set fourth column name and icon.
+ *
+ * @param columnName Name of the column.
+ * @param icon Icon of the column, color is ignored.
+ * @return Factory.
+ */
+ public Factory columnFour(String columnName, Icon icon) {
+ return column(3, columnName, icon);
+ }
+
+ /**
+ * Set fifth column name and icon.
+ *
+ * @param columnName Name of the column.
+ * @param icon Icon of the column, color is ignored.
+ * @return Factory.
+ */
+ public Factory columnFive(String columnName, Icon icon) {
+ return column(4, columnName, icon);
+ }
+
+ /**
+ * Add a row of values to the table.
+ *
+ * @param values One value per column you have defined, {@code Object#toString()} will be called on the objects.
+ * @return Factory.
+ * @throws IllegalArgumentException If given varargs for 'values' is null.
+ */
+ public Factory addRow(Object... values) {
+ if (values == null) {
+ throw new IllegalArgumentException("'values' for Table#addRow can not be null!");
+ }
+
+ if (values.length == 0 || areAllValuesNull(values)) {
+ return this; // Ignore row when all values are null or no values are present.
+ }
+
+ building.rows.add(Arrays.copyOf(values, 5));
+ return this;
+ }
+
+ private boolean areAllValuesNull(Object[] values) {
+ boolean allNull = true;
+ for (Object value : values) {
+ if (value != null) {
+ allNull = false;
+ break;
+ }
+ }
+ return allNull;
+ }
+
+ /**
+ * Finish building the table.
+ *
+ * @return Finished Table object.
+ */
+ public Table build() {
+ return building;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/Plan/api/src/test/java/com/djrapitops/plan/extension/extractor/ExtensionExtractorTest.java b/Plan/api/src/test/java/com/djrapitops/plan/extension/extractor/ExtensionExtractorTest.java
index 44d45d382..c91bb62f8 100644
--- a/Plan/api/src/test/java/com/djrapitops/plan/extension/extractor/ExtensionExtractorTest.java
+++ b/Plan/api/src/test/java/com/djrapitops/plan/extension/extractor/ExtensionExtractorTest.java
@@ -166,6 +166,20 @@ class ExtensionExtractorTest {
assertEquals("Extension.method has invalid return type. was: java.lang.Double, expected: java.lang.String", assertThrows(IllegalArgumentException.class, underTest::validateAnnotations).getMessage());
}
+ @Test
+ void tableProviderMustReturnTable() {
+ @PluginInfo(name = "Extension")
+ class Extension implements DataExtension {
+ @TableProvider
+ public Double method(UUID playerUUID) {
+ return null;
+ }
+ }
+
+ ExtensionExtractor underTest = new ExtensionExtractor(new Extension());
+ assertEquals("Extension.method has invalid return type. was: java.lang.Double, expected: com.djrapitops.plan.extension.table.Table", assertThrows(IllegalArgumentException.class, underTest::validateAnnotations).getMessage());
+ }
+
@Test
void booleanProviderCanNotSupplyItsOwnConditional() {
@PluginInfo(name = "Extension")
diff --git a/Plan/build.gradle b/Plan/build.gradle
index 2336e7789..7de08b4d6 100644
--- a/Plan/build.gradle
+++ b/Plan/build.gradle
@@ -2,9 +2,9 @@ plugins {
id "java"
id "jacoco"
id "checkstyle"
- id "org.sonarqube" version "2.6.2"
- id "net.ltgt.apt" version "0.19"
- id "net.ltgt.apt-idea" version "0.19"
+ id "org.sonarqube" version "2.7"
+ id "net.ltgt.apt" version "0.21"
+ id "net.ltgt.apt-idea" version "0.21"
id "com.github.johnrengelman.shadow" version "5.0.0"
}
@@ -12,7 +12,7 @@ allprojects {
wrapper.gradleVersion = "5.0"
group "com.djrapitops"
- version "4.8.1"
+ version "4.8.2"
test {
useJUnitPlatform()
@@ -43,11 +43,11 @@ subprojects {
sourceCompatibility = 1.8
targetCompatibility = 1.8
- ext.daggerVersion = "2.21"
- ext.daggerCompilerVersion = "2.21"
+ ext.daggerVersion = "2.22.1"
+ ext.daggerCompilerVersion = "2.22.1"
ext.abstractPluginFrameworkVersion = "3.4.1"
- ext.planPluginBridgeVersion = "4.7.0"
+ ext.planPluginBridgeVersion = "4.8.2-R0.3"
ext.bukkitVersion = "1.12.2-R0.1-SNAPSHOT"
ext.spigotVersion = "1.12.2-R0.1-SNAPSHOT"
@@ -61,12 +61,12 @@ subprojects {
ext.commonsTextVersion = "1.6"
ext.htmlCompressorVersion = "1.5.2"
ext.caffeineVersion = "2.7.0"
- ext.h2Version = "1.4.196"
+ ext.h2Version = "1.4.199"
ext.hikariVersion = "3.3.1"
ext.slf4jVersion = "1.7.26"
ext.geoIpVersion = "2.12.0"
ext.guavaVersion = "26.0-jre"
- ext.bstatsVersion = "1.2"
+ ext.bstatsVersion = "1.4"
repositories {
mavenCentral()
@@ -89,7 +89,7 @@ subprojects {
url = "https://repo.velocitypowered.com/snapshots/"
}
maven { // bStats Repository
- url = "http://repo.bstats.org/content/repositories/releases/"
+ url = "https://repo.codemc.org/repository/maven-public"
}
maven { // PlanPluginBridge Repository
url = "https://dl.bintray.com/rsl1122/Plan-repository"
@@ -103,18 +103,18 @@ subprojects {
testAnnotationProcessor "com.google.dagger:dagger-compiler:$daggerCompilerVersion"
// Test Tooling Dependencies
- testCompile "org.junit.jupiter:junit-jupiter-engine:5.4.1" // JUnit 5
- testCompile "org.junit.platform:junit-platform-runner:1.4.1" // JUnit 4 runner for JUnit 5 tests
- testCompile "org.junit.vintage:junit-vintage-engine:5.4.1" // JUnit 4 compatibility for JUnit 5
- testCompile "org.junit.jupiter:junit-jupiter-params:5.4.1" // JUnit 5, parameterized tests
- testCompile "org.mockito:mockito-core:2.25.1" // Mockito Core
- testCompile "org.mockito:mockito-junit-jupiter:2.25.1" // Mockito JUnit 5 Extension
+ testCompile "org.junit.jupiter:junit-jupiter-engine:5.4.2" // JUnit 5
+ testCompile "org.junit.platform:junit-platform-runner:1.4.2" // JUnit 4 runner for JUnit 5 tests
+ testCompile "org.junit.vintage:junit-vintage-engine:5.4.2" // JUnit 4 compatibility for JUnit 5
+ testCompile "org.junit.jupiter:junit-jupiter-params:5.4.2" // JUnit 5, parameterized tests
+ testCompile "org.mockito:mockito-core:2.27.0" // Mockito Core
+ testCompile "org.mockito:mockito-junit-jupiter:2.27.0" // Mockito JUnit 5 Extension
testCompile "org.seleniumhq.selenium:selenium-java:3.141.59" // Selenium (Browser tests)
testCompile "com.jayway.awaitility:awaitility:1.7.0" // Awaitility (Concurrent wait conditions)
// Testing dependencies required by Plan
testCompile "org.xerial:sqlite-jdbc:3.27.2.1" // SQLite
- testCompile "mysql:mysql-connector-java:8.0.15" // MySQL
+ testCompile "mysql:mysql-connector-java:8.0.16" // MySQL
}
configurations {
diff --git a/Plan/bukkit/src/main/java/com/djrapitops/plan/Plan.java b/Plan/bukkit/src/main/java/com/djrapitops/plan/Plan.java
index 2ae0e2506..e09fa21da 100644
--- a/Plan/bukkit/src/main/java/com/djrapitops/plan/Plan.java
+++ b/Plan/bukkit/src/main/java/com/djrapitops/plan/Plan.java
@@ -26,6 +26,7 @@ import com.djrapitops.plan.system.settings.theme.PlanColorScheme;
import com.djrapitops.plugin.BukkitPlugin;
import com.djrapitops.plugin.benchmarking.Benchmark;
import com.djrapitops.plugin.command.ColorScheme;
+import com.djrapitops.plugin.task.AbsRunnable;
import org.bukkit.configuration.file.FileConfiguration;
import java.util.logging.Level;
@@ -52,7 +53,7 @@ public class Plan extends BukkitPlugin implements PlanPlugin {
locale = system.getLocaleSystem().getLocale();
system.enable();
- new BStatsBukkit(this).registerMetrics();
+ registerMetrics();
logger.debug("Verbose debug messages are enabled.");
String benchTime = " (" + timings.end("Enable").map(Benchmark::toDurationString).orElse("-") + ")";
@@ -80,6 +81,19 @@ public class Plan extends BukkitPlugin implements PlanPlugin {
}
}
+ private void registerMetrics() {
+ Plan plugin = this;
+ // Spigot 1.14 requires Sync events to be fired from a server thread.
+ // Registering a service fires a sync event, and bStats registers a service,
+ // so this has to be run on the server thread.
+ runnableFactory.create("Register Metrics task", new AbsRunnable() {
+ @Override
+ public void run() {
+ new BStatsBukkit(plugin).registerMetrics();
+ }
+ }).runTask();
+ }
+
@Override
public ColorScheme getColorScheme() {
return PlanColorScheme.create(system.getConfigSystem().getConfig(), logger);
diff --git a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/importing/importers/BukkitImporter.java b/Plan/bukkit/src/main/java/com/djrapitops/plan/system/importing/importers/BukkitImporter.java
index bdfded339..f387b3a80 100644
--- a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/importing/importers/BukkitImporter.java
+++ b/Plan/bukkit/src/main/java/com/djrapitops/plan/system/importing/importers/BukkitImporter.java
@@ -33,10 +33,8 @@ import com.djrapitops.plan.system.importing.data.BukkitUserImportRefiner;
import com.djrapitops.plan.system.importing.data.ServerImportData;
import com.djrapitops.plan.system.importing.data.UserImportData;
import com.djrapitops.plan.system.info.server.ServerInfo;
-import com.djrapitops.plan.utilities.SHA256Hash;
import com.djrapitops.plugin.utilities.Verify;
-import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -177,11 +175,11 @@ public abstract class BukkitImporter implements Importer {
}
private BaseUser toBaseUser(UserImportData userImportData) {
- UUID uuid = userImportData.getUuid();
- String name = userImportData.getName();
+ UUID playerUUID = userImportData.getUuid();
+ String playerName = userImportData.getName();
long registered = userImportData.getRegistered();
int timesKicked = userImportData.getTimesKicked();
- return new BaseUser(uuid, name, registered, timesKicked);
+ return new BaseUser(playerUUID, playerName, registered, timesKicked);
}
private UserInfo toUserInfo(UserImportData userImportData) {
@@ -211,11 +209,7 @@ public abstract class BukkitImporter implements Importer {
return userImportData.getIps().parallelStream()
.map(ip -> {
String geoLoc = geolocationCache.getCountry(ip);
- try {
- return new GeoInfo(ip, geoLoc, date, new SHA256Hash(ip).create());
- } catch (NoSuchAlgorithmException e) {
- throw new IllegalArgumentException(e);
- }
+ return new GeoInfo(ip, geoLoc, date);
}).collect(Collectors.toList());
}
}
diff --git a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/info/server/properties/BukkitServerProperties.java b/Plan/bukkit/src/main/java/com/djrapitops/plan/system/info/server/properties/BukkitServerProperties.java
index ef84da41f..09bb86b44 100644
--- a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/info/server/properties/BukkitServerProperties.java
+++ b/Plan/bukkit/src/main/java/com/djrapitops/plan/system/info/server/properties/BukkitServerProperties.java
@@ -27,7 +27,6 @@ public class BukkitServerProperties extends ServerProperties {
public BukkitServerProperties(Server server) {
super(
- server.getServerId(),
server.getName(),
server.getPort(),
server.getVersion(),
diff --git a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/listeners/BukkitListenerSystem.java b/Plan/bukkit/src/main/java/com/djrapitops/plan/system/listeners/BukkitListenerSystem.java
index 693dfc3ba..6a00fe207 100644
--- a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/listeners/BukkitListenerSystem.java
+++ b/Plan/bukkit/src/main/java/com/djrapitops/plan/system/listeners/BukkitListenerSystem.java
@@ -19,6 +19,7 @@ package com.djrapitops.plan.system.listeners;
import com.djrapitops.plan.Plan;
import com.djrapitops.plan.PlanPlugin;
import com.djrapitops.plan.api.events.PlanBukkitEnableEvent;
+import com.djrapitops.plan.capability.CapabilityServiceImplementation;
import com.djrapitops.plan.system.listeners.bukkit.*;
import com.djrapitops.plan.system.status.Status;
import org.bukkit.Bukkit;
@@ -83,7 +84,9 @@ public class BukkitListenerSystem extends ListenerSystem {
@Override
public void callEnableEvent(PlanPlugin plugin) {
- PlanBukkitEnableEvent event = new PlanBukkitEnableEvent(plugin.isSystemEnabled());
+ boolean isEnabled = plugin.isSystemEnabled();
+ PlanBukkitEnableEvent event = new PlanBukkitEnableEvent(isEnabled);
Bukkit.getServer().getPluginManager().callEvent(event);
+ CapabilityServiceImplementation.notifyAboutEnable(isEnabled);
}
}
diff --git a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/tasks/BukkitTaskSystem.java b/Plan/bukkit/src/main/java/com/djrapitops/plan/system/tasks/BukkitTaskSystem.java
index 364c53215..cd2d4ea14 100644
--- a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/tasks/BukkitTaskSystem.java
+++ b/Plan/bukkit/src/main/java/com/djrapitops/plan/system/tasks/BukkitTaskSystem.java
@@ -21,6 +21,7 @@ import com.djrapitops.plan.ShutdownHook;
import com.djrapitops.plan.db.tasks.DBCleanTask;
import com.djrapitops.plan.extension.ExtensionServerMethodCallerTask;
import com.djrapitops.plan.system.settings.config.PlanConfig;
+import com.djrapitops.plan.system.settings.paths.DataGatheringSettings;
import com.djrapitops.plan.system.settings.paths.TimeSettings;
import com.djrapitops.plan.system.tasks.bukkit.BukkitTPSCountTimer;
import com.djrapitops.plan.system.tasks.bukkit.PaperTPSCountTimer;
@@ -90,9 +91,12 @@ public class BukkitTaskSystem extends ServerTaskSystem {
public void enable() {
super.enable();
try {
- plugin.registerListener(pingCountTimer);
- long startDelay = TimeAmount.toTicks(config.get(TimeSettings.PING_SERVER_ENABLE_DELAY), TimeUnit.MILLISECONDS);
- registerTask(pingCountTimer).runTaskTimer(startDelay, 40L);
+ Long pingDelay = config.get(TimeSettings.PING_SERVER_ENABLE_DELAY);
+ if (pingDelay < TimeUnit.HOURS.toMillis(1L) && config.get(DataGatheringSettings.PING)) {
+ plugin.registerListener(pingCountTimer);
+ long startDelay = TimeAmount.toTicks(pingDelay, TimeUnit.MILLISECONDS);
+ registerTask(pingCountTimer).runTaskTimer(startDelay, 40L);
+ }
} catch (ExceptionInInitializerError | NoClassDefFoundError ignore) {
// Running CraftBukkit
}
diff --git a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/tasks/bukkit/PingCountTimerBukkit.java b/Plan/bukkit/src/main/java/com/djrapitops/plan/system/tasks/bukkit/PingCountTimerBukkit.java
index 40390b164..3d98d62ba 100644
--- a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/tasks/bukkit/PingCountTimerBukkit.java
+++ b/Plan/bukkit/src/main/java/com/djrapitops/plan/system/tasks/bukkit/PingCountTimerBukkit.java
@@ -64,7 +64,6 @@ public class PingCountTimerBukkit extends AbsRunnable implements Listener {
//the server is pinging the client every 40 Ticks (2 sec) - so check it then
//https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/PlayerConnection.java#L178
- public static final int PING_INTERVAL = 2 * 20;
private static final boolean PING_METHOD_AVAILABLE;
@@ -196,6 +195,10 @@ public class PingCountTimerBukkit extends AbsRunnable implements Listener {
@EventHandler
public void onPlayerJoin(PlayerJoinEvent joinEvent) {
Player player = joinEvent.getPlayer();
+ Long pingDelay = config.get(TimeSettings.PING_PLAYER_LOGIN_DELAY);
+ if (pingDelay >= TimeUnit.HOURS.toMillis(2L)) {
+ return;
+ }
runnableFactory.create("Add Player to Ping list", new AbsRunnable() {
@Override
public void run() {
@@ -203,7 +206,7 @@ public class PingCountTimerBukkit extends AbsRunnable implements Listener {
addPlayer(player);
}
}
- }).runTaskLater(TimeAmount.toTicks(config.get(TimeSettings.PING_PLAYER_LOGIN_DELAY), TimeUnit.MILLISECONDS));
+ }).runTaskLater(TimeAmount.toTicks(pingDelay, TimeUnit.MILLISECONDS));
}
@EventHandler
diff --git a/Plan/bukkit/src/main/java/com/djrapitops/plan/utilities/java/Reflection.java b/Plan/bukkit/src/main/java/com/djrapitops/plan/utilities/java/Reflection.java
index 1372a529d..b1a143beb 100644
--- a/Plan/bukkit/src/main/java/com/djrapitops/plan/utilities/java/Reflection.java
+++ b/Plan/bukkit/src/main/java/com/djrapitops/plan/utilities/java/Reflection.java
@@ -122,7 +122,7 @@ public final class Reflection {
try {
return (T) field.get(target);
} catch (IllegalAccessException e) {
- throw new RuntimeException("Cannot access reflection.", e);
+ throw new IllegalStateException("Cannot access reflection.", e);
}
}
@@ -131,7 +131,7 @@ public final class Reflection {
try {
field.set(target, value);
} catch (IllegalAccessException e) {
- throw new RuntimeException("Cannot access reflection.", e);
+ throw new IllegalStateException("Cannot access reflection.", e);
}
}
@@ -197,7 +197,7 @@ public final class Reflection {
try {
return method.invoke(target, arguments);
} catch (Exception e) {
- throw new RuntimeException("Cannot invoke method " + method, e);
+ throw new IllegalStateException("Cannot invoke method " + method, e);
}
};
}
@@ -240,7 +240,7 @@ public final class Reflection {
try {
return constructor.newInstance(arguments);
} catch (Exception e) {
- throw new RuntimeException("Cannot invoke constructor " + constructor, e);
+ throw new IllegalStateException("Cannot invoke constructor " + constructor, e);
}
};
}
diff --git a/Plan/bukkit/src/test/java/com/djrapitops/plan/BukkitSystemTest.java b/Plan/bukkit/src/test/java/com/djrapitops/plan/BukkitSystemTest.java
index f76eba214..65f0fabcc 100644
--- a/Plan/bukkit/src/test/java/com/djrapitops/plan/BukkitSystemTest.java
+++ b/Plan/bukkit/src/test/java/com/djrapitops/plan/BukkitSystemTest.java
@@ -17,7 +17,10 @@
package com.djrapitops.plan;
import com.djrapitops.plan.api.exceptions.EnableException;
+import com.djrapitops.plan.db.Database;
+import com.djrapitops.plan.db.access.queries.objects.ServerQueries;
import com.djrapitops.plan.system.PlanSystem;
+import com.djrapitops.plan.system.info.server.Server;
import com.djrapitops.plan.system.settings.ConfigSettingKeyTest;
import com.djrapitops.plan.system.settings.config.PlanConfig;
import com.djrapitops.plan.system.settings.paths.WebserverSettings;
@@ -31,9 +34,11 @@ import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
import rules.BukkitComponentMocker;
import rules.ComponentMocker;
+import utilities.OptionalAssert;
import utilities.RandomData;
import java.util.Collection;
+import java.util.Optional;
import static org.junit.Assert.assertTrue;
@@ -71,6 +76,21 @@ public class BukkitSystemTest {
}
}
+ @Test
+ public void correctWebAddressInDatabaseAfterEnable() throws EnableException {
+ try {
+ system.enable();
+ Database database = system.getDatabaseSystem().getDatabase();
+ String expectedAddress = system.getWebServerSystem().getWebServer().getAccessAddress();
+ Optional found = database.query(ServerQueries.fetchServerMatchingIdentifier(system.getServerInfo().getServerUUID()))
+ .map(Server::getWebAddress);
+
+ OptionalAssert.equals(expectedAddress, found);
+ } finally {
+ system.disable();
+ }
+ }
+
@Test
public void bukkitSystemHasDefaultConfigValuesAfterEnable() throws EnableException, IllegalAccessException {
try {
diff --git a/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/info/server/properties/BungeeServerProperties.java b/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/info/server/properties/BungeeServerProperties.java
index 653713b0b..424ac8494 100644
--- a/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/info/server/properties/BungeeServerProperties.java
+++ b/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/info/server/properties/BungeeServerProperties.java
@@ -31,7 +31,6 @@ public class BungeeServerProperties extends ServerProperties {
public BungeeServerProperties(ProxyServer server, PlanConfig config) {
super(
- server.getServers().toString(),
"BungeeCord",
-1,
server.getVersion(),
diff --git a/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/listeners/BungeeListenerSystem.java b/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/listeners/BungeeListenerSystem.java
index b4fba6aa4..722ed46dd 100644
--- a/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/listeners/BungeeListenerSystem.java
+++ b/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/listeners/BungeeListenerSystem.java
@@ -19,6 +19,7 @@ package com.djrapitops.plan.system.listeners;
import com.djrapitops.plan.PlanBungee;
import com.djrapitops.plan.PlanPlugin;
import com.djrapitops.plan.api.events.PlanBungeeEnableEvent;
+import com.djrapitops.plan.capability.CapabilityServiceImplementation;
import com.djrapitops.plan.system.listeners.bungee.PlayerOnlineListener;
import javax.inject.Inject;
@@ -46,7 +47,9 @@ public class BungeeListenerSystem extends ListenerSystem {
@Override
public void callEnableEvent(PlanPlugin plugin) {
- PlanBungeeEnableEvent event = new PlanBungeeEnableEvent(plugin.isSystemEnabled());
+ boolean isEnabled = plugin.isSystemEnabled();
+ PlanBungeeEnableEvent event = new PlanBungeeEnableEvent(isEnabled);
((PlanBungee) plugin).getProxy().getPluginManager().callEvent(event);
+ CapabilityServiceImplementation.notifyAboutEnable(isEnabled);
}
}
diff --git a/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/tasks/BungeeTaskSystem.java b/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/tasks/BungeeTaskSystem.java
index b34ead8a6..dca6baaa5 100644
--- a/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/tasks/BungeeTaskSystem.java
+++ b/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/tasks/BungeeTaskSystem.java
@@ -20,6 +20,7 @@ import com.djrapitops.plan.PlanBungee;
import com.djrapitops.plan.db.tasks.DBCleanTask;
import com.djrapitops.plan.extension.ExtensionServerMethodCallerTask;
import com.djrapitops.plan.system.settings.config.PlanConfig;
+import com.djrapitops.plan.system.settings.paths.DataGatheringSettings;
import com.djrapitops.plan.system.settings.paths.TimeSettings;
import com.djrapitops.plan.system.tasks.bungee.BungeeTPSCountTimer;
import com.djrapitops.plan.system.tasks.bungee.PingCountTimerBungee;
@@ -87,9 +88,12 @@ public class BungeeTaskSystem extends TaskSystem {
registerTask(networkPageRefreshTask).runTaskTimerAsynchronously(1500, TimeAmount.toTicks(5L, TimeUnit.MINUTES));
registerTask(logsFolderCleanTask).runTaskLaterAsynchronously(TimeAmount.toTicks(30L, TimeUnit.SECONDS));
- plugin.registerListener(pingCountTimer);
- long startDelay = TimeAmount.toTicks(config.get(TimeSettings.PING_SERVER_ENABLE_DELAY), TimeUnit.MILLISECONDS);
- registerTask(pingCountTimer).runTaskTimer(startDelay, PingCountTimerBungee.PING_INTERVAL);
+ Long pingDelay = config.get(TimeSettings.PING_SERVER_ENABLE_DELAY);
+ if (pingDelay < TimeUnit.HOURS.toMillis(1L) && config.get(DataGatheringSettings.PING)) {
+ plugin.registerListener(pingCountTimer);
+ long startDelay = TimeAmount.toTicks(pingDelay, TimeUnit.MILLISECONDS);
+ registerTask(pingCountTimer).runTaskTimer(startDelay, 40L);
+ }
registerTask(playersPageRefreshTask)
.runTaskTimerAsynchronously(TimeAmount.toTicks(5L, TimeUnit.MINUTES), TimeAmount.toTicks(5L, TimeUnit.MINUTES));
diff --git a/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/tasks/bungee/PingCountTimerBungee.java b/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/tasks/bungee/PingCountTimerBungee.java
index 6d36301d0..b9245fb62 100644
--- a/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/tasks/bungee/PingCountTimerBungee.java
+++ b/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/tasks/bungee/PingCountTimerBungee.java
@@ -52,10 +52,6 @@ import java.util.concurrent.TimeUnit;
@Singleton
public class PingCountTimerBungee extends AbsRunnable implements Listener {
- //the server is pinging the client every 40 Ticks (2 sec) - so check it then
- //https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/PlayerConnection.java#L178
- public static final int PING_INTERVAL = 2 * 20;
-
private final Map>> playerHistory;
private final PlanConfig config;
@@ -121,6 +117,10 @@ public class PingCountTimerBungee extends AbsRunnable implements Listener {
@EventHandler
public void onPlayerJoin(ServerConnectedEvent joinEvent) {
ProxiedPlayer player = joinEvent.getPlayer();
+ Long pingDelay = config.get(TimeSettings.PING_PLAYER_LOGIN_DELAY);
+ if (pingDelay >= TimeUnit.HOURS.toMillis(2L)) {
+ return;
+ }
runnableFactory.create("Add Player to Ping list", new AbsRunnable() {
@Override
public void run() {
@@ -128,7 +128,7 @@ public class PingCountTimerBungee extends AbsRunnable implements Listener {
addPlayer(player);
}
}
- }).runTaskLater(TimeAmount.toTicks(config.get(TimeSettings.PING_PLAYER_LOGIN_DELAY), TimeUnit.MILLISECONDS));
+ }).runTaskLater(TimeAmount.toTicks(pingDelay, TimeUnit.MILLISECONDS));
}
@EventHandler
diff --git a/Plan/bungeecord/src/test/java/com/djrapitops/plan/BungeeSystemTest.java b/Plan/bungeecord/src/test/java/com/djrapitops/plan/BungeeSystemTest.java
index 07cf69a24..ab4a66fb0 100644
--- a/Plan/bungeecord/src/test/java/com/djrapitops/plan/BungeeSystemTest.java
+++ b/Plan/bungeecord/src/test/java/com/djrapitops/plan/BungeeSystemTest.java
@@ -21,6 +21,7 @@ import com.djrapitops.plan.db.SQLiteDB;
import com.djrapitops.plan.system.PlanSystem;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.settings.config.PlanConfig;
+import com.djrapitops.plan.system.settings.paths.DatabaseSettings;
import com.djrapitops.plan.system.settings.paths.ProxySettings;
import com.djrapitops.plan.system.settings.paths.WebserverSettings;
import com.google.common.util.concurrent.MoreExecutors;
@@ -33,9 +34,11 @@ import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
import rules.BungeeComponentMocker;
import rules.ComponentMocker;
+import utilities.CIProperties;
import utilities.RandomData;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
/**
* Test for Bungee PlanSystem.
@@ -69,6 +72,7 @@ public class BungeeSystemTest {
dbSystem.setActiveDatabase(db);
bungeeSystem.enable();
+ assertTrue(bungeeSystem.isEnabled());
} finally {
bungeeSystem.disable();
}
@@ -90,8 +94,7 @@ public class BungeeSystemTest {
db.setTransactionExecutorServiceProvider(MoreExecutors::newDirectExecutorService);
dbSystem.setActiveDatabase(db);
- bungeeSystem.enable();
- assertTrue(bungeeSystem.isEnabled());
+ bungeeSystem.enable(); // Throws EnableException
} finally {
bungeeSystem.disable();
}
@@ -107,7 +110,31 @@ public class BungeeSystemTest {
config.set(WebserverSettings.PORT, TEST_PORT_NUMBER);
config.set(ProxySettings.IP, "8.8.8.8");
+ bungeeSystem.enable(); // Throws EnableException
+ } finally {
+ bungeeSystem.disable();
+ }
+ }
+
+ @Test
+ public void testEnableWithMySQL() throws EnableException {
+ boolean isCI = Boolean.parseBoolean(System.getenv(CIProperties.IS_CI_SERVICE));
+ assumeTrue(isCI);
+
+ PlanSystem bungeeSystem = component.getPlanSystem();
+ try {
+ PlanConfig config = bungeeSystem.getConfigSystem().getConfig();
+ config.set(DatabaseSettings.MYSQL_DATABASE, "Plan");
+ config.set(DatabaseSettings.MYSQL_USER, "travis");
+ config.set(DatabaseSettings.MYSQL_PASS, "");
+ config.set(DatabaseSettings.MYSQL_HOST, "127.0.0.1");
+ config.set(DatabaseSettings.TYPE, "MySQL");
+
+ config.set(WebserverSettings.PORT, TEST_PORT_NUMBER);
+ config.set(ProxySettings.IP, "8.8.8.8");
+
bungeeSystem.enable();
+ assertTrue(bungeeSystem.isEnabled());
} finally {
bungeeSystem.disable();
}
diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/redprotect/RedProtectHook.java b/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/DataExtensionMethodCallException.java
similarity index 52%
rename from PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/redprotect/RedProtectHook.java
rename to Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/DataExtensionMethodCallException.java
index 7f9a36a64..342d9cb2b 100644
--- a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/redprotect/RedProtectHook.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/DataExtensionMethodCallException.java
@@ -14,31 +14,31 @@
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see .
*/
-package com.djrapitops.pluginbridge.plan.redprotect;
+package com.djrapitops.plan.api.exceptions;
-import com.djrapitops.plan.data.plugin.HookHandler;
-import com.djrapitops.pluginbridge.plan.Hook;
-
-import javax.inject.Inject;
-import javax.inject.Singleton;
+import com.djrapitops.plan.extension.implementation.providers.MethodWrapper;
/**
- * Hook for RedProtect plugin.
+ * Exception that is thrown when a call to a DataExtension method throws an exception.
*
* @author Rsl1122
*/
-@Singleton
-public class RedProtectHook extends Hook {
+public class DataExtensionMethodCallException extends IllegalStateException {
- @Inject
- public RedProtectHook() {
- super("br.net.fabiozumbi12.RedProtect.Bukkit.RedProtect");
+ private final String pluginName;
+ private final MethodWrapper method;
+
+ public DataExtensionMethodCallException(Throwable cause, String pluginName, MethodWrapper method) {
+ super(cause);
+ this.pluginName = pluginName;
+ this.method = method;
}
- @Override
- public void hook(HookHandler handler) throws NoClassDefFoundError {
- if (enabled) {
- handler.addPluginDataSource(new RedProtectData());
- }
+ public String getPluginName() {
+ return pluginName;
+ }
+
+ public MethodWrapper getMethod() {
+ return method;
}
}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/capability/CapabilityServiceImplementation.java b/Plan/common/src/main/java/com/djrapitops/plan/capability/CapabilityServiceImplementation.java
new file mode 100644
index 000000000..38696be98
--- /dev/null
+++ b/Plan/common/src/main/java/com/djrapitops/plan/capability/CapabilityServiceImplementation.java
@@ -0,0 +1,57 @@
+/*
+ * 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 GNU 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
+ * GNU 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 .
+ */
+package com.djrapitops.plan.capability;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * Singleton instance implementation for {@link CapabilityService}.
+ *
+ * Only one instance exists per runtime in order to notify others when the plugin enables.
+ *
+ * @author Rsl1122
+ */
+public class CapabilityServiceImplementation implements CapabilityService {
+
+ private List> enableListeners;
+
+ private CapabilityServiceImplementation() {
+ /* Inject required for dagger */
+ CapabilityServiceHolder.set(this);
+ enableListeners = new ArrayList<>();
+ }
+
+ private static CapabilityServiceImplementation get() {
+ if (CapabilityServiceHolder.service == null) {
+ return new CapabilityServiceImplementation();
+ }
+ return (CapabilityServiceImplementation) CapabilityServiceHolder.service;
+ }
+
+ public static void notifyAboutEnable(boolean isEnabled) {
+ for (Consumer enableListener : get().enableListeners) {
+ enableListener.accept(isEnabled);
+ }
+ }
+
+ @Override
+ public void registerEnableListener(Consumer enableListener) {
+ enableListeners.add(enableListener);
+ }
+}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageConDebugCommand.java b/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageConDebugCommand.java
index 4a9e4449d..c82c653b8 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageConDebugCommand.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageConDebugCommand.java
@@ -32,7 +32,6 @@ import com.djrapitops.plan.system.locale.lang.ManageLang;
import com.djrapitops.plan.system.processing.Processing;
import com.djrapitops.plan.system.settings.Permissions;
import com.djrapitops.plan.system.webserver.WebServer;
-import com.djrapitops.plugin.api.Check;
import com.djrapitops.plugin.command.ColorScheme;
import com.djrapitops.plugin.command.CommandNode;
import com.djrapitops.plugin.command.CommandType;
@@ -82,7 +81,7 @@ public class ManageConDebugCommand extends CommandNode {
this.webServer = webServer;
this.dbSystem = dbSystem;
- setShortHelp(locale.getString(Check.isBungeeAvailable() || Check.isVelocityAvailable() ? CmdHelpLang.CON : CmdHelpLang.MANAGE_CON));
+ setShortHelp(locale.getString(serverInfo.getServer().isProxy() ? CmdHelpLang.CON : CmdHelpLang.MANAGE_CON));
setInDepthHelp(locale.getArray(DeepHelpLang.MANAGE_CON));
}
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageDisableCommand.java b/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageDisableCommand.java
index 30ab4d9b5..913c32886 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageDisableCommand.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageDisableCommand.java
@@ -60,7 +60,7 @@ public class ManageDisableCommand extends CommandNode {
Verify.isTrue(args.length >= 1,
() -> new IllegalArgumentException(locale.getString(CommandLang.FAIL_REQ_ONE_ARG, Arrays.toString(this.getArguments()))));
- if ("kickcount".equals(args[0].toLowerCase())) {
+ if ("kickcount".equalsIgnoreCase(args[0])) {
status.setCountKicks(false);
sender.sendMessage(locale.getString(CommandLang.FEATURE_DISABLED, "Kick Counting"));
} else {
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/container/GeoInfo.java b/Plan/common/src/main/java/com/djrapitops/plan/data/container/GeoInfo.java
index 02fc740f0..f5ab5b430 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/data/container/GeoInfo.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/data/container/GeoInfo.java
@@ -18,13 +18,11 @@ package com.djrapitops.plan.data.container;
import com.djrapitops.plan.data.store.objects.DateHolder;
import com.djrapitops.plan.data.store.objects.DateMap;
-import com.djrapitops.plan.utilities.SHA256Hash;
import com.google.common.base.Objects;
import java.io.Serializable;
import java.net.Inet6Address;
import java.net.InetAddress;
-import java.security.NoSuchAlgorithmException;
/**
* Data class that contains information about IP and Geolocation.
@@ -35,18 +33,16 @@ public class GeoInfo implements DateHolder, Serializable {
private final String ip;
private final String geolocation;
- private final String ipHash;
private final long date;
- public GeoInfo(InetAddress address, String geolocation, long lastUsed) throws NoSuchAlgorithmException {
- this(formatIP(address), geolocation, lastUsed, new SHA256Hash(address.getHostAddress()).create());
+ public GeoInfo(InetAddress address, String geolocation, long lastUsed) {
+ this(formatIP(address), geolocation, lastUsed);
}
- public GeoInfo(String ip, String geolocation, long date, String ipHash) {
+ public GeoInfo(String ip, String geolocation, long date) {
this.ip = ip;
this.geolocation = geolocation;
this.date = date;
- this.ipHash = ipHash;
}
public static DateMap intoDateMap(Iterable geoInfo) {
@@ -106,23 +102,18 @@ public class GeoInfo implements DateHolder, Serializable {
return date;
}
- public String getIpHash() {
- return ipHash;
- }
-
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
GeoInfo geoInfo = (GeoInfo) o;
return Objects.equal(ip, geoInfo.ip) &&
- Objects.equal(geolocation, geoInfo.geolocation) &&
- Objects.equal(ipHash, geoInfo.ipHash);
+ Objects.equal(geolocation, geoInfo.geolocation);
}
@Override
public int hashCode() {
- return Objects.hashCode(ip, geolocation, ipHash);
+ return Objects.hashCode(ip, geolocation);
}
@Override
@@ -130,7 +121,6 @@ public class GeoInfo implements DateHolder, Serializable {
return "GeoInfo{" +
"ip='" + ip + '\'' +
", geolocation='" + geolocation + '\'' +
- ", ipHash='" + ipHash + '\'' +
", date=" + date +
'}';
}
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/element/TableContainer.java b/Plan/common/src/main/java/com/djrapitops/plan/data/element/TableContainer.java
index 8adde4ffc..d4bf8046c 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/data/element/TableContainer.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/data/element/TableContainer.java
@@ -92,10 +92,7 @@ public class TableContainer {
if (i > maxIndex) {
body.append("-");
} else {
- Serializable value = row[i];
- Formatter formatter = formatters[i];
- body.append(" " : ">");
- body.append(formatter != null ? formatter.apply(value) : (value != null ? value : '-'));
+ appendValue(body, row[i], formatters[i]);
}
body.append(" ");
} catch (ClassCastException | ArrayIndexOutOfBoundsException e) {
@@ -105,6 +102,15 @@ public class TableContainer {
body.append("");
}
+ private void appendValue(StringBuilder body, Serializable value, Formatter formatter) {
+ body.append("" : ">");
+ if (formatter != null) {
+ body.append(formatter.apply(value));
+ } else {
+ body.append(value != null ? value : '-');
+ }
+ }
+
public final void setColor(String color) {
this.color = color;
}
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/health/AbstractHealthInfo.java b/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/health/AbstractHealthInfo.java
index e6f38111d..ce42de3ed 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/health/AbstractHealthInfo.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/health/AbstractHealthInfo.java
@@ -29,7 +29,7 @@ import java.util.concurrent.TimeUnit;
public abstract class AbstractHealthInfo {
- protected final String subNote = " ";
+ protected static final String SUB_NOTE = " ";
protected final List notes;
protected final long now;
@@ -109,7 +109,7 @@ public abstract class AbstractHealthInfo {
StringBuilder remainNote = new StringBuilder();
if (activeFWAGNum != 0) {
- remainNote.append(subNote);
+ remainNote.append(SUB_NOTE);
if (percRemain > 0.5) {
remainNote.append(Icons.GREEN_THUMB);
} else if (percRemain > 0.2) {
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/health/HealthInformation.java b/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/health/HealthInformation.java
index 63b440403..ea79c05a4 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/health/HealthInformation.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/health/HealthInformation.java
@@ -124,7 +124,7 @@ public class HealthInformation extends AbstractHealthInfo {
double aboveThreshold = tpsMutator.percentageTPSAboveThreshold(lowTPSThreshold);
long tpsSpikeMonth = analysisContainer.getValue(AnalysisKeys.TPS_SPIKE_MONTH).orElse(0);
- StringBuilder avgLowThresholdString = new StringBuilder(subNote);
+ StringBuilder avgLowThresholdString = new StringBuilder(SUB_NOTE);
if (aboveThreshold >= 0.96) {
avgLowThresholdString.append(Icons.GREEN_THUMB);
} else if (aboveThreshold >= 0.9) {
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/health/NetworkHealthInformation.java b/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/health/NetworkHealthInformation.java
index 8e4f0f6e5..db50e1b8c 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/health/NetworkHealthInformation.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/health/NetworkHealthInformation.java
@@ -112,7 +112,7 @@ public class NetworkHealthInformation extends AbstractHealthInfo {
.map(c -> {
int playersPerMonth = c.getUnsafe(AnalysisKeys.AVG_PLAYERS_MONTH);
Server server = c.getUnsafe(serverKey);
- return subNote + (playersPerMonth >= average && playersPerMonth > 0 ? Icons.GREEN_PLUS : Icons.RED_MINUS) + " " +
+ return SUB_NOTE + (playersPerMonth >= average && playersPerMonth > 0 ? Icons.GREEN_PLUS : Icons.RED_MINUS) + " " +
server.getName() + ": " + playersPerMonth;
}).forEach(subNotes::append);
addNote(icon + " " + decimalFormatter.apply(average) + uniquePlayersNote + subNotes.toString());
@@ -139,7 +139,7 @@ public class NetworkHealthInformation extends AbstractHealthInfo {
.map(c -> {
int playersPerMonth = c.getUnsafe(AnalysisKeys.AVG_PLAYERS_NEW_MONTH);
Server server = c.getUnsafe(serverKey);
- return subNote + (playersPerMonth >= average && playersPerMonth > 0 ? Icons.GREEN_PLUS : Icons.RED_MINUS) + " " +
+ return SUB_NOTE + (playersPerMonth >= average && playersPerMonth > 0 ? Icons.GREEN_PLUS : Icons.RED_MINUS) + " " +
server.getName() + ": " + playersPerMonth;
}).forEach(subNotes::append);
addNote(icon + " " + decimalFormatter.apply(average) + newPlayersNote + subNotes.toString());
@@ -179,7 +179,7 @@ public class NetworkHealthInformation extends AbstractHealthInfo {
.map(c -> {
int playersPerMonth = c.getUnsafe(AnalysisKeys.PLAYERS_MONTH);
Server server = c.getUnsafe(serverKey);
- return subNote + (playersPerMonth > 0 ? Icons.GREEN_PLUS : Icons.RED_MINUS) + " " +
+ return SUB_NOTE + (playersPerMonth > 0 ? Icons.GREEN_PLUS : Icons.RED_MINUS) + " " +
server.getName() + ": " + playersPerMonth;
}).forEach(subNotes::append);
addNote(icon.toHtml() + " " + uniquePlayersNote + subNotes.toString());
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/H2DB.java b/Plan/common/src/main/java/com/djrapitops/plan/db/H2DB.java
index aead059b5..b5f08b0e9 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/db/H2DB.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/db/H2DB.java
@@ -92,7 +92,7 @@ public class H2DB extends SQLDB {
String password = config.get(DatabaseSettings.MYSQL_PASS);
JdbcDataSource jdbcDataSource = new JdbcDataSource();
- jdbcDataSource.setURL("jdbc:h2:file:" + dbFilePath + ";mode=MySQL");
+ jdbcDataSource.setURL("jdbc:h2:file:" + dbFilePath + ";mode=MySQL;DATABASE_TO_UPPER=false");
jdbcDataSource.setUser(username);
jdbcDataSource.setPassword(password);
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/SQLDB.java b/Plan/common/src/main/java/com/djrapitops/plan/db/SQLDB.java
index 51015b4f4..f92e7c793 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/db/SQLDB.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/db/SQLDB.java
@@ -115,6 +115,10 @@ public abstract class SQLDB extends AbstractDatabase {
transactionExecutor.shutdown();
try {
Long waitMs = config.getOrDefault(TimeSettings.DB_TRANSACTION_FINISH_WAIT_DELAY, TimeUnit.SECONDS.toMillis(20L));
+ if (waitMs > TimeUnit.MINUTES.toMillis(5L)) {
+ logger.warn(TimeSettings.DB_TRANSACTION_FINISH_WAIT_DELAY.getPath() + " was set to over 5 minutes, using 5 min instead.");
+ waitMs = TimeUnit.MINUTES.toMillis(5L);
+ }
if (!transactionExecutor.awaitTermination(waitMs, TimeUnit.MILLISECONDS)) {
List unfinished = transactionExecutor.shutdownNow();
int unfinishedCount = unfinished.size();
@@ -149,9 +153,9 @@ public abstract class SQLDB extends AbstractDatabase {
new UserInfoOptimizationPatch(),
new GeoInfoOptimizationPatch(),
new TransferTableRemovalPatch(),
- new IPHashPatch(),
new IPAnonPatch(),
- new BadAFKThresholdValuePatch()
+ new BadAFKThresholdValuePatch(),
+ new DeleteIPHashesPatch()
};
}
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/DataStoreQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/DataStoreQueries.java
index a35f6aa9a..dfbf6c388 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/DataStoreQueries.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/DataStoreQueries.java
@@ -27,6 +27,7 @@ import com.djrapitops.plan.db.access.ExecBatchStatement;
import com.djrapitops.plan.db.access.ExecStatement;
import com.djrapitops.plan.db.access.Executable;
import com.djrapitops.plan.db.sql.tables.*;
+import com.djrapitops.plugin.utilities.Verify;
import java.sql.PreparedStatement;
import java.sql.SQLException;
@@ -88,7 +89,7 @@ public class DataStoreQueries {
* @throws IllegalArgumentException If {@link Session#endSession(long)} has not yet been called.
*/
public static Executable storeSession(Session session) {
- session.getValue(SessionKeys.END).orElseThrow(() -> new IllegalArgumentException("Attempted to save a session that has not ended."));
+ Verify.isTrue(session.supports(SessionKeys.END), () -> new IllegalArgumentException("Attempted to save a session that has not ended."));
return connection -> {
storeSessionInformation(session).execute(connection);
storeSessionKills(session).execute(connection);
@@ -164,8 +165,7 @@ public class DataStoreQueries {
public void prepare(PreparedStatement statement) throws SQLException {
statement.setLong(1, geoInfo.getDate());
statement.setString(2, playerUUID.toString());
- statement.setString(3, geoInfo.getIpHash());
- statement.setString(4, geoInfo.getGeolocation());
+ statement.setString(3, geoInfo.getGeolocation());
}
};
}
@@ -176,9 +176,8 @@ public class DataStoreQueries {
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, playerUUID.toString());
statement.setString(2, geoInfo.getIp());
- statement.setString(3, geoInfo.getIpHash());
- statement.setString(4, geoInfo.getGeolocation());
- statement.setLong(5, geoInfo.getDate());
+ statement.setString(3, geoInfo.getGeolocation());
+ statement.setLong(4, geoInfo.getDate());
}
};
}
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/LargeStoreQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/LargeStoreQueries.java
index 2846e1b9c..6014fc86e 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/LargeStoreQueries.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/LargeStoreQueries.java
@@ -97,15 +97,13 @@ public class LargeStoreQueries {
// Every GeoInfo
for (GeoInfo info : playerEntry.getValue()) {
String ip = info.getIp();
- String ipHash = info.getIpHash();
String geoLocation = info.getGeolocation();
long lastUsed = info.getDate();
statement.setString(1, playerUUID.toString());
statement.setString(2, ip);
- statement.setString(3, ipHash);
- statement.setString(4, geoLocation);
- statement.setLong(5, lastUsed);
+ statement.setString(3, geoLocation);
+ statement.setLong(4, lastUsed);
statement.addBatch();
}
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/containers/ServerPlayersTableContainersQuery.java b/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/containers/ServerPlayersTableContainersQuery.java
new file mode 100644
index 000000000..c1594e184
--- /dev/null
+++ b/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/containers/ServerPlayersTableContainersQuery.java
@@ -0,0 +1,82 @@
+/*
+ * 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 GNU 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
+ * GNU 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 .
+ */
+package com.djrapitops.plan.db.access.queries.containers;
+
+import com.djrapitops.plan.data.container.BaseUser;
+import com.djrapitops.plan.data.container.GeoInfo;
+import com.djrapitops.plan.data.container.Session;
+import com.djrapitops.plan.data.store.containers.PlayerContainer;
+import com.djrapitops.plan.data.store.keys.PlayerKeys;
+import com.djrapitops.plan.db.SQLDB;
+import com.djrapitops.plan.db.access.Query;
+import com.djrapitops.plan.db.access.queries.objects.BaseUserQueries;
+import com.djrapitops.plan.db.access.queries.objects.GeoInfoQueries;
+import com.djrapitops.plan.db.access.queries.objects.SessionQueries;
+import com.djrapitops.plan.db.access.queries.objects.UserInfoQueries;
+
+import java.util.*;
+
+/**
+ * Optimized version of {@link ServerPlayerContainersQuery} for /server page Players table.
+ *
+ * @author Rsl1122
+ * @see com.djrapitops.plan.utilities.html.tables.PlayersTableJSONParser For what needs to be included.
+ */
+public class ServerPlayersTableContainersQuery implements Query> {
+
+ private final UUID serverUUID;
+
+ public ServerPlayersTableContainersQuery(UUID serverUUID) {
+ this.serverUUID = serverUUID;
+ }
+
+ @Override
+ public List executeQuery(SQLDB db) {
+ List containers = new ArrayList<>();
+
+ Collection baseUsers = db.query(BaseUserQueries.fetchServerBaseUsers(serverUUID));
+
+ Map> geoInformation = db.query(GeoInfoQueries.fetchServerGeoInformation(serverUUID));
+ Map> sessions = db.query(SessionQueries.fetchSessionsOfServer(serverUUID));
+ Set bannedUsers = db.query(UserInfoQueries.fetchBannedUUIDsOfServer(serverUUID));
+
+ for (BaseUser user : baseUsers) {
+ PlayerContainer container = new PlayerContainer();
+
+ // BaseUser
+ UUID uuid = user.getUuid();
+ container.putRawData(PlayerKeys.UUID, uuid);
+ container.putRawData(PlayerKeys.NAME, user.getName());
+ container.putRawData(PlayerKeys.REGISTERED, user.getRegistered());
+ container.putRawData(PlayerKeys.BANNED, bannedUsers.contains(uuid));
+
+ // GeoInfo
+ container.putRawData(PlayerKeys.GEO_INFO, geoInformation.getOrDefault(uuid, new ArrayList<>()));
+
+ container.putCachingSupplier(PlayerKeys.SESSIONS, () -> {
+ List playerSessions = sessions.getOrDefault(uuid, new ArrayList<>());
+ container.getValue(PlayerKeys.ACTIVE_SESSION).ifPresent(playerSessions::add);
+ return playerSessions;
+ }
+ );
+
+ containers.add(container);
+ }
+ return containers;
+ }
+
+}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/GeoInfoQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/GeoInfoQueries.java
index f5cc2f439..8db220947 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/GeoInfoQueries.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/GeoInfoQueries.java
@@ -49,7 +49,6 @@ public class GeoInfoQueries {
GeoInfoTable.IP + ", " +
GeoInfoTable.GEOLOCATION + ", " +
GeoInfoTable.LAST_USED + ", " +
- GeoInfoTable.IP_HASH + ", " +
GeoInfoTable.USER_UUID +
" FROM " + GeoInfoTable.TABLE_NAME;
@@ -64,9 +63,8 @@ public class GeoInfoQueries {
String ip = set.getString(GeoInfoTable.IP);
String geolocation = set.getString(GeoInfoTable.GEOLOCATION);
- String ipHash = set.getString(GeoInfoTable.IP_HASH);
long lastUsed = set.getLong(GeoInfoTable.LAST_USED);
- userGeoInfo.add(new GeoInfo(ip, geolocation, lastUsed, ipHash));
+ userGeoInfo.add(new GeoInfo(ip, geolocation, lastUsed));
geoInformation.put(uuid, userGeoInfo);
}
@@ -97,9 +95,8 @@ public class GeoInfoQueries {
while (set.next()) {
String ip = set.getString(GeoInfoTable.IP);
String geolocation = set.getString(GeoInfoTable.GEOLOCATION);
- String ipHash = set.getString(GeoInfoTable.IP_HASH);
long lastUsed = set.getLong(GeoInfoTable.LAST_USED);
- geoInfo.add(new GeoInfo(ip, geolocation, lastUsed, ipHash));
+ geoInfo.add(new GeoInfo(ip, geolocation, lastUsed));
}
return geoInfo;
}
@@ -110,8 +107,7 @@ public class GeoInfoQueries {
String sql = "SELECT " + GeoInfoTable.TABLE_NAME + "." + GeoInfoTable.USER_UUID + ", " +
GeoInfoTable.GEOLOCATION + ", " +
GeoInfoTable.LAST_USED + ", " +
- GeoInfoTable.IP + ", " +
- GeoInfoTable.IP_HASH +
+ GeoInfoTable.IP +
" FROM " + GeoInfoTable.TABLE_NAME +
" INNER JOIN " + UserInfoTable.TABLE_NAME + " on " +
GeoInfoTable.TABLE_NAME + "." + GeoInfoTable.USER_UUID + "=" + UserInfoTable.TABLE_NAME + "." + UserInfoTable.USER_UUID +
@@ -132,9 +128,8 @@ public class GeoInfoQueries {
String ip = set.getString(GeoInfoTable.IP);
String geolocation = set.getString(GeoInfoTable.GEOLOCATION);
- String ipHash = set.getString(GeoInfoTable.IP_HASH);
long lastUsed = set.getLong(GeoInfoTable.LAST_USED);
- userGeoInfo.add(new GeoInfo(ip, geolocation, lastUsed, ipHash));
+ userGeoInfo.add(new GeoInfo(ip, geolocation, lastUsed));
geoInformation.put(uuid, userGeoInfo);
}
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/UserInfoQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/UserInfoQueries.java
index 042ba64d4..8d9ec2836 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/UserInfoQueries.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/UserInfoQueries.java
@@ -114,6 +114,12 @@ public class UserInfoQueries {
};
}
+ /**
+ * Query database for all User information of a specific server.
+ *
+ * @param serverUUID UUID of the Plan server.
+ * @return Map: Player UUID - user information
+ */
public static Query> fetchUserInformationOfServer(UUID serverUUID) {
String sql = SELECT +
UserInfoTable.REGISTERED + ", " +
@@ -146,4 +152,34 @@ public class UserInfoQueries {
}
};
}
+
+ /**
+ * Query database for UUIDs of banned players on a server.
+ *
+ * @param serverUUID UUID of the Plan server.
+ * @return Set: Player UUID of a banned player.
+ */
+ public static Query> fetchBannedUUIDsOfServer(UUID serverUUID) {
+ String sql = SELECT +
+ UserInfoTable.USER_UUID +
+ FROM + UserInfoTable.TABLE_NAME +
+ WHERE + UserInfoTable.SERVER_UUID + "=?" +
+ AND + UserInfoTable.BANNED + "=?";
+ return new QueryStatement>(sql, 1000) {
+ @Override
+ public void prepare(PreparedStatement statement) throws SQLException {
+ statement.setString(1, serverUUID.toString());
+ statement.setBoolean(2, true);
+ }
+
+ @Override
+ public Set processResults(ResultSet set) throws SQLException {
+ Set bannedUsers = new HashSet<>();
+ while (set.next()) {
+ bannedUsers.add(UUID.fromString(set.getString(UserInfoTable.USER_UUID)));
+ }
+ return bannedUsers;
+ }
+ };
+ }
}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/Transaction.java b/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/Transaction.java
index 07b5b5055..788890e72 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/Transaction.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/Transaction.java
@@ -157,4 +157,8 @@ public abstract class Transaction {
public String toString() {
return getClass().getSimpleName() + (success ? " (finished)" : "");
}
+
+ public boolean wasSuccessful() {
+ return success;
+ }
}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/commands/RemoveEverythingTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/commands/RemoveEverythingTransaction.java
index c426cad99..2ca5cdf1a 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/commands/RemoveEverythingTransaction.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/commands/RemoveEverythingTransaction.java
@@ -47,6 +47,9 @@ public class RemoveEverythingTransaction extends Transaction {
clearTable(ExtensionPlayerValueTable.TABLE_NAME);
clearTable(ExtensionServerValueTable.TABLE_NAME);
clearTable(ExtensionProviderTable.TABLE_NAME);
+ clearTable(ExtensionPlayerTableValueTable.TABLE_NAME);
+ clearTable(ExtensionServerTableValueTable.TABLE_NAME);
+ clearTable(ExtensionTableProviderTable.TABLE_NAME);
clearTable(ExtensionTabTable.TABLE_NAME);
clearTable(ExtensionPluginTable.TABLE_NAME);
clearTable(ExtensionIconTable.TABLE_NAME);
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/init/CreateTablesTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/init/CreateTablesTransaction.java
index 6718b40ae..35fd01ee1 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/init/CreateTablesTransaction.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/init/CreateTablesTransaction.java
@@ -53,5 +53,8 @@ public class CreateTablesTransaction extends OperationCriticalTransaction {
execute(ExtensionProviderTable.createTableSQL(dbType));
execute(ExtensionPlayerValueTable.createTableSQL(dbType));
execute(ExtensionServerValueTable.createTableSQL(dbType));
+ execute(ExtensionTableProviderTable.createTableSQL(dbType));
+ execute(ExtensionPlayerTableValueTable.createTableSQL(dbType));
+ execute(ExtensionServerTableValueTable.createTableSQL(dbType));
}
}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/init/RemoveOldSampledDataTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/init/RemoveOldSampledDataTransaction.java
index 46b99d5c4..c86739439 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/init/RemoveOldSampledDataTransaction.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/init/RemoveOldSampledDataTransaction.java
@@ -23,7 +23,6 @@ import com.djrapitops.plan.db.access.queries.objects.TPSQueries;
import com.djrapitops.plan.db.access.transactions.Transaction;
import com.djrapitops.plan.db.sql.tables.PingTable;
import com.djrapitops.plan.db.sql.tables.TPSTable;
-import com.djrapitops.plugin.api.TimeAmount;
import java.sql.PreparedStatement;
import java.sql.SQLException;
@@ -38,11 +37,17 @@ import java.util.UUID;
public class RemoveOldSampledDataTransaction extends Transaction {
private final UUID serverUUID;
+ private final long deleteTPSOlderThanMs;
+ private final long deletePingOlderThanMs;
public RemoveOldSampledDataTransaction(
- UUID serverUUID
+ UUID serverUUID,
+ long deleteTPSOlderThanMs,
+ long deletePingOlderThanMs
) {
this.serverUUID = serverUUID;
+ this.deleteTPSOlderThanMs = deleteTPSOlderThanMs;
+ this.deletePingOlderThanMs = deletePingOlderThanMs;
}
@Override
@@ -61,9 +66,7 @@ public class RemoveOldSampledDataTransaction extends Transaction {
return new ExecStatement(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
- // More than 3 Months ago.
- long threeMonths = TimeAmount.MONTH.toMillis(3L);
- statement.setLong(1, System.currentTimeMillis() - threeMonths);
+ statement.setLong(1, System.currentTimeMillis() - deleteTPSOlderThanMs);
statement.setInt(2, allTimePlayerPeak);
}
};
@@ -77,8 +80,7 @@ public class RemoveOldSampledDataTransaction extends Transaction {
return new ExecStatement(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
- long twoWeeks = TimeAmount.WEEK.toMillis(2L);
- statement.setLong(1, System.currentTimeMillis() - twoWeeks);
+ statement.setLong(1, System.currentTimeMillis() - deletePingOlderThanMs);
}
};
}
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/DeleteIPHashesPatch.java b/Plan/common/src/main/java/com/djrapitops/plan/db/patches/DeleteIPHashesPatch.java
new file mode 100644
index 000000000..b449a317f
--- /dev/null
+++ b/Plan/common/src/main/java/com/djrapitops/plan/db/patches/DeleteIPHashesPatch.java
@@ -0,0 +1,73 @@
+/*
+ * 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 GNU 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
+ * GNU 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 .
+ */
+package com.djrapitops.plan.db.patches;
+
+import com.djrapitops.plan.db.access.ExecStatement;
+import com.djrapitops.plan.db.access.HasMoreThanZeroQueryStatement;
+import com.djrapitops.plan.db.sql.tables.GeoInfoTable;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Types;
+
+import static com.djrapitops.plan.db.sql.parsing.Sql.*;
+
+/**
+ * Patch for removing ip_hash values from plan_ips table.
+ *
+ * The patch is a response to a concern:
+ * "Hashed IP addresses are pseudonymised not anonymised and can be easily decoded using a rainbow table".
+ *
+ * @author Rsl1122
+ */
+public class DeleteIPHashesPatch extends Patch {
+
+ private static final String IP_HASH = "ip_hash";
+
+ private boolean hasNoHashColumn;
+
+ @Override
+ public boolean hasBeenApplied() {
+ hasNoHashColumn = !hasColumn(GeoInfoTable.TABLE_NAME, IP_HASH);
+
+ String sql = SELECT + "COUNT(1) as c" + FROM + GeoInfoTable.TABLE_NAME +
+ WHERE + IP_HASH + IS_NOT_NULL;
+
+ return hasNoHashColumn || !query(new HasMoreThanZeroQueryStatement(sql) {
+ @Override
+ public void prepare(PreparedStatement statement) {
+ /* No variables needed */
+ }
+ });
+ }
+
+ @Override
+ protected void applyPatch() {
+ if (hasNoHashColumn) {
+ return;
+ }
+
+ String sql = "UPDATE " + GeoInfoTable.TABLE_NAME + " SET ip_hash=?" + WHERE + IP_HASH + IS_NOT_NULL;
+ execute(new ExecStatement(sql) {
+ @Override
+ public void prepare(PreparedStatement statement) throws SQLException {
+ statement.setNull(1, Types.VARCHAR);
+ }
+ });
+ }
+
+}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/GeoInfoOptimizationPatch.java b/Plan/common/src/main/java/com/djrapitops/plan/db/patches/GeoInfoOptimizationPatch.java
index 7a1eb2cfc..38438a4b3 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/GeoInfoOptimizationPatch.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/db/patches/GeoInfoOptimizationPatch.java
@@ -44,13 +44,11 @@ public class GeoInfoOptimizationPatch extends Patch {
execute("INSERT INTO " + tableName + " (" +
GeoInfoTable.USER_UUID + ", " +
GeoInfoTable.IP + ", " +
- GeoInfoTable.IP_HASH + ", " +
GeoInfoTable.LAST_USED + ", " +
GeoInfoTable.GEOLOCATION +
") SELECT " +
"(SELECT plan_users.uuid FROM plan_users WHERE plan_users.id = " + tempTableName + ".user_id LIMIT 1), " +
GeoInfoTable.IP + ", " +
- GeoInfoTable.IP_HASH + ", " +
GeoInfoTable.LAST_USED + ", " +
GeoInfoTable.GEOLOCATION +
" FROM " + tempTableName
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/IPAnonPatch.java b/Plan/common/src/main/java/com/djrapitops/plan/db/patches/IPAnonPatch.java
index 99f910bca..7ca218142 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/IPAnonPatch.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/db/patches/IPAnonPatch.java
@@ -24,7 +24,6 @@ import com.djrapitops.plan.db.sql.tables.GeoInfoTable;
import java.net.InetAddress;
import java.net.UnknownHostException;
-import java.security.NoSuchAlgorithmException;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@@ -73,8 +72,7 @@ public class IPAnonPatch extends Patch {
private void anonymizeIPs(Map> allGeoInfo) {
String sql = "UPDATE " + GeoInfoTable.TABLE_NAME + " SET " +
- GeoInfoTable.IP + "=?, " +
- GeoInfoTable.IP_HASH + "=? " +
+ GeoInfoTable.IP + "=? " +
"WHERE " + GeoInfoTable.IP + "=?";
execute(new ExecBatchStatement(sql) {
@@ -99,11 +97,10 @@ public class IPAnonPatch extends Patch {
geoInfo.getDate()
);
statement.setString(1, updatedInfo.getIp());
- statement.setString(2, updatedInfo.getIpHash());
- statement.setString(3, geoInfo.getIp());
+ statement.setString(2, geoInfo.getIp());
statement.addBatch();
- } catch (UnknownHostException | NoSuchAlgorithmException ignore) {
- // This ip is already anonymised or completely unusable.
+ } catch (UnknownHostException ignore) {
+ // This ip is completely unusable.
}
}
});
@@ -120,11 +117,11 @@ public class IPAnonPatch extends Patch {
String identifiers = hasUserIdColumn ? userIdColumn : "id, uuid";
execute("INSERT INTO plan_ips (" +
- identifiers + ", ip, ip_hash, geolocation, last_used" +
+ identifiers + ", ip, geolocation, last_used" +
") SELECT " +
- identifiers + ", ip, ip_hash, geolocation, MAX(last_used) FROM plan_ips_temp GROUP BY ip_hash, " +
+ identifiers + ", ip, geolocation, MAX(last_used) FROM plan_ips_temp GROUP BY ip, " +
(hasUserIdColumn ? userIdColumn : "uuid") +
- ", ip, geolocation");
+ ", geolocation");
dropTable(tempTableName);
}
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionIconTable.java b/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionIconTable.java
index 5407e884f..3cb91e5fa 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionIconTable.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionIconTable.java
@@ -26,6 +26,7 @@ import org.apache.commons.lang3.StringUtils;
import java.sql.PreparedStatement;
import java.sql.SQLException;
+import java.sql.Types;
import static com.djrapitops.plan.db.sql.parsing.Sql.*;
@@ -54,9 +55,15 @@ public class ExtensionIconTable {
}
public static void set3IconValuesToStatement(PreparedStatement statement, int parameterIndex, Icon icon) throws SQLException {
- statement.setString(parameterIndex, StringUtils.truncate(icon.getName(), 50));
- statement.setString(parameterIndex + 1, icon.getFamily().name());
- statement.setString(parameterIndex + 2, icon.getColor().name());
+ if (icon != null) {
+ statement.setString(parameterIndex, StringUtils.truncate(icon.getName(), 50));
+ statement.setString(parameterIndex + 1, icon.getFamily().name());
+ statement.setString(parameterIndex + 2, icon.getColor().name());
+ } else {
+ statement.setNull(parameterIndex, Types.VARCHAR);
+ statement.setNull(parameterIndex + 1, Types.VARCHAR);
+ statement.setNull(parameterIndex + 2, Types.VARCHAR);
+ }
}
private ExtensionIconTable() {
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionPlayerTableValueTable.java b/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionPlayerTableValueTable.java
new file mode 100644
index 000000000..eb7f4c041
--- /dev/null
+++ b/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionPlayerTableValueTable.java
@@ -0,0 +1,60 @@
+/*
+ * 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 GNU 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
+ * GNU 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 .
+ */
+package com.djrapitops.plan.db.sql.tables;
+
+import com.djrapitops.plan.db.DBType;
+import com.djrapitops.plan.db.sql.parsing.CreateTableParser;
+import com.djrapitops.plan.db.sql.parsing.Sql;
+
+import static com.djrapitops.plan.db.sql.parsing.Sql.INT;
+
+/**
+ * Table information about 'plan_extension_user_table_values'.
+ *
+ * @author Rsl1122
+ */
+public class ExtensionPlayerTableValueTable {
+
+ public static final String TABLE_NAME = "plan_extension_user_table_values";
+
+ public static final String ID = "id";
+ public static final String TABLE_ID = "table_id";
+ public static final String USER_UUID = "uuid";
+
+ // All values can be null
+ public static final String VALUE_1 = "col_1_value";
+ public static final String VALUE_2 = "col_2_value";
+ public static final String VALUE_3 = "col_3_value";
+ public static final String VALUE_4 = "col_4_value";
+
+ private ExtensionPlayerTableValueTable() {
+ /* Static information class */
+ }
+
+ public static String createTableSQL(DBType dbType) {
+ return CreateTableParser.create(TABLE_NAME, dbType)
+ .column(ID, INT).primaryKey()
+ .column(USER_UUID, Sql.varchar(36)).notNull()
+ .column(VALUE_1, Sql.varchar(50))
+ .column(VALUE_2, Sql.varchar(50))
+ .column(VALUE_3, Sql.varchar(50))
+ .column(VALUE_4, Sql.varchar(50))
+ .column(TABLE_ID, INT).notNull()
+ .foreignKey(TABLE_ID, ExtensionTableProviderTable.TABLE_NAME, ExtensionPluginTable.ID)
+ .build();
+ }
+}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionProviderTable.java b/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionProviderTable.java
index 1f9c08d50..cb14fad5e 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionProviderTable.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionProviderTable.java
@@ -37,7 +37,7 @@ public class ExtensionProviderTable {
public static final String ID = "id";
public static final String PROVIDER_NAME = "name";
- public static final String TEXT = "text"; // Can be null
+ public static final String TEXT = "text";
public static final String DESCRIPTION = "description"; // Can be null
public static final String PRIORITY = "priority";
public static final String GROUPABLE = "groupable"; // default false
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionServerTableValueTable.java b/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionServerTableValueTable.java
new file mode 100644
index 000000000..8d28a4817
--- /dev/null
+++ b/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionServerTableValueTable.java
@@ -0,0 +1,62 @@
+/*
+ * 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 GNU 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
+ * GNU 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 .
+ */
+package com.djrapitops.plan.db.sql.tables;
+
+import com.djrapitops.plan.db.DBType;
+import com.djrapitops.plan.db.sql.parsing.CreateTableParser;
+import com.djrapitops.plan.db.sql.parsing.Sql;
+
+import static com.djrapitops.plan.db.sql.parsing.Sql.INT;
+
+/**
+ * Table information about 'plan_extension_server_table_values'.
+ *
+ * @author Rsl1122
+ */
+public class ExtensionServerTableValueTable {
+
+ public static final String TABLE_NAME = "plan_extension_server_table_values";
+
+ public static final String ID = "id";
+ public static final String TABLE_ID = "table_id";
+ public static final String SERVER_UUID = "uuid";
+
+ // All values can be null
+ public static final String VALUE_1 = "col_1_value";
+ public static final String VALUE_2 = "col_2_value";
+ public static final String VALUE_3 = "col_3_value";
+ public static final String VALUE_4 = "col_4_value";
+ public static final String VALUE_5 = "col_5_value";
+
+ private ExtensionServerTableValueTable() {
+ /* Static information class */
+ }
+
+ public static String createTableSQL(DBType dbType) {
+ return CreateTableParser.create(TABLE_NAME, dbType)
+ .column(ID, INT).primaryKey()
+ .column(SERVER_UUID, Sql.varchar(36)).notNull()
+ .column(VALUE_1, Sql.varchar(50))
+ .column(VALUE_2, Sql.varchar(50))
+ .column(VALUE_3, Sql.varchar(50))
+ .column(VALUE_4, Sql.varchar(50))
+ .column(VALUE_5, Sql.varchar(50))
+ .column(TABLE_ID, INT).notNull()
+ .foreignKey(TABLE_ID, ExtensionTableProviderTable.TABLE_NAME, ExtensionPluginTable.ID)
+ .build();
+ }
+}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionTableProviderTable.java b/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionTableProviderTable.java
new file mode 100644
index 000000000..99ae732e4
--- /dev/null
+++ b/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionTableProviderTable.java
@@ -0,0 +1,100 @@
+/*
+ * 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 GNU 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
+ * GNU 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 .
+ */
+package com.djrapitops.plan.db.sql.tables;
+
+import com.djrapitops.plan.db.DBType;
+import com.djrapitops.plan.db.sql.parsing.CreateTableParser;
+import com.djrapitops.plan.db.sql.parsing.Sql;
+import com.djrapitops.plan.extension.icon.Color;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.UUID;
+
+import static com.djrapitops.plan.db.sql.parsing.Sql.*;
+
+/**
+ * Table information about 'plan_extension_tables'.
+ *
+ * @author Rsl1122
+ */
+public class ExtensionTableProviderTable {
+
+ public static final String TABLE_NAME = "plan_extension_tables";
+
+ public static final String ID = "id";
+ public static final String PROVIDER_NAME = "name";
+ public static final String COLOR = "color";
+ public static final String CONDITION = "condition_name"; // Can be null, related to @Conditional
+ public static final String PLUGIN_ID = "plugin_id";
+ public static final String TAB_ID = "tab_id"; // Can be null, related to @Tab
+
+ // All columns can be null
+ public static final String COL_1 = "col_1_name";
+ public static final String COL_2 = "col_2_name";
+ public static final String COL_3 = "col_3_name";
+ public static final String COL_4 = "col_4_name";
+ public static final String COL_5 = "col_5_name";
+
+ // All icons can be null
+ public static final String ICON_1_ID = "icon_1_id";
+ public static final String ICON_2_ID = "icon_2_id";
+ public static final String ICON_3_ID = "icon_3_id";
+ public static final String ICON_4_ID = "icon_4_id";
+ public static final String ICON_5_ID = "icon_5_id";
+
+ public static final String STATEMENT_SELECT_TABLE_ID = "(" + SELECT + ID + FROM + TABLE_NAME +
+ WHERE + PROVIDER_NAME + "=?" +
+ AND + PLUGIN_ID + "=" + ExtensionPluginTable.STATEMENT_SELECT_PLUGIN_ID + ")";
+
+ private ExtensionTableProviderTable() {
+ /* Static information class */
+ }
+
+ public static void set3PluginValuesToStatement(PreparedStatement statement, int parameterIndex, String providerName, String pluginName, UUID serverUUID) throws SQLException {
+ statement.setString(parameterIndex, providerName);
+ ExtensionPluginTable.set2PluginValuesToStatement(statement, parameterIndex + 1, pluginName, serverUUID);
+ }
+
+ public static String createTableSQL(DBType dbType) {
+ return CreateTableParser.create(TABLE_NAME, dbType)
+ .column(ID, INT).primaryKey()
+ .column(PROVIDER_NAME, Sql.varchar(50)).notNull()
+ .column(COLOR, Sql.varchar(25)).notNull().defaultValue("'" + Color.NONE.name() + "'")
+ .column(CONDITION, Sql.varchar(54)) // 50 + 4 for "not_"
+ .column(COL_1, Sql.varchar(50))
+ .column(COL_2, Sql.varchar(50))
+ .column(COL_3, Sql.varchar(50))
+ .column(COL_4, Sql.varchar(50))
+ .column(COL_5, Sql.varchar(50))
+ .column(PLUGIN_ID, INT).notNull()
+ .column(ICON_1_ID, INT)
+ .column(ICON_2_ID, INT)
+ .column(ICON_3_ID, INT)
+ .column(ICON_4_ID, INT)
+ .column(ICON_5_ID, INT)
+ .column(TAB_ID, INT)
+ .foreignKey(PLUGIN_ID, ExtensionPluginTable.TABLE_NAME, ExtensionPluginTable.ID)
+ .foreignKey(ICON_1_ID, ExtensionIconTable.TABLE_NAME, ExtensionIconTable.ID)
+ .foreignKey(ICON_2_ID, ExtensionIconTable.TABLE_NAME, ExtensionIconTable.ID)
+ .foreignKey(ICON_3_ID, ExtensionIconTable.TABLE_NAME, ExtensionIconTable.ID)
+ .foreignKey(ICON_4_ID, ExtensionIconTable.TABLE_NAME, ExtensionIconTable.ID)
+ .foreignKey(ICON_5_ID, ExtensionIconTable.TABLE_NAME, ExtensionIconTable.ID)
+ .foreignKey(TAB_ID, ExtensionTabTable.TABLE_NAME, ExtensionTabTable.ID)
+ .build();
+ }
+}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/GeoInfoTable.java b/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/GeoInfoTable.java
index f45ae84cc..850fc4b64 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/GeoInfoTable.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/GeoInfoTable.java
@@ -28,8 +28,8 @@ import com.djrapitops.plan.db.sql.parsing.Sql;
* {@link Version10Patch}
* {@link GeoInfoLastUsedPatch}
* {@link IPAnonPatch}
- * {@link IPHashPatch}
* {@link GeoInfoOptimizationPatch}
+ * {@link DeleteIPHashesPatch}
*
* @author Rsl1122
*/
@@ -40,22 +40,19 @@ public class GeoInfoTable {
public static final String ID = "id";
public static final String USER_UUID = "uuid";
public static final String IP = "ip";
- public static final String IP_HASH = "ip_hash";
public static final String GEOLOCATION = "geolocation";
public static final String LAST_USED = "last_used";
public static final String INSERT_STATEMENT = "INSERT INTO " + TABLE_NAME + " ("
+ USER_UUID + ", "
+ IP + ", "
- + IP_HASH + ", "
+ GEOLOCATION + ", "
+ LAST_USED
- + ") VALUES (?, ?, ?, ?, ?)";
+ + ") VALUES (?, ?, ?, ?)";
public static final String UPDATE_STATEMENT = "UPDATE " + TABLE_NAME + " SET "
+ LAST_USED + "=?" +
" WHERE " + USER_UUID + "=?" +
- " AND " + IP_HASH + "=?" +
" AND " + GEOLOCATION + "=?";
private GeoInfoTable() {
@@ -68,7 +65,6 @@ public class GeoInfoTable {
.column(USER_UUID, Sql.varchar(36)).notNull()
.column(IP, Sql.varchar(39)).notNull()
.column(GEOLOCATION, Sql.varchar(50)).notNull()
- .column(IP_HASH, Sql.varchar(200))
.column(LAST_USED, Sql.LONG).notNull().defaultValue("0")
.toString();
}
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/tasks/DBCleanTask.java b/Plan/common/src/main/java/com/djrapitops/plan/db/tasks/DBCleanTask.java
index d2253d13f..f2c5fa681 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/db/tasks/DBCleanTask.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/db/tasks/DBCleanTask.java
@@ -24,7 +24,8 @@ import com.djrapitops.plan.db.access.transactions.commands.RemovePlayerTransacti
import com.djrapitops.plan.db.access.transactions.init.RemoveDuplicateUserInfoTransaction;
import com.djrapitops.plan.db.access.transactions.init.RemoveOldSampledDataTransaction;
import com.djrapitops.plan.db.sql.tables.SessionsTable;
-import com.djrapitops.plan.extension.implementation.storage.transactions.results.RemoveUnsatisfiedConditionalResultsTransaction;
+import com.djrapitops.plan.extension.implementation.storage.transactions.results.RemoveUnsatisfiedConditionalPlayerResultsTransaction;
+import com.djrapitops.plan.extension.implementation.storage.transactions.results.RemoveUnsatisfiedConditionalServerResultsTransaction;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.locale.Locale;
@@ -84,9 +85,14 @@ public class DBCleanTask extends AbsRunnable {
Database database = dbSystem.getDatabase();
try {
if (database.getState() != Database.State.CLOSED) {
- database.executeTransaction(new RemoveOldSampledDataTransaction(serverInfo.getServerUUID()));
+ database.executeTransaction(new RemoveOldSampledDataTransaction(
+ serverInfo.getServerUUID(),
+ config.get(TimeSettings.DELETE_TPS_DATA_AFTER),
+ config.get(TimeSettings.DELETE_PING_DATA_AFTER)
+ ));
database.executeTransaction(new RemoveDuplicateUserInfoTransaction());
- database.executeTransaction(new RemoveUnsatisfiedConditionalResultsTransaction());
+ database.executeTransaction(new RemoveUnsatisfiedConditionalPlayerResultsTransaction());
+ database.executeTransaction(new RemoveUnsatisfiedConditionalServerResultsTransaction());
int removed = cleanOldPlayers(database);
if (removed > 0) {
logger.info(locale.getString(PluginLang.DB_NOTIFY_CLEAN, removed));
@@ -101,7 +107,7 @@ public class DBCleanTask extends AbsRunnable {
@VisibleForTesting
public int cleanOldPlayers(Database database) {
long now = System.currentTimeMillis();
- long keepActiveAfter = now - config.get(TimeSettings.KEEP_INACTIVE_PLAYERS);
+ long keepActiveAfter = now - config.get(TimeSettings.DELETE_INACTIVE_PLAYERS_AFTER);
List inactivePlayers = database.query(fetchInactivePlayerUUIDs(keepActiveAfter));
for (UUID uuid : inactivePlayers) {
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/ExtensionServiceImplementation.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/ExtensionServiceImplementation.java
index 0ca7d16eb..9a84eeaad 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/extension/ExtensionServiceImplementation.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/ExtensionServiceImplementation.java
@@ -16,6 +16,7 @@
*/
package com.djrapitops.plan.extension;
+import com.djrapitops.plan.api.exceptions.DataExtensionMethodCallException;
import com.djrapitops.plan.data.plugin.PluginsConfigSection;
import com.djrapitops.plan.extension.implementation.CallerImplementation;
import com.djrapitops.plan.extension.implementation.DataProviderExtractor;
@@ -94,11 +95,11 @@ public class ExtensionServiceImplementation implements ExtensionService {
logger.warn("DataExtension API implementation mistake for " + pluginName + ": " + warning);
}
- ProviderValueGatherer gatherer = new ProviderValueGatherer(extension, extractor, dbSystem, serverInfo, logger);
+ ProviderValueGatherer gatherer = new ProviderValueGatherer(extension, extractor, dbSystem, serverInfo);
gatherer.storeExtensionInformation();
extensionGatherers.put(pluginName, gatherer);
- updateServerValues(gatherer, CallEvents.SERVER_EXTENSION_REGISTER);
+ processing.submitNonCritical(() -> updateServerValues(gatherer, CallEvents.SERVER_EXTENSION_REGISTER));
logger.getDebugLogger().logOn(DebugChannels.DATA_EXTENSIONS, pluginName + " extension registered.");
return Optional.of(new CallerImplementation(gatherer, this, processing));
@@ -111,7 +112,6 @@ public class ExtensionServiceImplementation implements ExtensionService {
if (extensionGatherers.remove(pluginName) != null) {
logger.getDebugLogger().logOn(DebugChannels.DATA_EXTENSIONS, pluginName + " extension unregistered.");
}
-
}
private boolean shouldNotAllowRegistration(String pluginName) {
@@ -150,16 +150,29 @@ public class ExtensionServiceImplementation implements ExtensionService {
gatherer.updateValues(playerUUID, playerName);
logger.getDebugLogger().logOn(DebugChannels.DATA_EXTENSIONS, "Gathering completed: " + playerName);
- } catch (Exception | NoClassDefFoundError | NoSuchMethodError | NoSuchFieldError e) {
- logger.warn(gatherer.getPluginName() + " ran into (but failed safely) " + e.getClass().getSimpleName() +
- " when updating value for '" + playerName +
- "', (You can disable integration with setting 'Plugins." + gatherer.getPluginName() + ".Enabled')" +
- " reason: '" + e.getMessage() +
+ } catch (DataExtensionMethodCallException methodCallFailed) {
+ logFailure(playerName, methodCallFailed);
+ gatherer.disableMethodFromUse(methodCallFailed.getMethod());
+ // Try again
+ updatePlayerValues(gatherer, playerUUID, playerName, event);
+ } catch (Exception | NoClassDefFoundError | NoSuchFieldError | NoSuchMethodError unexpectedError) {
+ logger.warn(gatherer.getPluginName() + " ran into unexpected error (please report this)" + unexpectedError +
+ " (but failed safely) when updating value for '" + playerName +
"', stack trace to follow:");
- errorHandler.log(L.WARN, gatherer.getClass(), e);
+ errorHandler.log(L.WARN, gatherer.getClass(), unexpectedError);
}
}
+ private void logFailure(String playerName, DataExtensionMethodCallException methodCallFailed) {
+ Throwable cause = methodCallFailed.getCause();
+ String causeName = cause.getClass().getSimpleName();
+ logger.warn(methodCallFailed.getPluginName() + " ran into " + causeName +
+ " (but failed safely) when updating value for '" + playerName +
+ "', the method was disabled temporarily (won't be called until next Plan reload)" +
+ ", stack trace to follow:");
+ errorHandler.log(L.WARN, getClass(), cause);
+ }
+
public void updateServerValues(CallEvents event) {
for (ProviderValueGatherer gatherer : extensionGatherers.values()) {
updateServerValues(gatherer, event);
@@ -176,13 +189,15 @@ public class ExtensionServiceImplementation implements ExtensionService {
gatherer.updateValues();
logger.getDebugLogger().logOn(DebugChannels.DATA_EXTENSIONS, "Gathering completed for server");
- } catch (Exception | NoClassDefFoundError | NoSuchMethodError | NoSuchFieldError e) {
- logger.warn(gatherer.getPluginName() + " ran into (but failed safely) " + e.getClass().getSimpleName() +
- " when updating value for server" +
- ", (You can disable integration with setting 'Plugins." + gatherer.getPluginName() + ".Enabled')" +
- " reason: '" + e.getMessage() +
- "', stack trace to follow:");
- errorHandler.log(L.WARN, gatherer.getClass(), e);
+ } catch (DataExtensionMethodCallException methodCallFailed) {
+ logFailure("server", methodCallFailed);
+ gatherer.disableMethodFromUse(methodCallFailed.getMethod());
+ // Try again
+ updateServerValues(gatherer, event);
+ } catch (Exception | NoClassDefFoundError | NoSuchFieldError | NoSuchMethodError unexpectedError) {
+ logger.warn(gatherer.getPluginName() + " ran into unexpected error (please report this)" + unexpectedError +
+ " (but failed safely) when updating value for server, stack trace to follow:");
+ errorHandler.log(L.WARN, gatherer.getClass(), unexpectedError);
}
}
}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/CallerImplementation.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/CallerImplementation.java
index e3f526c99..fa05d4516 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/CallerImplementation.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/CallerImplementation.java
@@ -47,7 +47,7 @@ public class CallerImplementation implements Caller {
}
@Override
- public void updatePlayerData(UUID playerUUID, String playerName) throws IllegalArgumentException {
+ public void updatePlayerData(UUID playerUUID, String playerName) {
Verify.nullCheck(playerUUID, () -> new IllegalArgumentException("'playerUUID' can not be null!"));
Verify.nullCheck(playerName, () -> new IllegalArgumentException("'playerName' can not be null!"));
processing.submitNonCritical(() -> extensionServiceImplementation.updatePlayerValues(gatherer, playerUUID, playerName, CallEvents.MANUAL));
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/DataProviderExtractor.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/DataProviderExtractor.java
index 892248dac..d3aa47fa2 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/DataProviderExtractor.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/DataProviderExtractor.java
@@ -124,6 +124,7 @@ public class DataProviderExtractor {
extractDataProviders(pluginInfo, tabs, conditions, PercentageProvider.class, PercentageDataProvider::placeToDataProviders);
extractDataProviders(pluginInfo, tabs, conditions, NumberProvider.class, NumberDataProvider::placeToDataProviders);
extractDataProviders(pluginInfo, tabs, conditions, StringProvider.class, StringDataProvider::placeToDataProviders);
+ extractDataProviders(pluginInfo, tabs, conditions, TableProvider.class, TableDataProvider::placeToDataProviders);
}
private void extractDataProviders(PluginInfo pluginInfo, Map tabs, Map conditions, Class ofKind, DataProviderFactory factory) {
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/ProviderInformation.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/ProviderInformation.java
index 4ca642e15..0e446f422 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/ProviderInformation.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/ProviderInformation.java
@@ -21,6 +21,7 @@ import com.djrapitops.plan.extension.icon.Icon;
import com.djrapitops.plan.extension.implementation.results.ExtensionDescriptive;
import org.apache.commons.lang3.StringUtils;
+import java.util.Objects;
import java.util.Optional;
/**
@@ -48,10 +49,38 @@ public class ProviderInformation extends ExtensionDescriptive {
}
public Optional getTab() {
- return tab == null || tab.isEmpty() ? Optional.empty() : Optional.of(StringUtils.truncate(tab, 50));
+ return tab == null || tab.isEmpty()
+ ? Optional.empty()
+ : Optional.of(StringUtils.truncate(tab, 50));
}
public Optional getCondition() {
- return condition == null || condition.value().isEmpty() ? Optional.empty() : Optional.of((condition.negated() ? "not_" : "") + StringUtils.truncate(condition.value(), 50));
+ if (condition == null || condition.value().isEmpty()) {
+ return Optional.empty();
+ } else if (condition.negated()) {
+ return Optional.of("not_" + getTruncatedConditionName());
+ } else {
+ return Optional.of(getTruncatedConditionName());
+ }
+ }
+
+ private String getTruncatedConditionName() {
+ return StringUtils.truncate(condition.value(), 50);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof ProviderInformation)) return false;
+ if (!super.equals(o)) return false;
+ ProviderInformation that = (ProviderInformation) o;
+ return pluginName.equals(that.pluginName) &&
+ Objects.equals(tab, that.tab) &&
+ Objects.equals(condition, that.condition);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), pluginName, tab, condition);
}
}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/DataProviders.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/DataProviders.java
index 99ab3cf4b..f0cc6ecba 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/DataProviders.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/DataProviders.java
@@ -19,6 +19,7 @@ package com.djrapitops.plan.extension.implementation.providers;
import com.djrapitops.plan.extension.implementation.MethodType;
import java.util.*;
+import java.util.stream.Collectors;
/**
* Group class for handling multiple different types of {@link DataProvider}s.
@@ -77,4 +78,24 @@ public class DataProviders {
}
return byReturnType;
}
+
+ public void removeProviderWithMethod(MethodWrapper method) {
+ MethodType methodType = method.getMethodType();
+ Map> byResultType = byMethodType.getOrDefault(methodType, Collections.emptyMap());
+ if (byResultType.isEmpty()) {
+ return;
+ }
+
+ Class resultType = method.getResultType();
+ List providers = byResultType.getOrDefault(resultType, Collections.emptyList());
+ if (providers.isEmpty()) {
+ return;
+ }
+
+ byResultType.put(resultType, providers.stream()
+ .filter(provider -> provider.getMethod().equals(method))
+ .collect(Collectors.toList())
+ );
+ byMethodType.put(methodType, byResultType);
+ }
}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/MethodWrapper.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/MethodWrapper.java
index 4889e80b4..358d8795f 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/MethodWrapper.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/MethodWrapper.java
@@ -20,8 +20,10 @@ import com.djrapitops.plan.extension.DataExtension;
import com.djrapitops.plan.extension.Group;
import com.djrapitops.plan.extension.implementation.MethodType;
+import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+import java.util.Objects;
import java.util.UUID;
/**
@@ -29,7 +31,7 @@ import java.util.UUID;
*
* @author Rsl1122
*/
-public class MethodWrapper {
+public class MethodWrapper implements Serializable {
private final Method method;
private final Class resultType;
@@ -41,39 +43,46 @@ public class MethodWrapper {
methodType = MethodType.forMethod(this.method);
}
- public T callMethod(DataExtension extension, UUID playerUUID, String playerName) throws InvocationTargetException, IllegalAccessException {
+ public T callMethod(DataExtension extension, UUID playerUUID, String playerName) {
if (methodType != MethodType.PLAYER_NAME && methodType != MethodType.PLAYER_UUID) {
throw new IllegalStateException(method.getDeclaringClass() + " method " + method.getName() + " is not GROUP method.");
}
return callMethod(extension, playerUUID, playerName, null);
}
- public T callMethod(DataExtension extension, Group group) throws InvocationTargetException, IllegalAccessException {
+ public T callMethod(DataExtension extension, Group group) {
if (methodType != MethodType.GROUP) {
throw new IllegalStateException(method.getDeclaringClass() + " method " + method.getName() + " is not GROUP method.");
}
return callMethod(extension, null, null, group);
}
- public T callMethod(DataExtension extension) throws InvocationTargetException, IllegalAccessException {
+ public T callMethod(DataExtension extension) {
if (methodType != MethodType.SERVER) {
throw new IllegalStateException(method.getDeclaringClass() + " method " + method.getName() + " is not SERVER method.");
}
return callMethod(extension, null, null, null);
}
- public T callMethod(DataExtension extension, UUID playerUUID, String playerName, Group group) throws InvocationTargetException, IllegalAccessException {
- switch (methodType) {
- case SERVER:
- return resultType.cast(method.invoke(extension));
- case PLAYER_UUID:
- return resultType.cast(method.invoke(extension, playerUUID));
- case PLAYER_NAME:
- return resultType.cast(method.invoke(extension, playerName));
- case GROUP:
- return resultType.cast(method.invoke(extension, group));
- default:
- throw new IllegalArgumentException(method.getDeclaringClass() + " method " + method.getName() + " had invalid parameters.");
+ public T callMethod(DataExtension extension, UUID playerUUID, String playerName, Group group) {
+ try {
+ switch (methodType) {
+ case SERVER:
+ return resultType.cast(method.invoke(extension));
+ case PLAYER_UUID:
+ return resultType.cast(method.invoke(extension, playerUUID));
+ case PLAYER_NAME:
+ return resultType.cast(method.invoke(extension, playerName));
+ case GROUP:
+ return resultType.cast(method.invoke(extension, group));
+ default:
+ throw new IllegalArgumentException(method.getDeclaringClass() + " method " + method.getName() + " had invalid parameters.");
+ }
+ } catch (InvocationTargetException | IllegalAccessException e) {
+ Throwable cause = e.getCause();
+ boolean hasCause = cause != null;
+ throw new IllegalArgumentException(method.getDeclaringClass() + " method " + method.getName() + " had invalid parameters; caused " +
+ (hasCause ? cause.toString() : e.toString()));
}
}
@@ -88,4 +97,19 @@ public class MethodWrapper {
public Class getResultType() {
return resultType;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof MethodWrapper)) return false;
+ MethodWrapper> that = (MethodWrapper>) o;
+ return method.equals(that.method) &&
+ resultType.equals(that.resultType) &&
+ methodType == that.methodType;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(method, resultType, methodType);
+ }
}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/TableDataProvider.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/TableDataProvider.java
new file mode 100644
index 000000000..77150fe6b
--- /dev/null
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/TableDataProvider.java
@@ -0,0 +1,69 @@
+/*
+ * 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 GNU 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
+ * GNU 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 .
+ */
+package com.djrapitops.plan.extension.implementation.providers;
+
+import com.djrapitops.plan.extension.annotation.Conditional;
+import com.djrapitops.plan.extension.annotation.TableProvider;
+import com.djrapitops.plan.extension.icon.Color;
+import com.djrapitops.plan.extension.implementation.ProviderInformation;
+import com.djrapitops.plan.extension.table.Table;
+
+import java.lang.reflect.Method;
+
+/**
+ * Represents a DataExtension API method annotated with {@link com.djrapitops.plan.extension.annotation.TableProvider} annotation.
+ *
+ * Used to obtain data to place in the database.
+ *
+ * Please note that not all {@link ProviderInformation} is present.
+ *
+ * @author Rsl1122
+ */
+public class TableDataProvider extends DataProvider
{
+
+ private final Color tableColor;
+
+ private TableDataProvider(ProviderInformation providerInformation, MethodWrapper methodWrapper, Color tableColor) {
+ super(providerInformation, methodWrapper);
+
+ this.tableColor = tableColor;
+ }
+
+ public static void placeToDataProviders(
+ DataProviders dataProviders, Method method, TableProvider annotation,
+ Conditional condition, String tab, String pluginName
+ ) {
+ MethodWrapper methodWrapper = new MethodWrapper<>(method, Table.class);
+
+ ProviderInformation providerInformation = new ProviderInformation(
+ pluginName, method.getName(), null, null, null, 0, tab, condition
+ );
+
+ dataProviders.put(new TableDataProvider(providerInformation, methodWrapper, annotation.tableColor()));
+ }
+
+ public static Color getTableColor(DataProvider provider) {
+ if (provider instanceof TableDataProvider) {
+ return ((TableDataProvider) provider).getTableColor();
+ }
+ return Color.NONE;
+ }
+
+ public Color getTableColor() {
+ return tableColor;
+ }
+}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/BooleanProviderValueGatherer.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/BooleanProviderValueGatherer.java
index 2d73be796..be1112abf 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/BooleanProviderValueGatherer.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/BooleanProviderValueGatherer.java
@@ -16,6 +16,7 @@
*/
package com.djrapitops.plan.extension.implementation.providers.gathering;
+import com.djrapitops.plan.api.exceptions.DataExtensionMethodCallException;
import com.djrapitops.plan.db.Database;
import com.djrapitops.plan.db.access.transactions.Transaction;
import com.djrapitops.plan.extension.DataExtension;
@@ -28,7 +29,6 @@ import com.djrapitops.plan.extension.implementation.storage.transactions.StoreIc
import com.djrapitops.plan.extension.implementation.storage.transactions.providers.StoreBooleanProviderTransaction;
import com.djrapitops.plan.extension.implementation.storage.transactions.results.StorePlayerBooleanResultTransaction;
import com.djrapitops.plan.extension.implementation.storage.transactions.results.StoreServerBooleanResultTransaction;
-import com.djrapitops.plugin.logging.console.PluginLogger;
import java.util.*;
import java.util.concurrent.Callable;
@@ -48,20 +48,17 @@ class BooleanProviderValueGatherer {
private final Database database;
private final DataProviders dataProviders;
- private final PluginLogger logger;
BooleanProviderValueGatherer(
String pluginName, DataExtension extension,
UUID serverUUID, Database database,
- DataProviders dataProviders,
- PluginLogger logger
+ DataProviders dataProviders
) {
this.pluginName = pluginName;
this.extension = extension;
this.serverUUID = serverUUID;
this.database = database;
this.dataProviders = dataProviders;
- this.logger = logger;
}
Conditions gatherBooleanDataOfPlayer(UUID playerUUID, String playerName) {
@@ -127,10 +124,7 @@ class BooleanProviderValueGatherer {
boolean hidden = BooleanDataProvider.isHidden(booleanProvider);
MethodWrapper method = booleanProvider.getMethod();
- Boolean result = getMethodResult(
- methodCaller.apply(method),
- throwable -> pluginName + " has invalid implementation, method " + method.getMethodName() + " threw exception: " + throwable.toString()
- );
+ Boolean result = getMethodResult(methodCaller.apply(method), method);
if (result == null) {
// Error during method call
satisfied.add(booleanProvider); // Prevents further attempts to call this provider for this player.
@@ -155,13 +149,11 @@ class BooleanProviderValueGatherer {
return satisfied;
}
- private T getMethodResult(Callable callable, Function errorMsg) {
+ private T getMethodResult(Callable callable, MethodWrapper method) {
try {
return callable.call();
- } catch (Exception | NoSuchFieldError | NoSuchMethodError e) {
- logger.warn(errorMsg.apply(e));
- return null;
+ } catch (Exception | NoClassDefFoundError | NoSuchFieldError | NoSuchMethodError e) {
+ throw new DataExtensionMethodCallException(e, pluginName, method);
}
}
-
}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/DoubleAndPercentageProviderValueGatherer.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/DoubleAndPercentageProviderValueGatherer.java
index 6f2ffcab4..3e3c668c5 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/DoubleAndPercentageProviderValueGatherer.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/DoubleAndPercentageProviderValueGatherer.java
@@ -16,6 +16,7 @@
*/
package com.djrapitops.plan.extension.implementation.providers.gathering;
+import com.djrapitops.plan.api.exceptions.DataExtensionMethodCallException;
import com.djrapitops.plan.db.Database;
import com.djrapitops.plan.db.access.transactions.Transaction;
import com.djrapitops.plan.extension.DataExtension;
@@ -30,7 +31,6 @@ import com.djrapitops.plan.extension.implementation.storage.transactions.results
import com.djrapitops.plan.extension.implementation.storage.transactions.results.StorePlayerPercentageResultTransaction;
import com.djrapitops.plan.extension.implementation.storage.transactions.results.StoreServerDoubleResultTransaction;
import com.djrapitops.plan.extension.implementation.storage.transactions.results.StoreServerPercentageResultTransaction;
-import com.djrapitops.plugin.logging.console.PluginLogger;
import java.util.Optional;
import java.util.UUID;
@@ -51,20 +51,17 @@ class DoubleAndPercentageProviderValueGatherer {
private final Database database;
private final DataProviders dataProviders;
- private final PluginLogger logger;
DoubleAndPercentageProviderValueGatherer(
String pluginName, DataExtension extension,
UUID serverUUID, Database database,
- DataProviders dataProviders,
- PluginLogger logger
+ DataProviders dataProviders
) {
this.pluginName = pluginName;
this.extension = extension;
this.serverUUID = serverUUID;
this.database = database;
this.dataProviders = dataProviders;
- this.logger = logger;
}
void gatherDoubleDataOfPlayer(UUID playerUUID, String playerName, Conditions conditions) {
@@ -105,10 +102,7 @@ class DoubleAndPercentageProviderValueGatherer {
}
MethodWrapper method = doubleProvider.getMethod();
- Double result = getMethodResult(
- methodCaller.apply(method),
- throwable -> pluginName + " has invalid implementation, method " + method.getMethodName() + " threw exception: " + throwable.toString()
- );
+ Double result = getMethodResult(methodCaller.apply(method), method);
if (result == null) {
return;
}
@@ -123,13 +117,11 @@ class DoubleAndPercentageProviderValueGatherer {
}
}
- private T getMethodResult(Callable callable, Function errorMsg) {
+ private T getMethodResult(Callable callable, MethodWrapper method) {
try {
return callable.call();
- } catch (Exception | NoSuchFieldError | NoSuchMethodError e) {
- logger.warn(errorMsg.apply(e));
- return null;
+ } catch (Exception | NoClassDefFoundError | NoSuchFieldError | NoSuchMethodError e) {
+ throw new DataExtensionMethodCallException(e, pluginName, method);
}
}
-
}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/NumberProviderValueGatherer.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/NumberProviderValueGatherer.java
index ef599841d..9fb06efed 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/NumberProviderValueGatherer.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/NumberProviderValueGatherer.java
@@ -16,6 +16,7 @@
*/
package com.djrapitops.plan.extension.implementation.providers.gathering;
+import com.djrapitops.plan.api.exceptions.DataExtensionMethodCallException;
import com.djrapitops.plan.db.Database;
import com.djrapitops.plan.db.access.transactions.Transaction;
import com.djrapitops.plan.extension.DataExtension;
@@ -29,7 +30,6 @@ import com.djrapitops.plan.extension.implementation.storage.transactions.StoreIc
import com.djrapitops.plan.extension.implementation.storage.transactions.providers.StoreNumberProviderTransaction;
import com.djrapitops.plan.extension.implementation.storage.transactions.results.StorePlayerNumberResultTransaction;
import com.djrapitops.plan.extension.implementation.storage.transactions.results.StoreServerNumberResultTransaction;
-import com.djrapitops.plugin.logging.console.PluginLogger;
import java.util.Optional;
import java.util.UUID;
@@ -50,20 +50,17 @@ class NumberProviderValueGatherer {
private final Database database;
private final DataProviders dataProviders;
- private final PluginLogger logger;
NumberProviderValueGatherer(
String pluginName, DataExtension extension,
UUID serverUUID, Database database,
- DataProviders dataProviders,
- PluginLogger logger
+ DataProviders dataProviders
) {
this.pluginName = pluginName;
this.extension = extension;
this.serverUUID = serverUUID;
this.database = database;
this.dataProviders = dataProviders;
- this.logger = logger;
}
void gatherNumberDataOfPlayer(UUID playerUUID, String playerName, Conditions conditions) {
@@ -100,10 +97,7 @@ class NumberProviderValueGatherer {
}
MethodWrapper method = numberProvider.getMethod();
- Long result = getMethodResult(
- methodCaller.apply(method),
- throwable -> pluginName + " has invalid implementation, method " + method.getMethodName() + " threw exception: " + throwable.toString()
- );
+ Long result = getMethodResult(methodCaller.apply(method), method);
if (result == null) {
return;
}
@@ -115,12 +109,11 @@ class NumberProviderValueGatherer {
database.executeTransaction(storeTransactionCreator.apply(method, result));
}
- private T getMethodResult(Callable callable, Function errorMsg) {
+ private T getMethodResult(Callable callable, MethodWrapper method) {
try {
return callable.call();
- } catch (Exception | NoSuchFieldError | NoSuchMethodError e) {
- logger.warn(errorMsg.apply(e));
- return null;
+ } catch (Exception | NoClassDefFoundError | NoSuchFieldError | NoSuchMethodError e) {
+ throw new DataExtensionMethodCallException(e, pluginName, method);
}
}
}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/ProviderValueGatherer.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/ProviderValueGatherer.java
index 02a0e0982..45ecfc273 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/ProviderValueGatherer.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/ProviderValueGatherer.java
@@ -22,13 +22,14 @@ import com.djrapitops.plan.extension.DataExtension;
import com.djrapitops.plan.extension.icon.Icon;
import com.djrapitops.plan.extension.implementation.DataProviderExtractor;
import com.djrapitops.plan.extension.implementation.TabInformation;
+import com.djrapitops.plan.extension.implementation.providers.DataProviders;
+import com.djrapitops.plan.extension.implementation.providers.MethodWrapper;
import com.djrapitops.plan.extension.implementation.storage.transactions.StoreIconTransaction;
import com.djrapitops.plan.extension.implementation.storage.transactions.StorePluginTransaction;
import com.djrapitops.plan.extension.implementation.storage.transactions.StoreTabInformationTransaction;
import com.djrapitops.plan.extension.implementation.storage.transactions.results.RemoveInvalidResultsTransaction;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.info.server.ServerInfo;
-import com.djrapitops.plugin.logging.console.PluginLogger;
import java.util.UUID;
@@ -43,43 +44,49 @@ public class ProviderValueGatherer {
private final DataProviderExtractor extractor;
private final DBSystem dbSystem;
private final ServerInfo serverInfo;
+
+ private DataProviders dataProviders;
private BooleanProviderValueGatherer booleanGatherer;
private NumberProviderValueGatherer numberGatherer;
private DoubleAndPercentageProviderValueGatherer doubleAndPercentageGatherer;
private StringProviderValueGatherer stringGatherer;
+ private TableProviderValueGatherer tableGatherer;
+
public ProviderValueGatherer(
DataExtension extension,
DataProviderExtractor extractor,
DBSystem dbSystem,
- ServerInfo serverInfo,
- PluginLogger logger
+ ServerInfo serverInfo
) {
this.callEvents = extension.callExtensionMethodsOn();
this.extractor = extractor;
this.dbSystem = dbSystem;
this.serverInfo = serverInfo;
+ String pluginName = extractor.getPluginName();
+ UUID serverUUID = serverInfo.getServerUUID();
+ Database database = dbSystem.getDatabase();
+ dataProviders = extractor.getDataProviders();
booleanGatherer = new BooleanProviderValueGatherer(
- extractor.getPluginName(), extension,
- serverInfo.getServerUUID(), dbSystem.getDatabase(),
- extractor.getDataProviders(), logger
+ pluginName, extension, serverUUID, database, dataProviders
);
numberGatherer = new NumberProviderValueGatherer(
- extractor.getPluginName(), extension,
- serverInfo.getServerUUID(), dbSystem.getDatabase(),
- extractor.getDataProviders(), logger
+ pluginName, extension, serverUUID, database, dataProviders
);
doubleAndPercentageGatherer = new DoubleAndPercentageProviderValueGatherer(
- extractor.getPluginName(), extension,
- serverInfo.getServerUUID(), dbSystem.getDatabase(),
- extractor.getDataProviders(), logger
+ pluginName, extension, serverUUID, database, dataProviders
);
stringGatherer = new StringProviderValueGatherer(
- extractor.getPluginName(), extension,
- serverInfo.getServerUUID(), dbSystem.getDatabase(),
- extractor.getDataProviders(), logger
+ pluginName, extension, serverUUID, database, dataProviders
);
+ tableGatherer = new TableProviderValueGatherer(
+ pluginName, extension, serverUUID, database, dataProviders
+ );
+ }
+
+ public void disableMethodFromUse(MethodWrapper method) {
+ dataProviders.removeProviderWithMethod(method);
}
public boolean canCallEvent(CallEvents event) {
@@ -121,6 +128,7 @@ public class ProviderValueGatherer {
numberGatherer.gatherNumberDataOfPlayer(playerUUID, playerName, conditions);
doubleAndPercentageGatherer.gatherDoubleDataOfPlayer(playerUUID, playerName, conditions);
stringGatherer.gatherStringDataOfPlayer(playerUUID, playerName, conditions);
+ tableGatherer.gatherTableDataOfPlayer(playerUUID, playerName, conditions);
}
public void updateValues() {
@@ -128,5 +136,6 @@ public class ProviderValueGatherer {
numberGatherer.gatherNumberDataOfServer(conditions);
doubleAndPercentageGatherer.gatherDoubleDataOfServer(conditions);
stringGatherer.gatherStringDataOfServer(conditions);
+ tableGatherer.gatherTableDataOfServer(conditions);
}
}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/StringProviderValueGatherer.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/StringProviderValueGatherer.java
index dc5932a6d..74fd5f99a 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/StringProviderValueGatherer.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/StringProviderValueGatherer.java
@@ -16,6 +16,7 @@
*/
package com.djrapitops.plan.extension.implementation.providers.gathering;
+import com.djrapitops.plan.api.exceptions.DataExtensionMethodCallException;
import com.djrapitops.plan.db.Database;
import com.djrapitops.plan.db.access.transactions.Transaction;
import com.djrapitops.plan.extension.DataExtension;
@@ -28,7 +29,6 @@ import com.djrapitops.plan.extension.implementation.storage.transactions.StoreIc
import com.djrapitops.plan.extension.implementation.storage.transactions.providers.StoreStringProviderTransaction;
import com.djrapitops.plan.extension.implementation.storage.transactions.results.StorePlayerStringResultTransaction;
import com.djrapitops.plan.extension.implementation.storage.transactions.results.StoreServerStringResultTransaction;
-import com.djrapitops.plugin.logging.console.PluginLogger;
import org.apache.commons.lang3.StringUtils;
import java.util.Optional;
@@ -50,20 +50,17 @@ class StringProviderValueGatherer {
private final Database database;
private final DataProviders dataProviders;
- private final PluginLogger logger;
StringProviderValueGatherer(
String pluginName, DataExtension extension,
UUID serverUUID, Database database,
- DataProviders dataProviders,
- PluginLogger logger
+ DataProviders dataProviders
) {
this.pluginName = pluginName;
this.extension = extension;
this.serverUUID = serverUUID;
this.database = database;
this.dataProviders = dataProviders;
- this.logger = logger;
}
void gatherStringDataOfPlayer(UUID playerUUID, String playerName, Conditions conditions) {
@@ -101,10 +98,7 @@ class StringProviderValueGatherer {
}
MethodWrapper method = stringProvider.getMethod();
- String result = getMethodResult(
- methodCaller.apply(method),
- throwable -> pluginName + " has invalid implementation, method " + method.getMethodName() + " threw exception: " + throwable.toString()
- );
+ String result = getMethodResult(methodCaller.apply(method), method);
if (result == null) {
return;
}
@@ -116,12 +110,11 @@ class StringProviderValueGatherer {
database.executeTransaction(storeTransactionCreator.apply(method, result));
}
- private T getMethodResult(Callable callable, Function errorMsg) {
+ private T getMethodResult(Callable callable, MethodWrapper method) {
try {
return callable.call();
- } catch (Exception | NoSuchFieldError | NoSuchMethodError e) {
- logger.warn(errorMsg.apply(e));
- return null;
+ } catch (Exception | NoClassDefFoundError | NoSuchFieldError | NoSuchMethodError e) {
+ throw new DataExtensionMethodCallException(e, pluginName, method);
}
}
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/TableProviderValueGatherer.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/TableProviderValueGatherer.java
new file mode 100644
index 000000000..12477548e
--- /dev/null
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/TableProviderValueGatherer.java
@@ -0,0 +1,124 @@
+/*
+ * 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 GNU 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
+ * GNU 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 .
+ */
+package com.djrapitops.plan.extension.implementation.providers.gathering;
+
+import com.djrapitops.plan.api.exceptions.DataExtensionMethodCallException;
+import com.djrapitops.plan.db.Database;
+import com.djrapitops.plan.db.access.transactions.Transaction;
+import com.djrapitops.plan.extension.DataExtension;
+import com.djrapitops.plan.extension.icon.Icon;
+import com.djrapitops.plan.extension.implementation.ProviderInformation;
+import com.djrapitops.plan.extension.implementation.providers.DataProvider;
+import com.djrapitops.plan.extension.implementation.providers.DataProviders;
+import com.djrapitops.plan.extension.implementation.providers.MethodWrapper;
+import com.djrapitops.plan.extension.implementation.providers.TableDataProvider;
+import com.djrapitops.plan.extension.implementation.storage.transactions.StoreIconTransaction;
+import com.djrapitops.plan.extension.implementation.storage.transactions.providers.StoreTableProviderTransaction;
+import com.djrapitops.plan.extension.implementation.storage.transactions.results.StorePlayerTableResultTransaction;
+import com.djrapitops.plan.extension.implementation.storage.transactions.results.StoreServerTableResultTransaction;
+import com.djrapitops.plan.extension.table.Table;
+
+import java.util.Optional;
+import java.util.UUID;
+import java.util.concurrent.Callable;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+/**
+ * Gathers TableProvider method data.
+ *
+ * @author Rsl1122
+ */
+class TableProviderValueGatherer {
+
+ private final String pluginName;
+ private final DataExtension extension;
+ private final UUID serverUUID;
+
+ private final Database database;
+ private final DataProviders dataProviders;
+
+ TableProviderValueGatherer(
+ String pluginName, DataExtension extension,
+ UUID serverUUID, Database database,
+ DataProviders dataProviders
+ ) {
+ this.pluginName = pluginName;
+ this.extension = extension;
+ this.serverUUID = serverUUID;
+ this.database = database;
+ this.dataProviders = dataProviders;
+ }
+
+ void gatherTableDataOfPlayer(UUID playerUUID, String playerName, Conditions conditions) {
+ // Method parameters abstracted away so that same method can be used for all parameter types
+ // Same with Method result store transaction creation
+ Function, Callable> methodCaller = method -> () -> method.callMethod(extension, playerUUID, playerName);
+ BiFunction, Table, Transaction> storeTransactionCreator = (method, result) -> new StorePlayerTableResultTransaction(pluginName, serverUUID, method.getMethodName(), playerUUID, result);
+
+ for (DataProvider tableProvider : dataProviders.getPlayerMethodsByType(Table.class)) {
+ gatherTableDataOfProvider(methodCaller, storeTransactionCreator, conditions, tableProvider);
+ }
+ }
+
+ void gatherTableDataOfServer(Conditions conditions) {
+ // Method parameters abstracted away so that same method can be used for all parameter types
+ // Same with Method result store transaction creation
+ Function, Callable> methodCaller = method -> () -> method.callMethod(extension);
+ BiFunction, Table, Transaction> storeTransactionCreator = (method, result) -> new StoreServerTableResultTransaction(pluginName, serverUUID, method.getMethodName(), result);
+
+ for (DataProvider tableProvider : dataProviders.getServerMethodsByType(Table.class)) {
+ gatherTableDataOfProvider(methodCaller, storeTransactionCreator, conditions, tableProvider);
+ }
+ }
+
+ private void gatherTableDataOfProvider(
+ Function, Callable> methodCaller,
+ BiFunction, Table, Transaction> storeTransactionCreator,
+ Conditions conditions,
+ DataProvider tableProvider
+ ) {
+ ProviderInformation providerInformation = tableProvider.getProviderInformation();
+ Optional condition = providerInformation.getCondition();
+ if (condition.isPresent() && conditions.isNotFulfilled(condition.get())) {
+ return;
+ }
+
+ MethodWrapper method = tableProvider.getMethod();
+ Table result = getMethodResult(methodCaller.apply(method), method);
+ if (result == null) {
+ return;
+ }
+
+ for (Icon icon : result.getIcons()) {
+ if (icon != null) {
+ database.executeTransaction(new StoreIconTransaction(icon));
+ }
+ }
+ database.executeTransaction(new StoreTableProviderTransaction(serverUUID, providerInformation, TableDataProvider.getTableColor(tableProvider), result));
+ database.executeTransaction(storeTransactionCreator.apply(method, result));
+ }
+
+ private T getMethodResult(Callable callable, MethodWrapper method) {
+ try {
+ return callable.call();
+ } catch (Exception | NoClassDefFoundError | NoSuchFieldError | NoSuchMethodError e) {
+ throw new DataExtensionMethodCallException(e, pluginName, method);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionDoubleData.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionDoubleData.java
index 29b0e305a..81e266b9b 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionDoubleData.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionDoubleData.java
@@ -40,4 +40,8 @@ public class ExtensionDoubleData implements ExtensionData {
public String getFormattedValue(Formatter formatter) {
return formatter.apply(value);
}
+
+ public double getRawValue() {
+ return value;
+ }
}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionNumberData.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionNumberData.java
index a4907b7ac..a8e784325 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionNumberData.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionNumberData.java
@@ -47,4 +47,8 @@ public class ExtensionNumberData implements ExtensionData {
public String getFormattedValue(Formatter formatter) {
return formatter.apply(value);
}
+
+ public long getRawValue() {
+ return value;
+ }
}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionTabData.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionTabData.java
index 41aaf8e50..b46624713 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionTabData.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionTabData.java
@@ -36,7 +36,10 @@ public class ExtensionTabData implements Comparable {
private final Map numberData;
private final Map stringData;
+ private final List tableData;
+
private List order;
+ private List descriptives;
// Table and Graph data will be added later.
@@ -48,6 +51,9 @@ public class ExtensionTabData implements Comparable {
percentageData = new HashMap<>();
numberData = new HashMap<>();
stringData = new HashMap<>();
+
+ tableData = new ArrayList<>();
+ descriptives = new ArrayList<>();
}
public TabInformation getTabInformation() {
@@ -78,6 +84,21 @@ public class ExtensionTabData implements Comparable {
return Optional.ofNullable(stringData.get(providerName));
}
+ public List getTableData() {
+ return tableData;
+ }
+
+ /**
+ * Get all Descriptives for this tabs data.
+ *
+ * Only available after the Tab has been built.
+ *
+ * @return List of {@link ExtensionDescriptive}s.
+ */
+ public List getDescriptives() {
+ return descriptives;
+ }
+
@Override
public int compareTo(ExtensionTabData other) {
return Integer.compare(this.tabInformation.getTabPriority(), other.tabInformation.getTabPriority()); // Lower is first
@@ -103,6 +124,23 @@ public class ExtensionTabData implements Comparable {
this.percentageData.putAll(other.percentageData);
this.numberData.putAll(other.numberData);
this.stringData.putAll(other.stringData);
+
+ this.tableData.addAll(other.tableData);
+
+ createOrderingList();
+ }
+
+ private void createOrderingList() {
+ booleanData.values().stream().map(ExtensionData::getDescriptive).forEach(descriptives::add);
+ doubleData.values().stream().map(ExtensionData::getDescriptive).forEach(descriptives::add);
+ percentageData.values().stream().map(ExtensionData::getDescriptive).forEach(descriptives::add);
+ numberData.values().stream().map(ExtensionData::getDescriptive).forEach(descriptives::add);
+ stringData.values().stream().map(ExtensionData::getDescriptive).forEach(descriptives::add);
+
+ order = descriptives.stream().sorted()
+ .map(ExtensionDescriptive::getName)
+ .distinct()// Method names are usually different, but in case someone had same method name with different parameters.
+ .collect(Collectors.toList());
}
public static class Factory {
@@ -138,22 +176,14 @@ public class ExtensionTabData implements Comparable {
return this;
}
- private void createOrderingList() {
- List descriptives = new ArrayList<>();
- data.booleanData.values().stream().map(ExtensionData::getDescriptive).forEach(descriptives::add);
- data.doubleData.values().stream().map(ExtensionData::getDescriptive).forEach(descriptives::add);
- data.percentageData.values().stream().map(ExtensionData::getDescriptive).forEach(descriptives::add);
- data.numberData.values().stream().map(ExtensionData::getDescriptive).forEach(descriptives::add);
- data.stringData.values().stream().map(ExtensionData::getDescriptive).forEach(descriptives::add);
-
- data.order = descriptives.stream().sorted()
- .map(ExtensionDescriptive::getName)
- .distinct()// Method names are usually different, but in case someone had same method name with different parameters.
- .collect(Collectors.toList());
+ public Factory putTableData(ExtensionTableData extensionTableData) {
+ data.tableData.add(extensionTableData);
+ return this;
}
public ExtensionTabData build() {
- createOrderingList();
+ data.createOrderingList();
+ Collections.sort(data.tableData);
return data;
}
}
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionTableData.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionTableData.java
new file mode 100644
index 000000000..99ce47816
--- /dev/null
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionTableData.java
@@ -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 GNU 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
+ * GNU 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 .
+ */
+package com.djrapitops.plan.extension.implementation.results;
+
+import com.djrapitops.plan.data.element.TableContainer;
+import com.djrapitops.plan.extension.icon.Color;
+import com.djrapitops.plan.extension.icon.Icon;
+import com.djrapitops.plan.extension.table.Table;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Represents table data from a single TableProvider.
+ *
+ * @author Rsl1122
+ */
+public class ExtensionTableData implements Comparable {
+
+ private final String providerName;
+ private final Table table;
+ private final Color tableColor;
+
+ public ExtensionTableData(String providerName, Table table, Color tableColor) {
+ this.providerName = providerName;
+ this.table = table;
+ this.tableColor = tableColor;
+ }
+
+ public TableContainer getHtmlTable() {
+ String[] columns = table.getColumns();
+ Icon[] icons = table.getIcons();
+ List rows = table.getRows();
+
+ String[] header = buildHeader(columns, icons);
+
+ TableContainer htmlTable = new TableContainer(header);
+ if (rows.size() > 50) {
+ htmlTable.useJqueryDataTables(); // Use a jQuery data table since there are a lot of rows.
+ } else {
+ String colorName = com.djrapitops.plan.utilities.html.icon.Color.getByName(tableColor.name()).orElse(com.djrapitops.plan.utilities.html.icon.Color.NONE).getHtmlClass()
+ .replace("col-", ""); // TODO after PluginData deprecation, change this thing
+ htmlTable.setColor(colorName);
+ }
+
+ for (Object[] row : rows) {
+ htmlTable.addRow(Arrays.stream(row).map(value -> value != null ? value.toString() : null).toArray(Serializable[]::new));
+ }
+
+ return htmlTable;
+ }
+
+ private String[] buildHeader(String[] columns, Icon[] icons) {
+ ArrayList header = new ArrayList<>();
+
+ for (int i = 0; i < columns.length; i++) {
+ String column = columns[i];
+ if (column == null) {
+ break;
+ }
+ header.add(com.djrapitops.plan.utilities.html.icon.Icon.fromExtensionIcon(icons[i]).toHtml() + ' ' + column);
+ }
+
+ return header.toArray(new String[0]);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof ExtensionTableData)) return false;
+ ExtensionTableData that = (ExtensionTableData) o;
+ return providerName.equals(that.providerName) &&
+ tableColor == that.tableColor;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(providerName, tableColor);
+ }
+
+ @Override
+ public int compareTo(ExtensionTableData other) {
+ return String.CASE_INSENSITIVE_ORDER.compare(providerName, other.providerName);
+ }
+
+ public boolean isWideTable() {
+ return table.getMaxColumnSize() > 3;
+ }
+}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/player/ExtensionPlayerData.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/player/ExtensionPlayerData.java
index 527134b8e..dba53f4a7 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/player/ExtensionPlayerData.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/player/ExtensionPlayerData.java
@@ -19,10 +19,7 @@ package com.djrapitops.plan.extension.implementation.results.player;
import com.djrapitops.plan.extension.implementation.results.ExtensionInformation;
import com.djrapitops.plan.extension.implementation.results.ExtensionTabData;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
+import java.util.*;
/**
* Represents data of a single extension about a player.
@@ -104,6 +101,31 @@ public class ExtensionPlayerData implements Comparable {
Collections.sort(data.tabs);
return data;
}
+
+ public Factory combine(Factory with) {
+ if (with != null) {
+ for (ExtensionTabData tab : with.build().getTabs()) {
+ Optional found = getTab(tab.getTabInformation().getTabName());
+ if (found.isPresent()) {
+ found.get().combine(tab);
+ } else {
+ addTab(tab);
+ }
+ }
+ }
+ return this;
+ }
+
+ public Optional getTab(String tabName) {
+ for (ExtensionTabData tab : data.tabs) {
+ if (tabName.equals(tab.getTabInformation().getTabName())) {
+ return Optional.of(tab);
+ }
+ }
+ return Optional.empty();
+ }
+
+
}
}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionAggregateNumbersQuery.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionAggregateNumbersQuery.java
index a71a44393..9ff611889 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionAggregateNumbersQuery.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionAggregateNumbersQuery.java
@@ -150,8 +150,8 @@ public class ExtensionAggregateNumbersQuery implements Query> extensionsByServerUUID = db.query(ExtensionInformationQueries.allExtensions());
Map extensionDataByPluginID = db.query(fetchIncompletePlayerDataByPluginID());
+ Map tableDataByPluginID = db.query(new ExtensionPlayerTablesQuery(playerUUID));
+ combine(extensionDataByPluginID, tableDataByPluginID);
+
return flatMapByServerUUID(extensionsByServerUUID, extensionDataByPluginID);
}
+ private void combine(
+ Map extensionDataByPluginID,
+ Map aggregates
+ ) {
+ for (Map.Entry entry : aggregates.entrySet()) {
+ Integer pluginID = entry.getKey();
+ ExtensionPlayerData.Factory data = entry.getValue();
+
+ ExtensionPlayerData.Factory found = extensionDataByPluginID.get(pluginID);
+ if (found == null) {
+ extensionDataByPluginID.put(pluginID, data);
+ } else {
+ found.combine(data);
+ }
+ }
+ }
+
private Map> flatMapByServerUUID(Map> extensionsByServerUUID, Map extensionDataByPluginID) {
Map> extensionDataByServerUUID = new HashMap<>();
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionPlayerTablesQuery.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionPlayerTablesQuery.java
new file mode 100644
index 000000000..d106195c8
--- /dev/null
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionPlayerTablesQuery.java
@@ -0,0 +1,287 @@
+/*
+ * 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 GNU 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
+ * GNU 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 .
+ */
+package com.djrapitops.plan.extension.implementation.storage.queries;
+
+import com.djrapitops.plan.db.SQLDB;
+import com.djrapitops.plan.db.access.Query;
+import com.djrapitops.plan.db.access.QueryStatement;
+import com.djrapitops.plan.db.sql.tables.ExtensionIconTable;
+import com.djrapitops.plan.db.sql.tables.ExtensionPlayerTableValueTable;
+import com.djrapitops.plan.db.sql.tables.ExtensionTabTable;
+import com.djrapitops.plan.db.sql.tables.ExtensionTableProviderTable;
+import com.djrapitops.plan.extension.ElementOrder;
+import com.djrapitops.plan.extension.icon.Color;
+import com.djrapitops.plan.extension.icon.Family;
+import com.djrapitops.plan.extension.icon.Icon;
+import com.djrapitops.plan.extension.implementation.TabInformation;
+import com.djrapitops.plan.extension.implementation.results.ExtensionTabData;
+import com.djrapitops.plan.extension.implementation.results.ExtensionTableData;
+import com.djrapitops.plan.extension.implementation.results.player.ExtensionPlayerData;
+import com.djrapitops.plan.extension.table.Table;
+import com.djrapitops.plan.extension.table.TableAccessor;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.*;
+
+import static com.djrapitops.plan.db.sql.parsing.Sql.*;
+
+/**
+ * Query player tables from tableprovider table.
+ *
+ * Returns Map: PluginID - ExtensionPlayerData.Factory.
+ *
+ * How it is done:
+ * - TableProviders are queried and formed into Table.Factory objects sorted into a multi-map: PluginID - TableID - Table.Factory
+ * - Table values are queried and merged into the above multimap
+ * - Data query is sorted into a multi-map: PluginID - Tab Name - Tab Data
+ * - (Tab Name can be empty.)
+ * - Multi-map is sorted into ExtensionPlayerData objects by PluginID, one per ID
+ *
+ * There are multiple data extraction methods to make extracting the value query easier.
+ *
+ * @author Rsl1122
+ */
+public class ExtensionPlayerTablesQuery implements Query> {
+
+ private final UUID playerUUID;
+
+ public ExtensionPlayerTablesQuery(UUID playerUUID) {
+ this.playerUUID = playerUUID;
+ }
+
+ private static Map flatMapByPluginID(Map> tabDataByPluginID) {
+ Map dataByPluginID = new HashMap<>();
+ for (Map.Entry> entry : tabDataByPluginID.entrySet()) {
+ Integer pluginID = entry.getKey();
+ ExtensionPlayerData.Factory data = dataByPluginID.getOrDefault(pluginID, new ExtensionPlayerData.Factory(pluginID));
+ for (ExtensionTabData.Factory tabData : entry.getValue().values()) {
+ data.addTab(tabData.build());
+ }
+ dataByPluginID.put(pluginID, data);
+ }
+ return dataByPluginID;
+ }
+
+ @Override
+ public Map executeQuery(SQLDB db) {
+ Map> tablesByPluginIDAndTableID = db.query(queryTableProviders());
+ Map> tablesWithValues = db.query(queryTableValues(tablesByPluginIDAndTableID));
+
+ Map> tabDataByPluginID = mapToTabsByPluginID(tablesWithValues);
+ return flatMapByPluginID(tabDataByPluginID);
+ }
+
+ /**
+ * @param tablesByPluginIDAndTableID {@code >}
+ * @return {@code }
+ */
+ private Map> mapToTabsByPluginID(Map> tablesByPluginIDAndTableID) {
+ Map> byPluginID = new HashMap<>();
+
+ for (Map.Entry> entry : tablesByPluginIDAndTableID.entrySet()) {
+ Integer pluginID = entry.getKey();
+ Map byTabName = byPluginID.getOrDefault(pluginID, new HashMap<>());
+ for (Table.Factory table : entry.getValue().values()) {
+ // Extra Table information
+ String tableName = TableAccessor.getTableName(table);
+ Color tableColor = TableAccessor.getColor(table);
+
+ // Extra tab information
+ String tabName = TableAccessor.getTabName(table);
+ int tabPriority = TableAccessor.getTabPriority(table);
+ ElementOrder[] tabOrder = TableAccessor.getTabOrder(table);
+ Icon tabIcon = TableAccessor.getTabIcon(table);
+
+ ExtensionTabData.Factory tab = byTabName.getOrDefault(tabName, new ExtensionTabData.Factory(new TabInformation(tabName, tabIcon, tabOrder, tabPriority)));
+ tab.putTableData(new ExtensionTableData(
+ tableName, table.build(), tableColor
+ ));
+ byTabName.put(tabName, tab);
+ }
+ byPluginID.put(pluginID, byTabName);
+ }
+
+ return byPluginID;
+ }
+
+ // Map: >
+ private Query>> queryTableValues(Map> tables) {
+ String selectTableValues = SELECT +
+ ExtensionTableProviderTable.PLUGIN_ID + ',' +
+ ExtensionPlayerTableValueTable.TABLE_ID + ',' +
+ ExtensionPlayerTableValueTable.VALUE_1 + ',' +
+ ExtensionPlayerTableValueTable.VALUE_2 + ',' +
+ ExtensionPlayerTableValueTable.VALUE_3 + ',' +
+ ExtensionPlayerTableValueTable.VALUE_4 +
+ FROM + ExtensionPlayerTableValueTable.TABLE_NAME +
+ INNER_JOIN + ExtensionTableProviderTable.TABLE_NAME + " on " + ExtensionTableProviderTable.TABLE_NAME + '.' + ExtensionTableProviderTable.ID + '=' + ExtensionPlayerTableValueTable.TABLE_NAME + '.' + ExtensionPlayerTableValueTable.TABLE_ID +
+ WHERE + ExtensionPlayerTableValueTable.USER_UUID + "=?";
+
+ return new QueryStatement>>(selectTableValues, 10000) {
+ @Override
+ public void prepare(PreparedStatement statement) throws SQLException {
+ statement.setString(1, playerUUID.toString());
+ }
+
+ @Override
+ public Map> processResults(ResultSet set) throws SQLException {
+ while (set.next()) {
+ Table.Factory table = getTable(set);
+ if (table == null) {
+ continue;
+ }
+
+ Object[] row = extractTableRow(set);
+ if (row.length > 0) {
+ table.addRow(row);
+ }
+ }
+ return tables;
+ }
+
+ private Table.Factory getTable(ResultSet set) throws SQLException {
+ int pluginID = set.getInt(ExtensionTableProviderTable.PLUGIN_ID);
+ Map byTableID = tables.get(pluginID);
+ if (byTableID == null) {
+ return null;
+ }
+ int tableID = set.getInt(ExtensionPlayerTableValueTable.TABLE_ID);
+ return byTableID.get(tableID);
+ }
+ };
+ }
+
+ private Object[] extractTableRow(ResultSet set) throws SQLException {
+ List row = new ArrayList<>();
+ for (int i = 1; i <= 5; i++) {
+ String columnName = "col_" + i + "_value"; // See ExtensionPlayerTableValueTable.VALUE_1
+ String value = set.getString(columnName);
+ if (value == null) {
+ return row.toArray(new Object[0]);
+ }
+ row.add(value);
+ }
+ return row.toArray(new Object[0]);
+ }
+
+ // Map: >
+ private Query>> queryTableProviders() {
+ String selectTables = SELECT +
+ "p1." + ExtensionTableProviderTable.ID + " as table_id," +
+ "p1." + ExtensionTableProviderTable.PLUGIN_ID + " as plugin_id," +
+ "p1." + ExtensionTableProviderTable.PROVIDER_NAME + " as table_name," +
+ "p1." + ExtensionTableProviderTable.COLOR + " as table_color," +
+ ExtensionTableProviderTable.COL_1 + ',' +
+ ExtensionTableProviderTable.COL_2 + ',' +
+ ExtensionTableProviderTable.COL_3 + ',' +
+ ExtensionTableProviderTable.COL_4 + ',' +
+ "t1." + ExtensionTabTable.TAB_NAME + " as tab_name," +
+ "t1." + ExtensionTabTable.TAB_PRIORITY + " as tab_priority," +
+ "t1." + ExtensionTabTable.ELEMENT_ORDER + " as element_order," +
+ "i1." + ExtensionIconTable.ICON_NAME + " as i1_name," +
+ "i1." + ExtensionIconTable.FAMILY + " as i1_family," +
+ "i1." + ExtensionIconTable.COLOR + " as i1_color," +
+ "i2." + ExtensionIconTable.ICON_NAME + " as i2_name," +
+ "i2." + ExtensionIconTable.FAMILY + " as i2_family," +
+ "i2." + ExtensionIconTable.COLOR + " as i2_color," +
+ "i3." + ExtensionIconTable.ICON_NAME + " as i3_name," +
+ "i3." + ExtensionIconTable.FAMILY + " as i3_family," +
+ "i3." + ExtensionIconTable.COLOR + " as i3_color," +
+ "i4." + ExtensionIconTable.ICON_NAME + " as i4_name," +
+ "i4." + ExtensionIconTable.FAMILY + " as i4_family," +
+ "i4." + ExtensionIconTable.COLOR + " as i4_color," +
+ "i6." + ExtensionIconTable.ICON_NAME + " as tab_icon_name," +
+ "i6." + ExtensionIconTable.FAMILY + " as tab_icon_family," +
+ "i6." + ExtensionIconTable.COLOR + " as tab_icon_color" +
+ FROM + ExtensionTableProviderTable.TABLE_NAME + " p1" +
+ INNER_JOIN + ExtensionPlayerTableValueTable.TABLE_NAME + " v1 on v1." + ExtensionPlayerTableValueTable.TABLE_ID + "=p1." + ExtensionTableProviderTable.ID +
+ LEFT_JOIN + ExtensionTabTable.TABLE_NAME + " t1 on t1." + ExtensionTabTable.ID + "=p1." + ExtensionTableProviderTable.TAB_ID +
+ LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i1 on i1." + ExtensionIconTable.ID + "=p1." + ExtensionTableProviderTable.ICON_1_ID +
+ LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i2 on i2." + ExtensionIconTable.ID + "=p1." + ExtensionTableProviderTable.ICON_2_ID +
+ LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i3 on i3." + ExtensionIconTable.ID + "=p1." + ExtensionTableProviderTable.ICON_3_ID +
+ LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i4 on i4." + ExtensionIconTable.ID + "=p1." + ExtensionTableProviderTable.ICON_4_ID +
+ LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i6 on i6." + ExtensionIconTable.ID + "=t1." + ExtensionTabTable.ICON_ID +
+ WHERE + "v1." + ExtensionPlayerTableValueTable.USER_UUID + "=?";
+
+ return new QueryStatement>>(selectTables, 100) {
+ @Override
+ public void prepare(PreparedStatement statement) throws SQLException {
+ statement.setString(1, playerUUID.toString());
+ }
+
+ @Override
+ public Map> processResults(ResultSet set) throws SQLException {
+ Map> byPluginID = new HashMap<>();
+
+ while (set.next()) {
+ int pluginID = set.getInt("plugin_id");
+ Map byTableID = byPluginID.getOrDefault(pluginID, new HashMap<>());
+
+ int tableID = set.getInt("table_id");
+ Table.Factory table = Table.builder();
+
+ extractColumns(set, table);
+
+ TableAccessor.setColor(table, Color.getByName(set.getString("table_color")).orElse(Color.NONE));
+ TableAccessor.setTableName(table, set.getString("table_name"));
+ TableAccessor.setTabName(table, Optional.ofNullable(set.getString("tab_name")).orElse(""));
+ TableAccessor.setTabPriority(table, Optional.of(set.getInt("tab_priority")).orElse(100));
+ TableAccessor.setTabOrder(table, Optional.ofNullable(set.getString(ExtensionTabTable.ELEMENT_ORDER)).map(ElementOrder::deserialize).orElse(ElementOrder.values()));
+ TableAccessor.setTabIcon(table, extractIcon(set, "tab_icon"));
+
+ byTableID.put(tableID, table);
+ byPluginID.put(pluginID, byTableID);
+ }
+
+ return byPluginID;
+ }
+ };
+ }
+
+ private void extractColumns(ResultSet set, Table.Factory table) throws SQLException {
+ String col1 = set.getString(ExtensionTableProviderTable.COL_1);
+ if (col1 != null) {
+ table.columnOne(col1, extractIcon(set, "i1"));
+ }
+ String col2 = set.getString(ExtensionTableProviderTable.COL_2);
+ if (col2 != null) {
+ table.columnTwo(col2, extractIcon(set, "i2"));
+ }
+ String col3 = set.getString(ExtensionTableProviderTable.COL_3);
+ if (col3 != null) {
+ table.columnThree(col3, extractIcon(set, "i3"));
+ }
+ String col4 = set.getString(ExtensionTableProviderTable.COL_4);
+ if (col4 != null) {
+ table.columnFour(col4, extractIcon(set, "i4"));
+ }
+ }
+
+ private Icon extractIcon(ResultSet set, String iconColumnName) throws SQLException {
+ String iconName = set.getString(iconColumnName + "_name");
+ if (iconName == null) {
+ return null;
+ }
+ return new Icon(
+ Family.getByName(set.getString(iconColumnName + "_family")).orElse(Family.SOLID),
+ iconName,
+ Color.getByName(set.getString(iconColumnName + "_color")).orElse(Color.NONE)
+ );
+ }
+}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionServerDataQuery.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionServerDataQuery.java
index a855eaa64..a88d91ced 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionServerDataQuery.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionServerDataQuery.java
@@ -83,6 +83,7 @@ public class ExtensionServerDataQuery implements Query
combine(extensionDataByPluginID, db.query(new ExtensionAggregateDoublesQuery(serverUUID)));
combine(extensionDataByPluginID, db.query(new ExtensionAggregateNumbersQuery(serverUUID)));
combine(extensionDataByPluginID, db.query(new ExtensionAggregatePercentagesQuery(serverUUID)));
+ combine(extensionDataByPluginID, db.query(new ExtensionServerTablesQuery(serverUUID)));
return combineWithExtensionInfo(extensionsOfServer, extensionDataByPluginID);
}
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionServerPlayerDataTableQuery.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionServerPlayerDataTableQuery.java
new file mode 100644
index 000000000..73b000c6c
--- /dev/null
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionServerPlayerDataTableQuery.java
@@ -0,0 +1,151 @@
+/*
+ * 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 GNU 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
+ * GNU 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 .
+ */
+package com.djrapitops.plan.extension.implementation.storage.queries;
+
+import com.djrapitops.plan.db.SQLDB;
+import com.djrapitops.plan.db.access.Query;
+import com.djrapitops.plan.db.access.QueryStatement;
+import com.djrapitops.plan.db.sql.tables.*;
+import com.djrapitops.plan.extension.FormatType;
+import com.djrapitops.plan.extension.icon.Color;
+import com.djrapitops.plan.extension.icon.Family;
+import com.djrapitops.plan.extension.icon.Icon;
+import com.djrapitops.plan.extension.implementation.results.*;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+import static com.djrapitops.plan.db.sql.parsing.Sql.*;
+
+/**
+ * Query Extension data of x most recent players on a server.
+ *
+ * Returns Map: Player UUID - ExtensionTabData (container for provider based data)
+ *
+ * @author Rsl1122
+ */
+public class ExtensionServerPlayerDataTableQuery implements Query> {
+
+ private final UUID serverUUID;
+ private final int xMostRecentPlayers;
+
+ public ExtensionServerPlayerDataTableQuery(UUID serverUUID, int xMostRecentPlayers) {
+ this.serverUUID = serverUUID;
+ this.xMostRecentPlayers = xMostRecentPlayers;
+ }
+
+ @Override
+ public Map executeQuery(SQLDB db) {
+ return db.query(fetchIncompletePlayerDataByPluginID());
+ }
+
+ private Query> fetchIncompletePlayerDataByPluginID() {
+ String selectLimitedNumberOfPlayerUUIDsByLastSeenDate = SELECT +
+ SessionsTable.TABLE_NAME + '.' + SessionsTable.USER_UUID +
+ ",MAX(" + SessionsTable.SESSION_END + ") as last_seen" +
+ FROM + SessionsTable.TABLE_NAME +
+ GROUP_BY + SessionsTable.USER_UUID +
+ ORDER_BY + SessionsTable.SESSION_END + " DESC LIMIT ?";
+
+ String sql = SELECT +
+ "v1." + ExtensionPlayerValueTable.USER_UUID + " as uuid," +
+ "v1." + ExtensionPlayerValueTable.DOUBLE_VALUE + " as double_value," +
+ "v1." + ExtensionPlayerValueTable.LONG_VALUE + " as long_value," +
+ "v1." + ExtensionPlayerValueTable.STRING_VALUE + " as string_value," +
+ "p1." + ExtensionProviderTable.PROVIDER_NAME + " as provider_name," +
+ "p1." + ExtensionProviderTable.TEXT + " as text," +
+ "p1." + ExtensionProviderTable.FORMAT_TYPE + " as format_type," +
+ "p1." + ExtensionProviderTable.IS_PLAYER_NAME + " as is_player_name," +
+ "i1." + ExtensionIconTable.ICON_NAME + " as provider_icon_name," +
+ "i1." + ExtensionIconTable.FAMILY + " as provider_icon_family" +
+ FROM + ExtensionPlayerValueTable.TABLE_NAME + " v1" +
+ INNER_JOIN + '(' + selectLimitedNumberOfPlayerUUIDsByLastSeenDate + ") as last_seen_q on last_seen_q.uuid=v1." + ExtensionPlayerValueTable.USER_UUID +
+ INNER_JOIN + ExtensionProviderTable.TABLE_NAME + " p1 on p1." + ExtensionProviderTable.ID + "=v1." + ExtensionPlayerValueTable.PROVIDER_ID +
+ INNER_JOIN + ExtensionPluginTable.TABLE_NAME + " e1 on e1." + ExtensionPluginTable.ID + "=p1." + ExtensionProviderTable.PLUGIN_ID +
+ LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i1 on i1." + ExtensionIconTable.ID + "=p1." + ExtensionProviderTable.ICON_ID +
+ WHERE + "e1." + ExtensionPluginTable.SERVER_UUID + "=?" +
+ AND + " v1." + ExtensionPlayerValueTable.BOOLEAN_VALUE + IS_NULL + // Don't select Boolean value rows
+ AND + " v1." + ExtensionPlayerValueTable.PERCENTAGE_VALUE + IS_NULL + // Don't select Percentage value rows
+ AND + " p1." + ExtensionProviderTable.IS_PLAYER_NAME + "=?";
+
+ return new QueryStatement>(sql, 1000) {
+ @Override
+ public void prepare(PreparedStatement statement) throws SQLException {
+ statement.setInt(1, xMostRecentPlayers); // Limit to x most recently seen players
+ statement.setString(2, serverUUID.toString());
+ statement.setBoolean(3, false); // Don't select player_name String values
+ }
+
+ @Override
+ public Map processResults(ResultSet set) throws SQLException {
+ return extractDataByPlayer(set);
+ }
+ };
+ }
+
+ private Map extractDataByPlayer(ResultSet set) throws SQLException {
+ Map dataByPlayer = new HashMap<>();
+
+ while (set.next()) {
+ UUID playerUUID = UUID.fromString(set.getString("uuid"));
+ ExtensionTabData.Factory data = dataByPlayer.getOrDefault(playerUUID, new ExtensionTabData.Factory(null));
+
+ ExtensionDescriptive extensionDescriptive = extractDescriptive(set);
+ extractAndPutDataTo(data, extensionDescriptive, set);
+
+ dataByPlayer.put(playerUUID, data);
+ }
+ return dataByPlayer.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().build()));
+ }
+
+ private void extractAndPutDataTo(ExtensionTabData.Factory extensionTab, ExtensionDescriptive descriptive, ResultSet set) throws SQLException {
+ double doubleValue = set.getDouble(ExtensionPlayerValueTable.DOUBLE_VALUE);
+ if (!set.wasNull()) {
+ extensionTab.putDoubleData(new ExtensionDoubleData(descriptive, doubleValue));
+ return;
+ }
+
+ long numberValue = set.getLong(ExtensionPlayerValueTable.LONG_VALUE);
+ if (!set.wasNull()) {
+ FormatType formatType = FormatType.getByName(set.getString(ExtensionProviderTable.FORMAT_TYPE)).orElse(FormatType.NONE);
+ extensionTab.putNumberData(new ExtensionNumberData(descriptive, formatType, numberValue));
+ return;
+ }
+
+ String stringValue = set.getString(ExtensionPlayerValueTable.STRING_VALUE);
+ if (stringValue != null) {
+ boolean isPlayerName = false;
+ extensionTab.putStringData(new ExtensionStringData(descriptive, isPlayerName, stringValue));
+ }
+ }
+
+ private ExtensionDescriptive extractDescriptive(ResultSet set) throws SQLException {
+ String name = set.getString("provider_name");
+ String text = set.getString(ExtensionProviderTable.TEXT);
+
+ String iconName = set.getString("provider_icon_name");
+ Family family = Family.getByName(set.getString("provider_icon_family")).orElse(Family.SOLID);
+ Icon icon = new Icon(family, iconName, Color.NONE);
+
+ return new ExtensionDescriptive(name, text, null, icon, 0);
+ }
+}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionServerTablesQuery.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionServerTablesQuery.java
new file mode 100644
index 000000000..047b7b2be
--- /dev/null
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionServerTablesQuery.java
@@ -0,0 +1,284 @@
+/*
+ * 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 GNU 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
+ * GNU 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 .
+ */
+package com.djrapitops.plan.extension.implementation.storage.queries;
+
+import com.djrapitops.plan.db.SQLDB;
+import com.djrapitops.plan.db.access.Query;
+import com.djrapitops.plan.db.access.QueryStatement;
+import com.djrapitops.plan.db.sql.tables.ExtensionIconTable;
+import com.djrapitops.plan.db.sql.tables.ExtensionServerTableValueTable;
+import com.djrapitops.plan.db.sql.tables.ExtensionTabTable;
+import com.djrapitops.plan.db.sql.tables.ExtensionTableProviderTable;
+import com.djrapitops.plan.extension.ElementOrder;
+import com.djrapitops.plan.extension.icon.Color;
+import com.djrapitops.plan.extension.icon.Family;
+import com.djrapitops.plan.extension.icon.Icon;
+import com.djrapitops.plan.extension.implementation.TabInformation;
+import com.djrapitops.plan.extension.implementation.results.ExtensionTabData;
+import com.djrapitops.plan.extension.implementation.results.ExtensionTableData;
+import com.djrapitops.plan.extension.implementation.results.server.ExtensionServerData;
+import com.djrapitops.plan.extension.table.Table;
+import com.djrapitops.plan.extension.table.TableAccessor;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.*;
+
+import static com.djrapitops.plan.db.sql.parsing.Sql.*;
+
+/**
+ * Query server tables from tableprovider table.
+ *
+ * Returns Map: PluginID - ExtensionServerData.Factory.
+ *
+ * How it is done:
+ * - TableProviders are queried and formed into Table.Factory objects sorted into a multi-map: PluginID - TableID - Table.Factory
+ * - Table values are queried and merged into the above multimap
+ * - Data query is sorted into a multi-map: PluginID - Tab Name - Tab Data
+ * - (Tab Name can be empty.)
+ * - Multi-map is sorted into ExtensionServerData objects by PluginID, one per ID
+ *
+ * There are multiple data extraction methods to make extracting the value query easier.
+ *
+ * @author Rsl1122
+ */
+public class ExtensionServerTablesQuery implements Query> {
+
+ private final UUID serverUUID;
+
+ public ExtensionServerTablesQuery(UUID serverUUID) {
+ this.serverUUID = serverUUID;
+ }
+
+ @Override
+ public Map executeQuery(SQLDB db) {
+ Map> tablesByPluginIDAndTableID = db.query(queryTableProviders());
+ Map> tablesWithValues = db.query(queryTableValues(tablesByPluginIDAndTableID));
+
+ Map> tabDataByPluginID = mapToTabsByPluginID(tablesWithValues);
+ return ExtensionServerDataQuery.flatMapToServerData(tabDataByPluginID);
+ }
+
+ /**
+ * @param tablesByPluginIDAndTableID {@code >}
+ * @return {@code }
+ */
+ private Map> mapToTabsByPluginID(Map> tablesByPluginIDAndTableID) {
+ Map> byPluginID = new HashMap<>();
+
+ for (Map.Entry> entry : tablesByPluginIDAndTableID.entrySet()) {
+ Integer pluginID = entry.getKey();
+ Map byTabName = byPluginID.getOrDefault(pluginID, new HashMap<>());
+ for (Table.Factory table : entry.getValue().values()) {
+ // Extra Table information
+ String tableName = TableAccessor.getTableName(table);
+ Color tableColor = TableAccessor.getColor(table);
+
+ // Extra tab information
+ String tabName = TableAccessor.getTabName(table);
+ int tabPriority = TableAccessor.getTabPriority(table);
+ ElementOrder[] tabOrder = TableAccessor.getTabOrder(table);
+ Icon tabIcon = TableAccessor.getTabIcon(table);
+
+ ExtensionTabData.Factory tab = byTabName.getOrDefault(tabName, new ExtensionTabData.Factory(new TabInformation(tabName, tabIcon, tabOrder, tabPriority)));
+ tab.putTableData(new ExtensionTableData(
+ tableName, table.build(), tableColor
+ ));
+ byTabName.put(tabName, tab);
+ }
+ byPluginID.put(pluginID, byTabName);
+ }
+
+ return byPluginID;
+ }
+
+ // Map: >
+ private Query>> queryTableValues(Map> tables) {
+ String selectTableValues = SELECT +
+ ExtensionTableProviderTable.PLUGIN_ID + ',' +
+ ExtensionServerTableValueTable.TABLE_ID + ',' +
+ ExtensionServerTableValueTable.VALUE_1 + ',' +
+ ExtensionServerTableValueTable.VALUE_2 + ',' +
+ ExtensionServerTableValueTable.VALUE_3 + ',' +
+ ExtensionServerTableValueTable.VALUE_4 + ',' +
+ ExtensionServerTableValueTable.VALUE_5 +
+ FROM + ExtensionServerTableValueTable.TABLE_NAME +
+ INNER_JOIN + ExtensionTableProviderTable.TABLE_NAME + " on " + ExtensionTableProviderTable.TABLE_NAME + '.' + ExtensionTableProviderTable.ID + '=' + ExtensionServerTableValueTable.TABLE_NAME + '.' + ExtensionServerTableValueTable.TABLE_ID +
+ WHERE + ExtensionServerTableValueTable.SERVER_UUID + "=?";
+
+ return new QueryStatement>>(selectTableValues, 10000) {
+ @Override
+ public void prepare(PreparedStatement statement) throws SQLException {
+ statement.setString(1, serverUUID.toString());
+ }
+
+ @Override
+ public Map> processResults(ResultSet set) throws SQLException {
+ while (set.next()) {
+ Table.Factory table = getTable(set);
+ if (table == null) {
+ continue;
+ }
+
+ Object[] row = extractTableRow(set);
+ if (row.length > 0) {
+ table.addRow(row);
+ }
+ }
+ return tables;
+ }
+
+ private Table.Factory getTable(ResultSet set) throws SQLException {
+ int pluginID = set.getInt(ExtensionTableProviderTable.PLUGIN_ID);
+ Map byTableID = tables.get(pluginID);
+ if (byTableID == null) {
+ return null;
+ }
+ int tableID = set.getInt(ExtensionServerTableValueTable.TABLE_ID);
+ return byTableID.get(tableID);
+ }
+ };
+ }
+
+ private Object[] extractTableRow(ResultSet set) throws SQLException {
+ List row = new ArrayList<>();
+ for (int i = 1; i <= 5; i++) {
+ String columnName = "col_" + i + "_value"; // See ExtensionServerTableValueTable.VALUE_1
+ String value = set.getString(columnName);
+ if (value == null) {
+ return row.toArray(new Object[0]);
+ }
+ row.add(value);
+ }
+ return row.toArray(new Object[0]);
+ }
+
+ // Map: >
+ private Query>> queryTableProviders() {
+ String selectTables = SELECT +
+ "p1." + ExtensionTableProviderTable.ID + " as table_id," +
+ "p1." + ExtensionTableProviderTable.PLUGIN_ID + " as plugin_id," +
+ "p1." + ExtensionTableProviderTable.PROVIDER_NAME + " as table_name," +
+ "p1." + ExtensionTableProviderTable.COLOR + " as table_color," +
+ ExtensionTableProviderTable.COL_1 + ',' +
+ ExtensionTableProviderTable.COL_2 + ',' +
+ ExtensionTableProviderTable.COL_3 + ',' +
+ ExtensionTableProviderTable.COL_4 + ',' +
+ ExtensionTableProviderTable.COL_5 + ',' +
+ "t1." + ExtensionTabTable.TAB_NAME + " as tab_name," +
+ "t1." + ExtensionTabTable.TAB_PRIORITY + " as tab_priority," +
+ "t1." + ExtensionTabTable.ELEMENT_ORDER + " as element_order," +
+ "i1." + ExtensionIconTable.ICON_NAME + " as i1_name," +
+ "i1." + ExtensionIconTable.FAMILY + " as i1_family," +
+ "i1." + ExtensionIconTable.COLOR + " as i1_color," +
+ "i2." + ExtensionIconTable.ICON_NAME + " as i2_name," +
+ "i2." + ExtensionIconTable.FAMILY + " as i2_family," +
+ "i2." + ExtensionIconTable.COLOR + " as i2_color," +
+ "i3." + ExtensionIconTable.ICON_NAME + " as i3_name," +
+ "i3." + ExtensionIconTable.FAMILY + " as i3_family," +
+ "i3." + ExtensionIconTable.COLOR + " as i3_color," +
+ "i4." + ExtensionIconTable.ICON_NAME + " as i4_name," +
+ "i4." + ExtensionIconTable.FAMILY + " as i4_family," +
+ "i4." + ExtensionIconTable.COLOR + " as i4_color," +
+ "i5." + ExtensionIconTable.ICON_NAME + " as i5_name," +
+ "i5." + ExtensionIconTable.FAMILY + " as i5_family," +
+ "i5." + ExtensionIconTable.COLOR + " as i5_color," +
+ "i6." + ExtensionIconTable.ICON_NAME + " as tab_icon_name," +
+ "i6." + ExtensionIconTable.FAMILY + " as tab_icon_family," +
+ "i6." + ExtensionIconTable.COLOR + " as tab_icon_color" +
+ FROM + ExtensionTableProviderTable.TABLE_NAME + " p1" +
+ INNER_JOIN + ExtensionServerTableValueTable.TABLE_NAME + " v1 on v1." + ExtensionServerTableValueTable.TABLE_ID + "=p1." + ExtensionTableProviderTable.ID +
+ LEFT_JOIN + ExtensionTabTable.TABLE_NAME + " t1 on t1." + ExtensionTabTable.ID + "=p1." + ExtensionTableProviderTable.TAB_ID +
+ LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i1 on i1." + ExtensionIconTable.ID + "=p1." + ExtensionTableProviderTable.ICON_1_ID +
+ LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i2 on i2." + ExtensionIconTable.ID + "=p1." + ExtensionTableProviderTable.ICON_2_ID +
+ LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i3 on i3." + ExtensionIconTable.ID + "=p1." + ExtensionTableProviderTable.ICON_3_ID +
+ LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i4 on i4." + ExtensionIconTable.ID + "=p1." + ExtensionTableProviderTable.ICON_4_ID +
+ LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i5 on i5." + ExtensionIconTable.ID + "=p1." + ExtensionTableProviderTable.ICON_5_ID +
+ LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i6 on i6." + ExtensionIconTable.ID + "=t1." + ExtensionTabTable.ICON_ID +
+ WHERE + "v1." + ExtensionServerTableValueTable.SERVER_UUID + "=?";
+
+ return new QueryStatement>>(selectTables, 100) {
+ @Override
+ public void prepare(PreparedStatement statement) throws SQLException {
+ statement.setString(1, serverUUID.toString());
+ }
+
+ @Override
+ public Map> processResults(ResultSet set) throws SQLException {
+ Map> byPluginID = new HashMap<>();
+
+ while (set.next()) {
+ int pluginID = set.getInt("plugin_id");
+ Map byTableID = byPluginID.getOrDefault(pluginID, new HashMap<>());
+
+ int tableID = set.getInt("table_id");
+ Table.Factory table = Table.builder();
+
+ extractColumns(set, table);
+
+ TableAccessor.setColor(table, Color.getByName(set.getString("table_color")).orElse(Color.NONE));
+ TableAccessor.setTableName(table, set.getString("table_name"));
+ TableAccessor.setTabName(table, Optional.ofNullable(set.getString("tab_name")).orElse(""));
+ TableAccessor.setTabPriority(table, Optional.of(set.getInt("tab_priority")).orElse(100));
+ TableAccessor.setTabOrder(table, Optional.ofNullable(set.getString(ExtensionTabTable.ELEMENT_ORDER)).map(ElementOrder::deserialize).orElse(ElementOrder.values()));
+ TableAccessor.setTabIcon(table, extractIcon(set, "tab_icon"));
+
+ byTableID.put(tableID, table);
+ byPluginID.put(pluginID, byTableID);
+ }
+
+ return byPluginID;
+ }
+ };
+ }
+
+ private void extractColumns(ResultSet set, Table.Factory table) throws SQLException {
+ String col1 = set.getString(ExtensionTableProviderTable.COL_1);
+ if (col1 != null) {
+ table.columnOne(col1, extractIcon(set, "i1"));
+ }
+ String col2 = set.getString(ExtensionTableProviderTable.COL_2);
+ if (col2 != null) {
+ table.columnTwo(col2, extractIcon(set, "i2"));
+ }
+ String col3 = set.getString(ExtensionTableProviderTable.COL_3);
+ if (col3 != null) {
+ table.columnThree(col3, extractIcon(set, "i3"));
+ }
+ String col4 = set.getString(ExtensionTableProviderTable.COL_4);
+ if (col4 != null) {
+ table.columnFour(col4, extractIcon(set, "i4"));
+ }
+ String col5 = set.getString(ExtensionTableProviderTable.COL_5);
+ if (col5 != null) {
+ table.columnFive(col5, extractIcon(set, "i5"));
+ }
+ }
+
+ private Icon extractIcon(ResultSet set, String iconColumnName) throws SQLException {
+ String iconName = set.getString(iconColumnName + "_name");
+ if (iconName == null) {
+ return null;
+ }
+ return new Icon(
+ Family.getByName(set.getString(iconColumnName + "_family")).orElse(Family.SOLID),
+ iconName,
+ Color.getByName(set.getString(iconColumnName + "_color")).orElse(Color.NONE)
+ );
+ }
+}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/StoreTabInformationTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/StoreTabInformationTransaction.java
index 0ad9c8509..a4c44b37d 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/StoreTabInformationTransaction.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/StoreTabInformationTransaction.java
@@ -68,14 +68,14 @@ public class StoreTabInformationTransaction extends Transaction {
" SET " +
ExtensionTabTable.TAB_PRIORITY + "=?," +
ExtensionTabTable.ELEMENT_ORDER + "=?," +
- ExtensionTabTable.ICON_ID + "=" + ExtensionIconTable.STATEMENT_SELECT_ICON_ID + "," +
+ ExtensionTabTable.ICON_ID + "=" + ExtensionIconTable.STATEMENT_SELECT_ICON_ID +
WHERE + ExtensionTabTable.PLUGIN_ID + "=" + ExtensionPluginTable.STATEMENT_SELECT_PLUGIN_ID +
AND + ExtensionTabTable.TAB_NAME + "=?";
return new ExecStatement(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setInt(1, tabInformation.getTabPriority());
- statement.setString(2, tabInformation.getTabElementOrder().map(ElementOrder::serialize).orElse(null));
+ statement.setString(2, ElementOrder.serialize(tabInformation.getTabElementOrder().orElse(ElementOrder.values())));
ExtensionIconTable.set3IconValuesToStatement(statement, 3, tabInformation.getTabIcon());
ExtensionPluginTable.set2PluginValuesToStatement(statement, 6, pluginName, serverUUID);
statement.setString(8, tabInformation.getTabName());
@@ -88,14 +88,14 @@ public class StoreTabInformationTransaction extends Transaction {
ExtensionTabTable.TAB_NAME + "," +
ExtensionTabTable.ELEMENT_ORDER + "," +
ExtensionTabTable.TAB_PRIORITY + "," +
- ExtensionTabTable.ICON_ID +
+ ExtensionTabTable.ICON_ID + "," +
ExtensionTabTable.PLUGIN_ID +
") VALUES (?,?,?," + ExtensionIconTable.STATEMENT_SELECT_ICON_ID + "," + ExtensionPluginTable.STATEMENT_SELECT_PLUGIN_ID + ")";
return new ExecStatement(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, tabInformation.getTabName());
- statement.setString(2, tabInformation.getTabElementOrder().map(ElementOrder::serialize).orElse(null));
+ statement.setString(2, ElementOrder.serialize(tabInformation.getTabElementOrder().orElse(ElementOrder.values())));
statement.setInt(3, tabInformation.getTabPriority());
ExtensionIconTable.set3IconValuesToStatement(statement, 4, tabInformation.getTabIcon());
ExtensionPluginTable.set2PluginValuesToStatement(statement, 7, pluginName, serverUUID);
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/providers/StoreTableProviderTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/providers/StoreTableProviderTransaction.java
new file mode 100644
index 000000000..5b203abe9
--- /dev/null
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/providers/StoreTableProviderTransaction.java
@@ -0,0 +1,173 @@
+/*
+ * 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 GNU 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
+ * GNU 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 .
+ */
+package com.djrapitops.plan.extension.implementation.storage.transactions.providers;
+
+import com.djrapitops.plan.db.access.ExecStatement;
+import com.djrapitops.plan.db.access.Executable;
+import com.djrapitops.plan.db.access.transactions.Transaction;
+import com.djrapitops.plan.db.sql.tables.ExtensionIconTable;
+import com.djrapitops.plan.db.sql.tables.ExtensionPluginTable;
+import com.djrapitops.plan.db.sql.tables.ExtensionTabTable;
+import com.djrapitops.plan.extension.icon.Color;
+import com.djrapitops.plan.extension.icon.Icon;
+import com.djrapitops.plan.extension.implementation.ProviderInformation;
+import com.djrapitops.plan.extension.table.Table;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.util.UUID;
+
+import static com.djrapitops.plan.db.sql.parsing.Sql.AND;
+import static com.djrapitops.plan.db.sql.parsing.Sql.WHERE;
+import static com.djrapitops.plan.db.sql.tables.ExtensionTableProviderTable.*;
+
+/**
+ * Transaction to store information about a {@link com.djrapitops.plan.extension.implementation.providers.TableDataProvider}.
+ *
+ * @author Rsl1122
+ */
+public class StoreTableProviderTransaction extends Transaction {
+
+ private final UUID serverUUID;
+ private final ProviderInformation providerInformation;
+ private final Color tableColor;
+ private final Table table;
+
+ public StoreTableProviderTransaction(UUID serverUUID, ProviderInformation providerInformation, Color tableColor, Table table) {
+ this.providerInformation = providerInformation;
+ this.tableColor = tableColor;
+ this.table = table;
+ this.serverUUID = serverUUID;
+ }
+
+ @Override
+ protected void performOperations() {
+ execute(storeProvider());
+ }
+
+ private Executable storeProvider() {
+ return connection -> {
+ if (!updateProvider().execute(connection)) {
+ return insertProvider().execute(connection);
+ }
+ return false;
+ };
+ }
+
+ private Executable updateProvider() {
+ String[] columns = table.getColumns();
+ Icon[] icons = table.getIcons();
+
+ String sql = "UPDATE " + TABLE_NAME + " SET " +
+ COLOR + "=?," +
+ COL_1 + "=?," +
+ COL_2 + "=?," +
+ COL_3 + "=?," +
+ COL_4 + "=?," +
+ COL_5 + "=?," +
+ CONDITION + "=?," +
+ TAB_ID + '=' + ExtensionTabTable.STATEMENT_SELECT_TAB_ID + ',' +
+ ICON_1_ID + '=' + ExtensionIconTable.STATEMENT_SELECT_ICON_ID + ',' +
+ ICON_2_ID + '=' + ExtensionIconTable.STATEMENT_SELECT_ICON_ID + ',' +
+ ICON_3_ID + '=' + ExtensionIconTable.STATEMENT_SELECT_ICON_ID + ',' +
+ ICON_4_ID + '=' + ExtensionIconTable.STATEMENT_SELECT_ICON_ID + ',' +
+ ICON_5_ID + '=' + ExtensionIconTable.STATEMENT_SELECT_ICON_ID +
+ WHERE + PROVIDER_NAME + "=?" +
+ AND + PLUGIN_ID + '=' + ExtensionPluginTable.STATEMENT_SELECT_PLUGIN_ID;
+
+ return new ExecStatement(sql) {
+ @Override
+ public void prepare(PreparedStatement statement) throws SQLException {
+ statement.setString(1, tableColor.name());
+ setStringOrNull(statement, 2, columns[0]);
+ setStringOrNull(statement, 3, columns[1]);
+ setStringOrNull(statement, 4, columns[2]);
+ setStringOrNull(statement, 5, columns[3]);
+ setStringOrNull(statement, 6, columns[4]);
+ setStringOrNull(statement, 7, providerInformation.getCondition().orElse(null));
+ ExtensionTabTable.set3TabValuesToStatement(statement, 8, providerInformation.getTab().orElse("No Tab"), providerInformation.getPluginName(), serverUUID);
+ ExtensionIconTable.set3IconValuesToStatement(statement, 11, icons[0]);
+ ExtensionIconTable.set3IconValuesToStatement(statement, 14, icons[1]);
+ ExtensionIconTable.set3IconValuesToStatement(statement, 17, icons[2]);
+ ExtensionIconTable.set3IconValuesToStatement(statement, 20, icons[3]);
+ ExtensionIconTable.set3IconValuesToStatement(statement, 23, icons[4]);
+ statement.setString(26, providerInformation.getName());
+ ExtensionPluginTable.set2PluginValuesToStatement(statement, 27, providerInformation.getPluginName(), serverUUID);
+ }
+ };
+ }
+
+ private Executable insertProvider() {
+ String[] columns = table.getColumns();
+ Icon[] icons = table.getIcons();
+
+ String sql = "INSERT INTO " + TABLE_NAME + '(' +
+ PROVIDER_NAME + ',' +
+ COLOR + ',' +
+ COL_1 + ',' +
+ COL_2 + ',' +
+ COL_3 + ',' +
+ COL_4 + ',' +
+ COL_5 + ',' +
+ CONDITION + ',' +
+ TAB_ID + ',' +
+ PLUGIN_ID + ',' +
+ ICON_1_ID + ',' +
+ ICON_2_ID + ',' +
+ ICON_3_ID + ',' +
+ ICON_4_ID + ',' +
+ ICON_5_ID +
+ ") VALUES (?,?,?,?,?,?,?,?," +
+ ExtensionTabTable.STATEMENT_SELECT_TAB_ID + ',' +
+ ExtensionPluginTable.STATEMENT_SELECT_PLUGIN_ID + ',' +
+ ExtensionIconTable.STATEMENT_SELECT_ICON_ID + ',' +
+ ExtensionIconTable.STATEMENT_SELECT_ICON_ID + ',' +
+ ExtensionIconTable.STATEMENT_SELECT_ICON_ID + ',' +
+ ExtensionIconTable.STATEMENT_SELECT_ICON_ID + ',' +
+ ExtensionIconTable.STATEMENT_SELECT_ICON_ID + ')';
+
+ return new ExecStatement(sql) {
+ @Override
+ public void prepare(PreparedStatement statement) throws SQLException {
+ statement.setString(1, providerInformation.getName());
+ statement.setString(2, tableColor.name());
+ setStringOrNull(statement, 3, columns[0]);
+ setStringOrNull(statement, 4, columns[1]);
+ setStringOrNull(statement, 5, columns[2]);
+ setStringOrNull(statement, 6, columns[3]);
+ setStringOrNull(statement, 7, columns[4]);
+ setStringOrNull(statement, 8, providerInformation.getCondition().orElse(null));
+ ExtensionTabTable.set3TabValuesToStatement(statement, 9, providerInformation.getTab().orElse("No Tab"), providerInformation.getPluginName(), serverUUID);
+ ExtensionPluginTable.set2PluginValuesToStatement(statement, 12, providerInformation.getPluginName(), serverUUID);
+ ExtensionIconTable.set3IconValuesToStatement(statement, 14, icons[0]);
+ ExtensionIconTable.set3IconValuesToStatement(statement, 17, icons[1]);
+ ExtensionIconTable.set3IconValuesToStatement(statement, 20, icons[2]);
+ ExtensionIconTable.set3IconValuesToStatement(statement, 23, icons[3]);
+ ExtensionIconTable.set3IconValuesToStatement(statement, 26, icons[4]);
+ }
+ };
+ }
+
+ private void setStringOrNull(PreparedStatement statement, int index, String value) throws SQLException {
+ if (value != null) {
+ statement.setString(index, value);
+ } else {
+ statement.setNull(index, Types.VARCHAR);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/RemoveInvalidResultsTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/RemoveInvalidResultsTransaction.java
index dc0aa5613..4b0913ff7 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/RemoveInvalidResultsTransaction.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/RemoveInvalidResultsTransaction.java
@@ -19,9 +19,7 @@ package com.djrapitops.plan.extension.implementation.storage.transactions.result
import com.djrapitops.plan.db.access.ExecStatement;
import com.djrapitops.plan.db.access.Executable;
import com.djrapitops.plan.db.access.transactions.Transaction;
-import com.djrapitops.plan.db.sql.tables.ExtensionPlayerValueTable;
-import com.djrapitops.plan.db.sql.tables.ExtensionPluginTable;
-import com.djrapitops.plan.db.sql.tables.ExtensionProviderTable;
+import com.djrapitops.plan.db.sql.tables.*;
import java.sql.PreparedStatement;
import java.sql.SQLException;
@@ -51,12 +49,17 @@ public class RemoveInvalidResultsTransaction extends Transaction {
@Override
protected void performOperations() {
for (String invalidatedMethod : invalidatedMethods) {
- execute(deleteInvalidMethodResults(invalidatedMethod));
+ execute(deleteInvalidPlayerMethodResults(invalidatedMethod));
+ execute(deleteInvalidServerMethodResults(invalidatedMethod));
execute(deleteInvalidMethodProvider(invalidatedMethod));
+
+ execute(deleteInvalidPlayerTableResults(invalidatedMethod));
+ execute(deleteInvalidServerTableResults(invalidatedMethod));
+ execute(deleteInvalidTableProvider(invalidatedMethod));
}
}
- private Executable deleteInvalidMethodResults(String invalidMethod) {
+ private Executable deleteInvalidPlayerMethodResults(String invalidMethod) {
String sql = "DELETE FROM " + ExtensionPlayerValueTable.TABLE_NAME +
WHERE + ExtensionPlayerValueTable.PROVIDER_ID + "=" + ExtensionProviderTable.STATEMENT_SELECT_PROVIDER_ID;
return new ExecStatement(sql) {
@@ -67,10 +70,56 @@ public class RemoveInvalidResultsTransaction extends Transaction {
};
}
+ private Executable deleteInvalidServerMethodResults(String invalidMethod) {
+ String sql = "DELETE FROM " + ExtensionServerValueTable.TABLE_NAME +
+ WHERE + ExtensionServerValueTable.PROVIDER_ID + "=" + ExtensionProviderTable.STATEMENT_SELECT_PROVIDER_ID;
+ return new ExecStatement(sql) {
+ @Override
+ public void prepare(PreparedStatement statement) throws SQLException {
+ ExtensionProviderTable.set3PluginValuesToStatement(statement, 1, invalidMethod, pluginName, serverUUID);
+ }
+ };
+ }
+
+ private Executable deleteInvalidPlayerTableResults(String invalidMethod) {
+ String sql = "DELETE FROM " + ExtensionPlayerTableValueTable.TABLE_NAME +
+ WHERE + ExtensionPlayerTableValueTable.TABLE_ID + "=" + ExtensionTableProviderTable.STATEMENT_SELECT_TABLE_ID;
+ return new ExecStatement(sql) {
+ @Override
+ public void prepare(PreparedStatement statement) throws SQLException {
+ ExtensionTableProviderTable.set3PluginValuesToStatement(statement, 1, invalidMethod, pluginName, serverUUID);
+ }
+ };
+ }
+
+ private Executable deleteInvalidServerTableResults(String invalidMethod) {
+ String sql = "DELETE FROM " + ExtensionServerTableValueTable.TABLE_NAME +
+ WHERE + ExtensionServerTableValueTable.TABLE_ID + "=" + ExtensionTableProviderTable.STATEMENT_SELECT_TABLE_ID;
+ return new ExecStatement(sql) {
+ @Override
+ public void prepare(PreparedStatement statement) throws SQLException {
+ ExtensionTableProviderTable.set3PluginValuesToStatement(statement, 1, invalidMethod, pluginName, serverUUID);
+ }
+ };
+ }
+
private Executable deleteInvalidMethodProvider(String invalidMethod) {
String sql = "DELETE FROM " + ExtensionProviderTable.TABLE_NAME +
WHERE + ExtensionProviderTable.PROVIDER_NAME + "=?" +
- AND + ExtensionProviderTable.PLUGIN_ID + ExtensionPluginTable.STATEMENT_SELECT_PLUGIN_ID;
+ AND + ExtensionProviderTable.PLUGIN_ID + '=' + ExtensionPluginTable.STATEMENT_SELECT_PLUGIN_ID;
+ return new ExecStatement(sql) {
+ @Override
+ public void prepare(PreparedStatement statement) throws SQLException {
+ statement.setString(1, invalidMethod);
+ ExtensionPluginTable.set2PluginValuesToStatement(statement, 2, pluginName, serverUUID);
+ }
+ };
+ }
+
+ private Executable deleteInvalidTableProvider(String invalidMethod) {
+ String sql = "DELETE FROM " + ExtensionTableProviderTable.TABLE_NAME +
+ WHERE + ExtensionTableProviderTable.TABLE_NAME + "=?" +
+ AND + ExtensionTableProviderTable.PLUGIN_ID + '=' + ExtensionPluginTable.STATEMENT_SELECT_PLUGIN_ID;
return new ExecStatement(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/RemoveUnsatisfiedConditionalResultsTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/RemoveUnsatisfiedConditionalPlayerResultsTransaction.java
similarity index 62%
rename from Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/RemoveUnsatisfiedConditionalResultsTransaction.java
rename to Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/RemoveUnsatisfiedConditionalPlayerResultsTransaction.java
index f1e62ad27..9f729a432 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/RemoveUnsatisfiedConditionalResultsTransaction.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/RemoveUnsatisfiedConditionalPlayerResultsTransaction.java
@@ -20,8 +20,10 @@ import com.djrapitops.plan.db.DBType;
import com.djrapitops.plan.db.access.ExecStatement;
import com.djrapitops.plan.db.access.Executable;
import com.djrapitops.plan.db.access.transactions.Transaction;
+import com.djrapitops.plan.db.sql.tables.ExtensionPlayerTableValueTable;
import com.djrapitops.plan.db.sql.tables.ExtensionPlayerValueTable;
import com.djrapitops.plan.db.sql.tables.ExtensionProviderTable;
+import com.djrapitops.plan.db.sql.tables.ExtensionTableProviderTable;
import java.sql.PreparedStatement;
import java.sql.SQLException;
@@ -39,37 +41,53 @@ import static com.djrapitops.plan.db.sql.parsing.Sql.*;
*
* @author Rsl1122
*/
-public class RemoveUnsatisfiedConditionalResultsTransaction extends Transaction {
+public class RemoveUnsatisfiedConditionalPlayerResultsTransaction extends Transaction {
+
+ private final String providerTable;
+ private final String playerValueTable;
+ private final String playerTableValueTable;
+ private final String tableTable;
+
+ public RemoveUnsatisfiedConditionalPlayerResultsTransaction() {
+ providerTable = ExtensionProviderTable.TABLE_NAME;
+ playerValueTable = ExtensionPlayerValueTable.TABLE_NAME;
+ tableTable = ExtensionTableProviderTable.TABLE_NAME;
+ playerTableValueTable = ExtensionPlayerTableValueTable.TABLE_NAME;
+ }
@Override
protected void performOperations() {
- execute(deleteUnsatisfied());
+ String selectSatisfiedConditions = getSatisfiedConditionsSQL();
+
+ execute(deleteUnsatisfiedValues(selectSatisfiedConditions));
+ execute(deleteUnsatisfiedTableValues(selectSatisfiedConditions));
}
- private Executable deleteUnsatisfied() {
+ private String getSatisfiedConditionsSQL() {
String reversedCondition = dbType == DBType.SQLITE ? "'not_' || " + ExtensionProviderTable.PROVIDED_CONDITION : "CONCAT('not_'," + ExtensionProviderTable.PROVIDED_CONDITION + ')';
- String providerTable = ExtensionProviderTable.TABLE_NAME;
- String playerValueTable = ExtensionPlayerValueTable.TABLE_NAME;
-
String selectSatisfiedPositiveConditions = SELECT +
ExtensionProviderTable.PROVIDED_CONDITION + ',' +
- ExtensionPlayerValueTable.USER_UUID +
+ ExtensionProviderTable.PLUGIN_ID + ',' +
+ ExtensionPlayerTableValueTable.USER_UUID +
FROM + providerTable +
INNER_JOIN + playerValueTable + " on " + providerTable + '.' + ExtensionProviderTable.ID + "=" + ExtensionPlayerValueTable.PROVIDER_ID +
WHERE + ExtensionPlayerValueTable.BOOLEAN_VALUE + "=?" +
AND + ExtensionProviderTable.PROVIDED_CONDITION + IS_NOT_NULL;
String selectSatisfiedNegativeConditions = SELECT +
reversedCondition + " as " + ExtensionProviderTable.PROVIDED_CONDITION + ',' +
- ExtensionPlayerValueTable.USER_UUID +
+ ExtensionProviderTable.PLUGIN_ID + ',' +
+ ExtensionPlayerTableValueTable.USER_UUID +
FROM + providerTable +
INNER_JOIN + playerValueTable + " on " + providerTable + '.' + ExtensionProviderTable.ID + "=" + ExtensionPlayerValueTable.PROVIDER_ID +
WHERE + ExtensionPlayerValueTable.BOOLEAN_VALUE + "=?" +
AND + ExtensionProviderTable.PROVIDED_CONDITION + IS_NOT_NULL;
// Query contents: Set of provided_conditions
- String selectSatisfiedConditions = '(' + selectSatisfiedPositiveConditions + " UNION " + selectSatisfiedNegativeConditions + ") q1";
+ return '(' + selectSatisfiedPositiveConditions + " UNION " + selectSatisfiedNegativeConditions + ") q1";
+ }
+ private Executable deleteUnsatisfiedValues(String selectSatisfiedConditions) {
// Query contents:
// id | uuid | q1.uuid | condition | q1.provided_condition
// -- | ---- | ------- | --------- | ---------------------
@@ -87,6 +105,8 @@ public class RemoveUnsatisfiedConditionalResultsTransaction extends Transaction
"=q1." + ExtensionPlayerValueTable.USER_UUID +
AND + ExtensionProviderTable.CONDITION +
"=q1." + ExtensionProviderTable.PROVIDED_CONDITION +
+ AND + providerTable + '.' + ExtensionProviderTable.PLUGIN_ID +
+ "=q1." + ExtensionProviderTable.PLUGIN_ID +
')' +
WHERE + "q1." + ExtensionProviderTable.PROVIDED_CONDITION + IS_NULL + // Conditions that were not in the satisfied condition query
AND + ExtensionProviderTable.CONDITION + IS_NOT_NULL; // Ignore values that don't need condition
@@ -105,4 +125,32 @@ public class RemoveUnsatisfiedConditionalResultsTransaction extends Transaction
}
};
}
+
+ private Executable deleteUnsatisfiedTableValues(String selectSatisfiedConditions) {
+ String selectUnsatisfiedValueIDs = SELECT + ExtensionTableProviderTable.ID +
+ FROM + tableTable +
+ LEFT_JOIN + selectSatisfiedConditions + // Left join to preserve values that don't have their condition fulfilled
+ " on (" +
+ tableTable + '.' + ExtensionTableProviderTable.CONDITION +
+ "=q1." + ExtensionProviderTable.PROVIDED_CONDITION +
+ AND + tableTable + '.' + ExtensionTableProviderTable.PLUGIN_ID +
+ "=q1." + ExtensionProviderTable.PLUGIN_ID +
+ ')' +
+ WHERE + "q1." + ExtensionProviderTable.PROVIDED_CONDITION + IS_NULL + // Conditions that were not in the satisfied condition query
+ AND + ExtensionProviderTable.CONDITION + IS_NOT_NULL; // Ignore values that don't need condition
+
+ // Nested query here is required because MySQL limits update statements with nested queries:
+ // The nested query creates a temporary table that bypasses the same table query-update limit.
+ // Note: MySQL versions 5.6.7+ might optimize this nested query away leading to an exception.
+ String deleteValuesSQL = "DELETE FROM " + playerTableValueTable +
+ WHERE + ExtensionPlayerTableValueTable.TABLE_ID + " IN (" + SELECT + ExtensionTableProviderTable.ID + FROM + '(' + selectUnsatisfiedValueIDs + ") as ids)";
+
+ return new ExecStatement(deleteValuesSQL) {
+ @Override
+ public void prepare(PreparedStatement statement) throws SQLException {
+ statement.setBoolean(1, true); // Select provided conditions with 'true' value
+ statement.setBoolean(2, false); // Select negated conditions with 'false' value
+ }
+ };
+ }
}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/RemoveUnsatisfiedConditionalServerResultsTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/RemoveUnsatisfiedConditionalServerResultsTransaction.java
new file mode 100644
index 000000000..e03b36aaa
--- /dev/null
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/RemoveUnsatisfiedConditionalServerResultsTransaction.java
@@ -0,0 +1,150 @@
+/*
+ * 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 GNU 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
+ * GNU 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 .
+ */
+package com.djrapitops.plan.extension.implementation.storage.transactions.results;
+
+import com.djrapitops.plan.db.DBType;
+import com.djrapitops.plan.db.access.ExecStatement;
+import com.djrapitops.plan.db.access.Executable;
+import com.djrapitops.plan.db.access.transactions.Transaction;
+import com.djrapitops.plan.db.sql.tables.ExtensionProviderTable;
+import com.djrapitops.plan.db.sql.tables.ExtensionServerTableValueTable;
+import com.djrapitops.plan.db.sql.tables.ExtensionServerValueTable;
+import com.djrapitops.plan.db.sql.tables.ExtensionTableProviderTable;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+import static com.djrapitops.plan.db.sql.parsing.Sql.*;
+
+/**
+ * Transaction to remove older results that violate an updated condition value.
+ *
+ * How it works:
+ * - Select all fulfilled conditions for all servers (conditionName when true and not_conditionName when false)
+ * - Left join with server value & provider tables when plugin_ids match, and when condition matches a condition in the
+ * query above. (plugin_ids can be linked to servers)
+ * - Filter the join query for values where the condition did not match any provided condition in the join (Is null)
+ * - Delete all server values with IDs that are returned by the left join query after filtering
+ *
+ * @author Rsl1122
+ */
+public class RemoveUnsatisfiedConditionalServerResultsTransaction extends Transaction {
+
+ private final String providerTable;
+ private final String serverValueTable;
+ private final String serverTableValueTable;
+ private final String tableTable;
+
+ public RemoveUnsatisfiedConditionalServerResultsTransaction() {
+ providerTable = ExtensionProviderTable.TABLE_NAME;
+ serverValueTable = ExtensionServerValueTable.TABLE_NAME;
+ tableTable = ExtensionTableProviderTable.TABLE_NAME;
+ serverTableValueTable = ExtensionServerTableValueTable.TABLE_NAME;
+ }
+
+ @Override
+ protected void performOperations() {
+ String selectSatisfiedConditions = getSatisfiedConditionsSQL();
+ execute(deleteUnsatisfiedValues(selectSatisfiedConditions));
+ execute(deleteUnsatisfiedTableValues(selectSatisfiedConditions));
+ }
+
+ private Executable deleteUnsatisfiedValues(String selectSatisfiedConditions) {
+ // Query contents:
+ // id | provider_id | q1.provider_id | condition | q1.provided_condition
+ // -- | ----------- | -------------- | --------- | ---------------------
+ // 1 | ... | ... | A | A Satisfied condition
+ // 2 | ... | ... | not_B | not_B Satisfied condition
+ // 3 | ... | ... | NULL | NULL Satisfied condition
+ // 4 | ... | ... | B | NULL Unsatisfied condition, filtered to these in WHERE clause.
+ // 5 | ... | ... | not_C | NULL Unsatisfied condition
+ String selectUnsatisfiedValueIDs = SELECT + serverValueTable + '.' + ExtensionServerValueTable.ID +
+ FROM + providerTable +
+ INNER_JOIN + serverValueTable + " on " + providerTable + '.' + ExtensionProviderTable.ID + "=" + ExtensionServerValueTable.PROVIDER_ID +
+ LEFT_JOIN + selectSatisfiedConditions + // Left join to preserve values that don't have their condition fulfilled
+ " on (" +
+ ExtensionProviderTable.CONDITION + "=q1." + ExtensionProviderTable.PROVIDED_CONDITION +
+ AND + providerTable + '.' + ExtensionProviderTable.PLUGIN_ID + "=q1." + ExtensionProviderTable.PLUGIN_ID +
+ ')' +
+ WHERE + "q1." + ExtensionProviderTable.PROVIDED_CONDITION + IS_NULL + // Conditions that were not in the satisfied condition query
+ AND + ExtensionProviderTable.CONDITION + IS_NOT_NULL; // Ignore values that don't need condition
+
+ // Nested query here is required because MySQL limits update statements with nested queries:
+ // The nested query creates a temporary table that bypasses the same table query-update limit.
+ // Note: MySQL versions 5.6.7+ might optimize this nested query away leading to an exception.
+ String sql = "DELETE FROM " + serverValueTable +
+ WHERE + ExtensionServerValueTable.ID + " IN (" + SELECT + ExtensionServerValueTable.ID + FROM + '(' + selectUnsatisfiedValueIDs + ") as ids)";
+
+ return new ExecStatement(sql) {
+ @Override
+ public void prepare(PreparedStatement statement) throws SQLException {
+ statement.setBoolean(1, true); // Select provided conditions with 'true' value
+ statement.setBoolean(2, false); // Select negated conditions with 'false' value
+ }
+ };
+ }
+
+ private String getSatisfiedConditionsSQL() {
+ String reversedCondition = dbType == DBType.SQLITE ? "'not_' || " + ExtensionProviderTable.PROVIDED_CONDITION : "CONCAT('not_'," + ExtensionProviderTable.PROVIDED_CONDITION + ')';
+
+ String selectSatisfiedPositiveConditions = SELECT +
+ ExtensionProviderTable.PROVIDED_CONDITION + ',' +
+ ExtensionProviderTable.PLUGIN_ID +
+ FROM + providerTable +
+ INNER_JOIN + serverValueTable + " on " + providerTable + '.' + ExtensionProviderTable.ID + "=" + ExtensionServerValueTable.PROVIDER_ID +
+ WHERE + ExtensionServerValueTable.BOOLEAN_VALUE + "=?" +
+ AND + ExtensionProviderTable.PROVIDED_CONDITION + IS_NOT_NULL;
+ String selectSatisfiedNegativeConditions = SELECT +
+ reversedCondition + " as " + ExtensionProviderTable.PROVIDED_CONDITION + ',' +
+ ExtensionProviderTable.PLUGIN_ID +
+ FROM + providerTable +
+ INNER_JOIN + serverValueTable + " on " + providerTable + '.' + ExtensionProviderTable.ID + "=" + ExtensionServerValueTable.PROVIDER_ID +
+ WHERE + ExtensionServerValueTable.BOOLEAN_VALUE + "=?" +
+ AND + ExtensionProviderTable.PROVIDED_CONDITION + IS_NOT_NULL;
+
+ // Query contents: Set of provided_conditions
+ return '(' + selectSatisfiedPositiveConditions + " UNION " + selectSatisfiedNegativeConditions + ") q1";
+ }
+
+ private Executable deleteUnsatisfiedTableValues(String selectSatisfiedConditions) {
+ String selectUnsatisfiedValueIDs = SELECT + ExtensionTableProviderTable.ID +
+ FROM + tableTable +
+ LEFT_JOIN + selectSatisfiedConditions + // Left join to preserve values that don't have their condition fulfilled
+ " on (" +
+ tableTable + '.' + ExtensionTableProviderTable.CONDITION +
+ "=q1." + ExtensionProviderTable.PROVIDED_CONDITION +
+ AND + tableTable + '.' + ExtensionTableProviderTable.PLUGIN_ID +
+ "=q1." + ExtensionProviderTable.PLUGIN_ID +
+ ')' +
+ WHERE + "q1." + ExtensionProviderTable.PROVIDED_CONDITION + IS_NULL + // Conditions that were not in the satisfied condition query
+ AND + ExtensionProviderTable.CONDITION + IS_NOT_NULL; // Ignore values that don't need condition
+
+ // Nested query here is required because MySQL limits update statements with nested queries:
+ // The nested query creates a temporary table that bypasses the same table query-update limit.
+ // Note: MySQL versions 5.6.7+ might optimize this nested query away leading to an exception.
+ String deleteValuesSQL = "DELETE FROM " + serverTableValueTable +
+ WHERE + ExtensionServerTableValueTable.TABLE_ID + " IN (" + SELECT + ExtensionTableProviderTable.ID + FROM + '(' + selectUnsatisfiedValueIDs + ") as ids)";
+
+ return new ExecStatement(deleteValuesSQL) {
+ @Override
+ public void prepare(PreparedStatement statement) throws SQLException {
+ statement.setBoolean(1, true); // Select provided conditions with 'true' value
+ statement.setBoolean(2, false); // Select negated conditions with 'false' value
+ }
+ };
+ }
+}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StorePlayerTableResultTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StorePlayerTableResultTransaction.java
new file mode 100644
index 000000000..73f0b6a53
--- /dev/null
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StorePlayerTableResultTransaction.java
@@ -0,0 +1,155 @@
+/*
+ * 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 GNU 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
+ * GNU 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 .
+ */
+package com.djrapitops.plan.extension.implementation.storage.transactions.results;
+
+import com.djrapitops.plan.api.exceptions.database.DBOpException;
+import com.djrapitops.plan.db.access.*;
+import com.djrapitops.plan.db.access.transactions.Transaction;
+import com.djrapitops.plan.db.sql.tables.ExtensionPluginTable;
+import com.djrapitops.plan.db.sql.tables.ExtensionTableProviderTable;
+import com.djrapitops.plan.extension.table.Table;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.util.UUID;
+
+import static com.djrapitops.plan.db.sql.parsing.Sql.*;
+import static com.djrapitops.plan.db.sql.tables.ExtensionPlayerTableValueTable.*;
+
+/**
+ * Transaction to store method result of a {@link com.djrapitops.plan.extension.implementation.providers.TableDataProvider}.
+ *
+ * @author Rsl1122
+ */
+public class StorePlayerTableResultTransaction extends Transaction {
+
+ private final String pluginName;
+ private final UUID serverUUID;
+ private final String providerName;
+ private final UUID playerUUID;
+
+ private final Table table;
+
+ public StorePlayerTableResultTransaction(String pluginName, UUID serverUUID, String providerName, UUID playerUUID, Table table) {
+ this.pluginName = pluginName;
+ this.serverUUID = serverUUID;
+ this.providerName = providerName;
+ this.playerUUID = playerUUID;
+ this.table = table;
+ }
+
+ @Override
+ protected void performOperations() {
+ execute(storeValue());
+ }
+
+ private Executable storeValue() {
+ return connection -> {
+ int maxColumnSize = table.getMaxColumnSize();
+ if (maxColumnSize == 0) {
+ return false;
+ }
+
+ Integer tableID = query(tableID());
+ deleteOldValues(tableID).execute(connection);
+ insertNewValues(tableID).execute(connection);
+ return false;
+ };
+ }
+
+ private Executable deleteOldValues(int tableID) {
+ String sql = "DELETE FROM " + TABLE_NAME +
+ WHERE + TABLE_ID + "=?" +
+ AND + USER_UUID + "=?";
+
+ return new ExecStatement(sql) {
+ @Override
+ public void prepare(PreparedStatement statement) throws SQLException {
+ statement.setInt(1, tableID);
+ statement.setString(2, playerUUID.toString());
+ }
+ };
+ }
+
+ private Executable insertNewValues(int tableID) {
+ String sql = "INSERT INTO " + TABLE_NAME + '(' +
+ TABLE_ID + ',' +
+ USER_UUID + ',' +
+ VALUE_1 + ',' +
+ VALUE_2 + ',' +
+ VALUE_3 + ',' +
+ VALUE_4 +
+ ") VALUES (?,?,?,?,?,?)";
+
+ return new ExecBatchStatement(sql) {
+ @Override
+ public void prepare(PreparedStatement statement) throws SQLException {
+ int maxColumnSize = Math.min(table.getMaxColumnSize(), 4); // Limit to maximum 4 columns, or how many column names there are.
+
+ for (Object[] row : table.getRows()) {
+ statement.setInt(1, tableID);
+ statement.setString(2, playerUUID.toString());
+ for (int i = 0; i < maxColumnSize; i++) {
+ Object value = row[i];
+ setStringOrNull(statement, 3 + i, value != null ? value.toString() : null);
+ }
+ // Rest are set null if not 4 columns wide.
+ for (int i = maxColumnSize; i < 4; i++) {
+ statement.setNull(3 + i, Types.VARCHAR);
+ }
+
+ statement.addBatch();
+ }
+ }
+ };
+ }
+
+ private void setStringOrNull(PreparedStatement statement, int index, String value) throws SQLException {
+ if (value != null) {
+ statement.setString(index, value);
+ } else {
+ statement.setNull(index, Types.VARCHAR);
+ }
+ }
+
+ private Query tableID() {
+ String sql = SELECT + ExtensionTableProviderTable.ID +
+ FROM + ExtensionTableProviderTable.TABLE_NAME +
+ WHERE + ExtensionTableProviderTable.PROVIDER_NAME + "=?" +
+ AND + ExtensionTableProviderTable.PLUGIN_ID + "=" + ExtensionPluginTable.STATEMENT_SELECT_PLUGIN_ID +
+ " LIMIT 1";
+ return new QueryStatement(sql) {
+ @Override
+ public void prepare(PreparedStatement statement) throws SQLException {
+ ExtensionTableProviderTable.set3PluginValuesToStatement(statement, 1, providerName, pluginName, serverUUID);
+ }
+
+ @Override
+ public Integer processResults(ResultSet set) throws SQLException {
+ if (set.next()) {
+ int id = set.getInt(ExtensionTableProviderTable.ID);
+ if (!set.wasNull()) {
+ return id;
+ }
+ }
+ throw new DBOpException("Table Provider was not saved before storing results. Please report this issue. Extension method: " + pluginName + "#" + providerName);
+ }
+ };
+ }
+}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StoreServerTableResultTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StoreServerTableResultTransaction.java
new file mode 100644
index 000000000..ff5211097
--- /dev/null
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StoreServerTableResultTransaction.java
@@ -0,0 +1,154 @@
+/*
+ * 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 GNU 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
+ * GNU 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 .
+ */
+package com.djrapitops.plan.extension.implementation.storage.transactions.results;
+
+import com.djrapitops.plan.api.exceptions.database.DBOpException;
+import com.djrapitops.plan.db.access.*;
+import com.djrapitops.plan.db.access.transactions.Transaction;
+import com.djrapitops.plan.db.sql.tables.ExtensionPluginTable;
+import com.djrapitops.plan.db.sql.tables.ExtensionTableProviderTable;
+import com.djrapitops.plan.extension.table.Table;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.util.UUID;
+
+import static com.djrapitops.plan.db.sql.parsing.Sql.*;
+import static com.djrapitops.plan.db.sql.tables.ExtensionServerTableValueTable.*;
+
+/**
+ * Transaction to store method result of a {@link com.djrapitops.plan.extension.implementation.providers.TableDataProvider}.
+ *
+ * @author Rsl1122
+ */
+public class StoreServerTableResultTransaction extends Transaction {
+
+ private final String pluginName;
+ private final UUID serverUUID;
+ private final String providerName;
+
+ private final Table table;
+
+ public StoreServerTableResultTransaction(String pluginName, UUID serverUUID, String providerName, Table table) {
+ this.pluginName = pluginName;
+ this.serverUUID = serverUUID;
+ this.providerName = providerName;
+ this.table = table;
+ }
+
+ @Override
+ protected void performOperations() {
+ execute(storeValue());
+ }
+
+ private Executable storeValue() {
+ return connection -> {
+ int maxColumnSize = table.getMaxColumnSize();
+ if (maxColumnSize == 0) {
+ return false;
+ }
+
+ Integer tableID = query(tableID());
+ deleteOldValues(tableID).execute(connection);
+ insertNewValues(tableID).execute(connection);
+ return false;
+ };
+ }
+
+ private Executable deleteOldValues(int tableID) {
+ String sql = "DELETE FROM " + TABLE_NAME +
+ WHERE + TABLE_ID + "=?" +
+ AND + SERVER_UUID + "=?";
+
+ return new ExecStatement(sql) {
+ @Override
+ public void prepare(PreparedStatement statement) throws SQLException {
+ statement.setInt(1, tableID);
+ statement.setString(2, serverUUID.toString());
+ }
+ };
+ }
+
+ private Executable insertNewValues(int tableID) {
+ String sql = "INSERT INTO " + TABLE_NAME + '(' +
+ TABLE_ID + ',' +
+ SERVER_UUID + ',' +
+ VALUE_1 + ',' +
+ VALUE_2 + ',' +
+ VALUE_3 + ',' +
+ VALUE_4 + ',' +
+ VALUE_5 +
+ ") VALUES (?,?,?,?,?,?, ?)";
+
+ return new ExecBatchStatement(sql) {
+ @Override
+ public void prepare(PreparedStatement statement) throws SQLException {
+ int maxColumnSize = Math.min(table.getMaxColumnSize(), 5); // Limit to maximum 5 columns, or how many column names there are.
+
+ for (Object[] row : table.getRows()) {
+ statement.setInt(1, tableID);
+ statement.setString(2, serverUUID.toString());
+ for (int i = 0; i < maxColumnSize; i++) {
+ Object value = row[i];
+ setStringOrNull(statement, 3 + i, value != null ? value.toString() : null);
+ }
+ // Rest are set null if not 5 columns wide.
+ for (int i = maxColumnSize; i < 5; i++) {
+ statement.setNull(3 + i, Types.VARCHAR);
+ }
+
+ statement.addBatch();
+ }
+ }
+ };
+ }
+
+ private void setStringOrNull(PreparedStatement statement, int index, String value) throws SQLException {
+ if (value != null) {
+ statement.setString(index, value);
+ } else {
+ statement.setNull(index, Types.VARCHAR);
+ }
+ }
+
+ private Query tableID() {
+ String sql = SELECT + ExtensionTableProviderTable.ID +
+ FROM + ExtensionTableProviderTable.TABLE_NAME +
+ WHERE + ExtensionTableProviderTable.PROVIDER_NAME + "=?" +
+ AND + ExtensionTableProviderTable.PLUGIN_ID + "=" + ExtensionPluginTable.STATEMENT_SELECT_PLUGIN_ID +
+ " LIMIT 1";
+ return new QueryStatement(sql) {
+ @Override
+ public void prepare(PreparedStatement statement) throws SQLException {
+ ExtensionTableProviderTable.set3PluginValuesToStatement(statement, 1, providerName, pluginName, serverUUID);
+ }
+
+ @Override
+ public Integer processResults(ResultSet set) throws SQLException {
+ if (set.next()) {
+ int id = set.getInt(ExtensionTableProviderTable.ID);
+ if (!set.wasNull()) {
+ return id;
+ }
+ }
+ throw new DBOpException("Table Provider was not saved before storing results. Please report this issue. Extension method: " + pluginName + "#" + providerName);
+ }
+ };
+ }
+}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/table/TableAccessor.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/table/TableAccessor.java
new file mode 100644
index 000000000..19fef428f
--- /dev/null
+++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/table/TableAccessor.java
@@ -0,0 +1,81 @@
+/*
+ * 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 GNU 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
+ * GNU 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 .
+ */
+package com.djrapitops.plan.extension.table;
+
+import com.djrapitops.plan.extension.ElementOrder;
+import com.djrapitops.plan.extension.icon.Color;
+import com.djrapitops.plan.extension.icon.Icon;
+
+/**
+ * Utility for accessing implementation variables inside Table.Factory object.
+ *
+ * @author Rsl1122
+ */
+public class TableAccessor {
+
+ private TableAccessor() {
+ /* Static method class */
+ }
+
+ public static Color getColor(Table.Factory factory) {
+ return factory.color;
+ }
+
+ public static void setColor(Table.Factory factory, Color color) {
+ factory.color = color;
+ }
+
+ public static String getTableName(Table.Factory factory) {
+ return factory.tableName;
+ }
+
+ public static void setTableName(Table.Factory factory, String tableName) {
+ factory.tableName = tableName;
+ }
+
+ public static String getTabName(Table.Factory factory) {
+ return factory.tabName;
+ }
+
+ public static void setTabName(Table.Factory factory, String tabName) {
+ factory.tabName = tabName;
+ }
+
+ public static int getTabPriority(Table.Factory factory) {
+ return factory.tabPriority;
+ }
+
+ public static void setTabPriority(Table.Factory factory, int tabPriority) {
+ factory.tabPriority = tabPriority;
+ }
+
+ public static ElementOrder[] getTabOrder(Table.Factory factory) {
+ return factory.tabOrder;
+ }
+
+ public static void setTabOrder(Table.Factory factory, ElementOrder[] tabOrder) {
+ factory.tabOrder = tabOrder;
+ }
+
+ public static Icon getTabIcon(Table.Factory factory) {
+ return factory.tabIcon;
+ }
+
+ public static void setTabIcon(Table.Factory factory, Icon tabIcon) {
+ factory.tabIcon = tabIcon;
+ }
+}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/PlanSystem.java b/Plan/common/src/main/java/com/djrapitops/plan/system/PlanSystem.java
index b4933f345..a1610fc04 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/system/PlanSystem.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/system/PlanSystem.java
@@ -126,11 +126,11 @@ public class PlanSystem implements SubSystem {
localeSystem,
versionCheckSystem,
databaseSystem,
- importSystem,
- exportSystem,
webServerSystem,
processing,
serverInfo,
+ importSystem,
+ exportSystem,
infoSystem,
cacheSystem,
listenerSystem,
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/export/ExportSystem.java b/Plan/common/src/main/java/com/djrapitops/plan/system/export/ExportSystem.java
index 0d875e0ae..47aa56180 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/system/export/ExportSystem.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/system/export/ExportSystem.java
@@ -18,10 +18,10 @@ package com.djrapitops.plan.system.export;
import com.djrapitops.plan.system.SubSystem;
import com.djrapitops.plan.system.info.connection.ConnectionSystem;
+import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.processing.Processing;
import com.djrapitops.plan.system.settings.config.PlanConfig;
import com.djrapitops.plan.system.settings.paths.ExportSettings;
-import com.djrapitops.plugin.api.Check;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -35,6 +35,7 @@ import javax.inject.Singleton;
public class ExportSystem implements SubSystem {
private final PlanConfig config;
+ private final ServerInfo serverInfo;
private final Processing processing;
private final HtmlExport htmlExport;
private final ConnectionSystem connectionSystem;
@@ -42,11 +43,13 @@ public class ExportSystem implements SubSystem {
@Inject
public ExportSystem(
PlanConfig config,
+ ServerInfo serverInfo,
Processing processing,
HtmlExport htmlExport,
ConnectionSystem connectionSystem
) {
this.config = config;
+ this.serverInfo = serverInfo;
this.processing = processing;
this.htmlExport = htmlExport;
this.connectionSystem = connectionSystem;
@@ -54,7 +57,7 @@ public class ExportSystem implements SubSystem {
@Override
public void enable() {
- if (Check.isBukkitAvailable() && connectionSystem.isServerAvailable()) {
+ if (serverInfo.getServer().isNotProxy() && connectionSystem.isServerAvailable()) {
return;
}
if (config.isTrue(ExportSettings.JS_AND_CSS)) {
@@ -69,7 +72,10 @@ public class ExportSystem implements SubSystem {
processing.submitNonCritical(htmlExport::exportAvailablePlayers);
}
if (config.isTrue(ExportSettings.SERVER_PAGE)) {
- processing.submitNonCritical(htmlExport::exportAvailableServerPages);
+ processing.submitNonCritical(() -> {
+ htmlExport.cacheNetworkPage();
+ htmlExport.exportAvailableServerPages();
+ });
}
}
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/export/HtmlExport.java b/Plan/common/src/main/java/com/djrapitops/plan/system/export/HtmlExport.java
index 1f6483a08..844955ecb 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/system/export/HtmlExport.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/system/export/HtmlExport.java
@@ -16,7 +16,6 @@
*/
package com.djrapitops.plan.system.export;
-import com.djrapitops.plan.PlanPlugin;
import com.djrapitops.plan.api.exceptions.ParseException;
import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.data.container.BaseUser;
@@ -32,9 +31,13 @@ import com.djrapitops.plan.system.settings.config.PlanConfig;
import com.djrapitops.plan.system.settings.paths.ExportSettings;
import com.djrapitops.plan.system.settings.theme.Theme;
import com.djrapitops.plan.system.settings.theme.ThemeVal;
+import com.djrapitops.plan.system.webserver.cache.PageId;
+import com.djrapitops.plan.system.webserver.cache.ResponseCache;
+import com.djrapitops.plan.system.webserver.pages.json.JSONFactory;
+import com.djrapitops.plan.system.webserver.response.pages.NetworkPageResponse;
import com.djrapitops.plan.utilities.html.pages.InspectPage;
+import com.djrapitops.plan.utilities.html.pages.NetworkPage;
import com.djrapitops.plan.utilities.html.pages.PageFactory;
-import com.djrapitops.plugin.api.Check;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.logging.error.ErrorHandler;
import com.djrapitops.plugin.utilities.Verify;
@@ -55,7 +58,6 @@ import java.util.*;
@Singleton
public class HtmlExport extends SpecificExport {
- private final PlanPlugin plugin;
private final PlanConfig config;
private final Theme theme;
private final PlanFiles files;
@@ -66,18 +68,17 @@ public class HtmlExport extends SpecificExport {
@Inject
public HtmlExport(
- PlanPlugin plugin,
PlanFiles files,
PlanConfig config,
Theme theme,
DBSystem dbSystem,
PageFactory pageFactory,
+ JSONFactory jsonFactory,
ServerInfo serverInfo,
ConnectionSystem connectionSystem,
ErrorHandler errorHandler
) {
- super(files, serverInfo);
- this.plugin = plugin;
+ super(files, jsonFactory, serverInfo);
this.config = config;
this.theme = theme;
this.files = files;
@@ -93,7 +94,7 @@ public class HtmlExport extends SpecificExport {
}
public void exportServer(UUID serverUUID) {
- if (Check.isBukkitAvailable() && connectionSystem.isServerAvailable()) {
+ if (serverInfo.getServer().isNotProxy() && connectionSystem.isServerAvailable()) {
return;
}
dbSystem.getDatabase().query(ServerQueries.fetchServerMatchingIdentifier(serverUUID))
@@ -122,7 +123,7 @@ public class HtmlExport extends SpecificExport {
}
public void exportCachedPlayerPage(UUID playerUUID) {
- if (Check.isBukkitAvailable() && connectionSystem.isServerAvailable()) {
+ if (serverInfo.getServer().isNotProxy() && connectionSystem.isServerAvailable()) {
return;
}
@@ -166,6 +167,35 @@ public class HtmlExport extends SpecificExport {
}
}
+ public void cacheNetworkPage() {
+ if (serverInfo.getServer().isNotProxy()) {
+ return;
+ }
+
+ NetworkPage networkPage = pageFactory.networkPage();
+ ResponseCache.cacheResponse(PageId.SERVER.of(serverInfo.getServerUUID()), () -> {
+ try {
+ return new NetworkPageResponse(networkPage);
+ } catch (ParseException e) {
+ errorHandler.log(L.WARN, this.getClass(), e);
+ return null;
+ }
+ });
+ }
+
+ public void exportNetworkPage() {
+ if (serverInfo.getServer().isNotProxy()) {
+ return;
+ }
+
+ cacheNetworkPage();
+ try {
+ exportAvailableServerPage(serverInfo.getServerUUID(), serverInfo.getServer().getName());
+ } catch (IOException e) {
+ errorHandler.log(L.WARN, this.getClass(), e);
+ }
+ }
+
public void exportAvailableServerPages() {
try {
Map serverNames = dbSystem.getDatabase().query(ServerQueries.fetchServerNames());
@@ -231,15 +261,11 @@ public class HtmlExport extends SpecificExport {
public void exportPlugins() {
String[] resources = new String[]{
- "web/plugins/bootstrap/css/bootstrap.css",
"web/plugins/node-waves/waves.css",
"web/plugins/node-waves/waves.js",
"web/plugins/animate-css/animate.css",
"web/plugins/jquery-slimscroll/jquery.slimscroll.js",
"web/plugins/jquery/jquery.min.js",
- "web/plugins/bootstrap/js/bootstrap.js",
- "web/plugins/jquery-datatable/skin/bootstrap/js/dataTables.bootstrap.js",
- "web/plugins/jquery-datatable/jquery.dataTables.js",
"web/plugins/fullcalendar/fullcalendar.min.js",
"web/plugins/fullcalendar/fullcalendar.min.css",
"web/plugins/momentjs/moment.js",
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/export/JSONExport.java b/Plan/common/src/main/java/com/djrapitops/plan/system/export/JSONExport.java
index 805512eac..04ddf947c 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/system/export/JSONExport.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/system/export/JSONExport.java
@@ -24,6 +24,7 @@ import com.djrapitops.plan.system.info.server.Server;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.settings.config.PlanConfig;
import com.djrapitops.plan.system.settings.paths.ExportSettings;
+import com.djrapitops.plan.system.webserver.pages.json.JSONFactory;
import com.djrapitops.plan.system.webserver.response.ResponseFactory;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.logging.error.ErrorHandler;
@@ -55,10 +56,11 @@ public class JSONExport extends SpecificExport {
PlanConfig config,
DBSystem dbSystem,
ServerInfo serverInfo,
+ JSONFactory jsonFactory,
ResponseFactory responseFactory,
ErrorHandler errorHandler
) {
- super(files, serverInfo);
+ super(files, jsonFactory, serverInfo);
this.config = config;
this.dbSystem = dbSystem;
this.responseFactory = responseFactory;
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/export/SpecificExport.java b/Plan/common/src/main/java/com/djrapitops/plan/system/export/SpecificExport.java
index 226541042..15f450a77 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/system/export/SpecificExport.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/system/export/SpecificExport.java
@@ -20,8 +20,8 @@ import com.djrapitops.plan.system.file.PlanFiles;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.webserver.cache.PageId;
import com.djrapitops.plan.system.webserver.cache.ResponseCache;
+import com.djrapitops.plan.system.webserver.pages.json.JSONFactory;
import com.djrapitops.plan.system.webserver.response.Response;
-import com.djrapitops.plugin.api.Check;
import java.io.File;
import java.io.IOException;
@@ -30,6 +30,7 @@ import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.UUID;
@@ -41,17 +42,16 @@ import java.util.UUID;
public abstract class SpecificExport {
private final PlanFiles files;
- private final ServerInfo serverInfo;
+ private final JSONFactory jsonFactory; // Hacky, TODO export needs a rework
+ protected final ServerInfo serverInfo;
- private final boolean usingProxy;
-
- protected SpecificExport(
+ SpecificExport(
PlanFiles files,
- ServerInfo serverInfo
+ JSONFactory jsonFactory, ServerInfo serverInfo
) {
this.files = files;
+ this.jsonFactory = jsonFactory;
this.serverInfo = serverInfo;
- usingProxy = Check.isBungeeAvailable() || Check.isVelocityAvailable();
}
protected File getFolder() {
@@ -78,19 +78,19 @@ public abstract class SpecificExport {
Files.write(to.toPath(), lines, StandardCharsets.UTF_8);
}
- protected File getServerFolder() {
+ File getServerFolder() {
File server = new File(getFolder(), "server");
server.mkdirs();
return server;
}
- protected File getPlayerFolder() {
+ File getPlayerFolder() {
File player = new File(getFolder(), "player");
player.mkdirs();
return player;
}
- protected void exportPlayerPage(String playerName, String html) throws IOException {
+ void exportPlayerPage(String playerName, String html) throws IOException {
List lines = Arrays.asList(html.replace("../", "../../").split("\n"));
File htmlLocation = new File(getPlayerFolder(), URLEncoder.encode(playerName, "UTF-8").replace(".", "%2E"));
@@ -100,7 +100,7 @@ public abstract class SpecificExport {
export(exportFile, lines);
}
- protected void exportAvailablePlayerPage(UUID playerUUID, String name) throws IOException {
+ void exportAvailablePlayerPage(UUID playerUUID, String name) throws IOException {
Response response = ResponseCache.loadResponse(PageId.PLAYER.of(playerUUID));
if (response == null) {
return;
@@ -110,7 +110,7 @@ public abstract class SpecificExport {
exportPlayerPage(name, html);
}
- protected void exportAvailableServerPage(UUID serverUUID, String serverName) throws IOException {
+ void exportAvailableServerPage(UUID serverUUID, String serverName) throws IOException {
Response response = ResponseCache.loadResponse(PageId.SERVER.of(serverUUID));
if (response == null) {
@@ -121,19 +121,23 @@ public abstract class SpecificExport {
.replace("href=\"plugins/", "href=\"../plugins/")
.replace("href=\"css/", "href=\"../css/")
.replace("src=\"plugins/", "src=\"../plugins/")
- .replace("src=\"js/", "src=\"../js/");
+ .replace("src=\"js/", "src=\"../js/")
+ .replace("../json/players?serverName=" + serverName, "./players_table.json");
File htmlLocation;
- if (usingProxy) {
+ if (serverInfo.getServer().isProxy()) {
if (serverUUID.equals(serverInfo.getServerUUID())) {
htmlLocation = new File(getFolder(), "network");
} else {
htmlLocation = new File(getServerFolder(), URLEncoder.encode(serverName, "UTF-8").replace(".", "%2E"));
html = html.replace("../", "../../");
+ exportPlayersTableJSON(htmlLocation, serverUUID);
}
} else {
htmlLocation = getServerFolder();
+ exportPlayersTableJSON(htmlLocation, serverUUID);
}
+
htmlLocation.mkdirs();
File exportFile = new File(htmlLocation, "index.html");
@@ -141,4 +145,9 @@ public abstract class SpecificExport {
export(exportFile, lines);
}
+
+ private void exportPlayersTableJSON(File htmlLocation, UUID serverUUID) throws IOException {
+ File exportFile = new File(htmlLocation, "players_table.json");
+ export(exportFile, Collections.singletonList(jsonFactory.serverPlayersTableJSON(serverUUID)));
+ }
}
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/InfoSystem.java b/Plan/common/src/main/java/com/djrapitops/plan/system/info/InfoSystem.java
index 66b84b71b..a0a2fa502 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/InfoSystem.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/system/info/InfoSystem.java
@@ -29,7 +29,6 @@ import com.djrapitops.plan.system.info.request.InfoRequestFactory;
import com.djrapitops.plan.system.info.server.Server;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.webserver.WebServer;
-import com.djrapitops.plugin.api.Check;
import com.djrapitops.plugin.logging.console.PluginLogger;
import dagger.Lazy;
@@ -159,11 +158,8 @@ public abstract class InfoSystem implements SubSystem {
* @throws WebException If fails.
*/
public void requestSetUp(String addressToRequestServer) throws WebException {
- if (Check.isBungeeAvailable()) {
- throw new BadRequestException("Method not available on Bungee.");
- }
- if (Check.isVelocityAvailable()) {
- throw new BadRequestException("Method not available on Velocity.");
+ if (serverInfo.getServer().isProxy()) {
+ throw new BadRequestException("Method not available on a Proxy server.");
}
Server bungee = new Server(-1, null, "Bungee", addressToRequestServer, -1);
String addressOfThisServer = webServer.get().getAccessAddress();
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/connection/InfoRequestPageHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/system/info/connection/InfoRequestPageHandler.java
index 65efe125d..7e5da8f27 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/connection/InfoRequestPageHandler.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/system/info/connection/InfoRequestPageHandler.java
@@ -21,6 +21,7 @@ import com.djrapitops.plan.api.exceptions.connection.WebException;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.info.request.InfoRequest;
import com.djrapitops.plan.system.webserver.Request;
+import com.djrapitops.plan.system.webserver.RequestTarget;
import com.djrapitops.plan.system.webserver.pages.PageHandler;
import com.djrapitops.plan.system.webserver.response.Response;
import com.djrapitops.plan.system.webserver.response.ResponseFactory;
@@ -30,7 +31,6 @@ import com.djrapitops.plugin.utilities.Verify;
import javax.inject.Inject;
import javax.inject.Singleton;
-import java.util.List;
/**
* PageHandler for /info/requestclassname pages.
@@ -62,7 +62,7 @@ public class InfoRequestPageHandler implements PageHandler {
}
@Override
- public Response getResponse(Request request, List target) throws WebException {
+ public Response getResponse(Request request, RequestTarget target) throws WebException {
int responseCode = 200;
try {
@@ -85,7 +85,7 @@ public class InfoRequestPageHandler implements PageHandler {
responseCode = getResponseCodeFor(e);
throw e;
} finally {
- connectionSystem.getConnectionLog().logConnectionFrom(request.getRemoteAddress(), request.getTarget(), responseCode);
+ connectionSystem.getConnectionLog().logConnectionFrom(request.getRemoteAddress(), request.getTargetString(), responseCode);
}
}
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/CacheAnalysisPageRequest.java b/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/CacheAnalysisPageRequest.java
index 2489ddef1..8ce23d583 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/CacheAnalysisPageRequest.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/CacheAnalysisPageRequest.java
@@ -105,7 +105,10 @@ public class CacheAnalysisPageRequest extends InfoRequestWithVariables implement
}
if (config.get(ExportSettings.SERVER_PAGE)) {
- processing.submitNonCritical(() -> htmlExport.exportServer(serverUUID));
+ processing.submitNonCritical(() -> {
+ htmlExport.exportNetworkPage();
+ htmlExport.exportServer(serverUUID);
+ });
}
if (config.get(ExportSettings.SERVER_JSON)) {
processing.submitNonCritical(() -> jsonExport.exportServerJSON(serverUUID));
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/CheckConnectionRequest.java b/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/CheckConnectionRequest.java
index 79a22dfd2..2aa60a8b6 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/CheckConnectionRequest.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/CheckConnectionRequest.java
@@ -22,9 +22,9 @@ import com.djrapitops.plan.api.exceptions.connection.GatewayException;
import com.djrapitops.plan.api.exceptions.connection.WebException;
import com.djrapitops.plan.system.info.connection.ConnectionSystem;
import com.djrapitops.plan.system.info.server.Server;
+import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.webserver.response.DefaultResponses;
import com.djrapitops.plan.system.webserver.response.Response;
-import com.djrapitops.plugin.api.Check;
import com.djrapitops.plugin.utilities.Verify;
import java.util.Map;
@@ -37,9 +37,11 @@ import java.util.UUID;
*/
public class CheckConnectionRequest extends InfoRequestWithVariables {
+ private final ServerInfo serverInfo;
private final ConnectionSystem connectionSystem;
- CheckConnectionRequest(String webServerAddress, ConnectionSystem connectionSystem) {
+ CheckConnectionRequest(String webServerAddress, ServerInfo serverInfo, ConnectionSystem connectionSystem) {
+ this.serverInfo = serverInfo;
this.connectionSystem = connectionSystem;
Verify.nullCheck(webServerAddress, () -> new IllegalArgumentException("webServerAddress can not be null."));
@@ -47,7 +49,8 @@ public class CheckConnectionRequest extends InfoRequestWithVariables {
variables.put("continue", "yes");
}
- CheckConnectionRequest(ConnectionSystem connectionSystem) {
+ CheckConnectionRequest(ServerInfo serverInfo, ConnectionSystem connectionSystem) {
+ this.serverInfo = serverInfo;
this.connectionSystem = connectionSystem;
}
@@ -60,7 +63,7 @@ public class CheckConnectionRequest extends InfoRequestWithVariables {
public Response handleRequest(Map variables) throws WebException {
// Available variables: sender, address
- if (Check.isBungeeAvailable() || Check.isVelocityAvailable()) {
+ if (serverInfo.getServer().isProxy()) {
attemptConnection(variables);
}
@@ -82,7 +85,7 @@ public class CheckConnectionRequest extends InfoRequestWithVariables {
Server bukkit = new Server(-1, serverUUID, "", address, -1);
try {
- connectionSystem.sendInfoRequest(new CheckConnectionRequest(connectionSystem), bukkit);
+ connectionSystem.sendInfoRequest(new CheckConnectionRequest(serverInfo, connectionSystem), bukkit);
} catch (ConnectionFailException e) {
throw new GatewayException(e.getMessage());
}
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/InfoRequestFactory.java b/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/InfoRequestFactory.java
index 654d5cf6d..ac6e9f3cd 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/InfoRequestFactory.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/InfoRequestFactory.java
@@ -123,15 +123,15 @@ public class InfoRequestFactory {
}
public SaveDBSettingsRequest saveDBSettingsRequest() {
- return new SaveDBSettingsRequest(plugin.get(), config.get(), logger.get(), runnableFactory.get());
+ return new SaveDBSettingsRequest(plugin.get(), config.get(), serverInfo.get(), logger.get(), runnableFactory.get());
}
public SetupRequest sendDBSettingsRequest(String addressOfThisServer) {
- return new SendDBSettingsRequest(addressOfThisServer, this, connectionSystem.get());
+ return new SendDBSettingsRequest(addressOfThisServer, serverInfo.get(), this, connectionSystem.get());
}
public CheckConnectionRequest checkConnectionRequest(String webAddress) {
- return new CheckConnectionRequest(webAddress, connectionSystem.get());
+ return new CheckConnectionRequest(webAddress, serverInfo.get(), connectionSystem.get());
}
@Singleton
@@ -169,7 +169,7 @@ public class InfoRequestFactory {
}
CheckConnectionRequest checkConnectionRequest() {
- return new CheckConnectionRequest(factory.connectionSystem.get());
+ return new CheckConnectionRequest(factory.serverInfo.get(), factory.connectionSystem.get());
}
GenerateRequest generateAnalysisPageRequest() {
@@ -204,6 +204,7 @@ public class InfoRequestFactory {
return new SaveDBSettingsRequest(
factory.plugin.get(),
factory.config.get(),
+ factory.serverInfo.get(),
factory.logger.get(),
factory.runnableFactory.get()
);
@@ -211,6 +212,7 @@ public class InfoRequestFactory {
SetupRequest sendDBSettingsRequest() {
return new SendDBSettingsRequest(
+ factory.serverInfo.get(),
factory,
factory.connectionSystem.get()
);
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/SaveDBSettingsRequest.java b/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/SaveDBSettingsRequest.java
index 73c28af60..9c3f6716d 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/SaveDBSettingsRequest.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/SaveDBSettingsRequest.java
@@ -20,13 +20,13 @@ import com.djrapitops.plan.PlanPlugin;
import com.djrapitops.plan.api.exceptions.connection.BadRequestException;
import com.djrapitops.plan.api.exceptions.connection.InternalErrorException;
import com.djrapitops.plan.api.exceptions.connection.WebException;
+import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.settings.config.PlanConfig;
import com.djrapitops.plan.system.settings.paths.DatabaseSettings;
import com.djrapitops.plan.system.settings.paths.PluginSettings;
import com.djrapitops.plan.system.webserver.response.DefaultResponses;
import com.djrapitops.plan.system.webserver.response.Response;
import com.djrapitops.plan.system.webserver.response.errors.BadRequestResponse;
-import com.djrapitops.plugin.api.Check;
import com.djrapitops.plugin.api.TimeAmount;
import com.djrapitops.plugin.logging.console.PluginLogger;
import com.djrapitops.plugin.task.AbsRunnable;
@@ -46,17 +46,19 @@ public class SaveDBSettingsRequest extends InfoRequestWithVariables implements S
private final PlanPlugin plugin;
private final PlanConfig config;
+ private final ServerInfo serverInfo;
private final PluginLogger logger;
private final RunnableFactory runnableFactory;
SaveDBSettingsRequest(
PlanPlugin plugin,
PlanConfig config,
- PluginLogger logger,
+ ServerInfo serverInfo, PluginLogger logger,
RunnableFactory runnableFactory
) {
this.plugin = plugin;
this.config = config;
+ this.serverInfo = serverInfo;
this.logger = logger;
this.runnableFactory = runnableFactory;
@@ -75,11 +77,8 @@ public class SaveDBSettingsRequest extends InfoRequestWithVariables implements S
@Override
public Response handleRequest(Map variables) throws WebException {
- if (Check.isBungeeAvailable()) {
- return new BadRequestResponse("Not supposed to be called on a Bungee server");
- }
- if (Check.isVelocityAvailable()) {
- return new BadRequestResponse("Not supposed to be called on a Velocity server");
+ if (serverInfo.getServer().isProxy()) {
+ return new BadRequestResponse("Not supposed to be called on a proxy server");
}
if (config.isFalse(PluginSettings.BUNGEE_COPY_CONFIG)) {
return new BadRequestResponse("Bungee config settings overridden on this server.");
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/SendDBSettingsRequest.java b/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/SendDBSettingsRequest.java
index 168398621..50cd628d2 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/SendDBSettingsRequest.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/SendDBSettingsRequest.java
@@ -22,10 +22,10 @@ import com.djrapitops.plan.api.exceptions.connection.GatewayException;
import com.djrapitops.plan.api.exceptions.connection.WebException;
import com.djrapitops.plan.system.info.connection.ConnectionSystem;
import com.djrapitops.plan.system.info.server.Server;
+import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.webserver.response.DefaultResponses;
import com.djrapitops.plan.system.webserver.response.Response;
import com.djrapitops.plan.system.webserver.response.errors.BadRequestResponse;
-import com.djrapitops.plugin.api.Check;
import com.djrapitops.plugin.utilities.Verify;
import java.net.SocketException;
@@ -39,20 +39,23 @@ import java.util.UUID;
*/
public class SendDBSettingsRequest extends InfoRequestWithVariables implements SetupRequest {
+ private final ServerInfo serverInfo;
private final InfoRequestFactory infoRequestFactory;
private final ConnectionSystem connectionSystem;
SendDBSettingsRequest(
- InfoRequestFactory infoRequestFactory, ConnectionSystem connectionSystem
+ ServerInfo serverInfo, InfoRequestFactory infoRequestFactory, ConnectionSystem connectionSystem
) {
+ this.serverInfo = serverInfo;
this.infoRequestFactory = infoRequestFactory;
this.connectionSystem = connectionSystem;
}
SendDBSettingsRequest(
String webServerAddress,
- InfoRequestFactory infoRequestFactory, ConnectionSystem connectionSystem
+ ServerInfo serverInfo, InfoRequestFactory infoRequestFactory, ConnectionSystem connectionSystem
) {
+ this.serverInfo = serverInfo;
this.infoRequestFactory = infoRequestFactory;
this.connectionSystem = connectionSystem;
@@ -68,11 +71,8 @@ public class SendDBSettingsRequest extends InfoRequestWithVariables implements S
@Override
public Response handleRequest(Map variables) throws WebException {
// Available variables: sender, address
- if (Check.isBukkitAvailable()) {
- return new BadRequestResponse("Not supposed to be called on a Bukkit server");
- }
- if (Check.isSpongeAvailable()) {
- return new BadRequestResponse("Not supposed to be called on a Sponge server");
+ if (serverInfo.getServer().isNotProxy()) {
+ return new BadRequestResponse("Not supposed to be called on a non proxy server");
}
String address = variables.get("address");
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/server/properties/ServerProperties.java b/Plan/common/src/main/java/com/djrapitops/plan/system/info/server/properties/ServerProperties.java
index df5a4c518..fc9855c00 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/server/properties/ServerProperties.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/system/info/server/properties/ServerProperties.java
@@ -27,7 +27,6 @@ import java.util.function.Supplier;
*/
public abstract class ServerProperties {
- private final String id;
private final String name;
private final int port;
private final String version;
@@ -38,7 +37,6 @@ public abstract class ServerProperties {
private final IntSupplier onlinePlayers;
protected ServerProperties(
- String id,
String name,
int port,
String version,
@@ -47,7 +45,6 @@ public abstract class ServerProperties {
int maxPlayers,
IntSupplier onlinePlayers
) {
- this.id = id;
this.name = name;
this.port = port;
this.version = version;
@@ -86,10 +83,6 @@ public abstract class ServerProperties {
return maxPlayers;
}
- public String getServerId() {
- return id;
- }
-
public int getOnlinePlayers() {
return onlinePlayers.getAsInt();
}
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/LangCode.java b/Plan/common/src/main/java/com/djrapitops/plan/system/locale/LangCode.java
index 2a05ffa2b..fd766370d 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/LangCode.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/system/locale/LangCode.java
@@ -23,23 +23,21 @@ package com.djrapitops.plan.system.locale;
*/
public enum LangCode {
- CUSTOM("Custom"),
- EN("English"),
- FI("Finnish"),
- DE("Deutch"),
- FR("French"),
- GA("Irish (Gaeilge)"),
- CS("Czech"),
- PT("Portugese"),
- NL("Dutch"),
- NO("Norwegian"),
- PL("Polish"),
- IT("Italian");
+ CUSTOM("Custom", ""),
+ EN("English", "Rsl1122"),
+ CN("Simplified Chinese", "f0rb1d (佛壁灯) & qsefthuopq"),
+ DE("Deutch", "Eyremba & fuzzlemann & Morsmorse"),
+ FI("Finnish", "Rsl1122"),
+ FR("French", "CyanTech & Aurelien"),
+ JA("Japanese", "yukieji"),
+ IT("Italian", "- (Outdated, using English)");
private final String name;
+ private final String authors;
- LangCode(String name) {
+ LangCode(String name, String authors) {
this.name = name;
+ this.authors = authors;
}
public static LangCode fromString(String code) {
@@ -54,6 +52,10 @@ public enum LangCode {
return name;
}
+ public String getAuthors() {
+ return authors;
+ }
+
public String getFileName() {
return "locale_" + name() + ".txt";
}
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/Locale.java b/Plan/common/src/main/java/com/djrapitops/plan/system/locale/Locale.java
index bed27d08e..0a3c95763 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/Locale.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/system/locale/Locale.java
@@ -26,7 +26,7 @@ import java.io.Serializable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
-import java.util.Map;
+import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -41,12 +41,26 @@ public class Locale extends HashMap {
return forLangCode(LangCode.fromString(code), files);
}
+ private LangCode langCode;
+
+ public Locale() {
+ this(LangCode.EN);
+ }
+
+ public Locale(LangCode langCode) {
+ this.langCode = langCode;
+ }
+
public static Locale forLangCode(LangCode code, PlanFiles files) throws IOException {
- return new LocaleFileReader(files.getResourceFromJar("locale/" + code.getFileName())).load();
+ return new LocaleFileReader(files.getResourceFromJar("locale/" + code.getFileName())).load(code);
}
public static Locale fromFile(File file) throws IOException {
- return new LocaleFileReader(new FileResource(file.getName(), file)).load();
+ return new LocaleFileReader(new FileResource(file.getName(), file)).load(LangCode.CUSTOM);
+ }
+
+ public LangCode getLangCode() {
+ return langCode;
}
@Override
@@ -75,8 +89,9 @@ public class Locale extends HashMap {
return get(key).toArray(values);
}
- public void loadFromAnotherLocale(Map locale) {
+ public void loadFromAnotherLocale(Locale locale) {
putAll(locale);
+ this.langCode = locale.langCode;
}
public String replaceMatchingLanguage(String from) {
@@ -111,4 +126,18 @@ public class Locale extends HashMap {
}
return replaced;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof Locale)) return false;
+ if (!super.equals(o)) return false;
+ Locale locale = (Locale) o;
+ return langCode == locale.langCode;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), langCode);
+ }
}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/LocaleFileReader.java b/Plan/common/src/main/java/com/djrapitops/plan/system/locale/LocaleFileReader.java
index 82a0e394f..1738c9e96 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/LocaleFileReader.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/system/locale/LocaleFileReader.java
@@ -36,8 +36,8 @@ public class LocaleFileReader {
lines = resource.asLines();
}
- public Locale load() {
- Locale locale = new Locale();
+ public Locale load(LangCode code) {
+ Locale locale = new Locale(code);
Map identifiers = LocaleSystem.getIdentifiers();
lines.forEach(line -> {
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/LocaleSystem.java b/Plan/common/src/main/java/com/djrapitops/plan/system/locale/LocaleSystem.java
index bf8c161b7..02a734c71 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/LocaleSystem.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/system/locale/LocaleSystem.java
@@ -106,6 +106,9 @@ public class LocaleSystem implements SubSystem {
loaded = loadSettingLocale();
}
loaded.ifPresent(locale::loadFromAnotherLocale);
+
+ LangCode langCode = locale.getLangCode();
+ logger.info("Locale: '" + langCode.getName() + "' by " + langCode.getAuthors());
}
private void writeNewDefaultLocale(File localeFile) {
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/ErrorPageLang.java b/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/ErrorPageLang.java
index a5451a6cc..eb2270d64 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/ErrorPageLang.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/ErrorPageLang.java
@@ -27,7 +27,7 @@ public enum ErrorPageLang implements Lang {
NOT_PLAYED_404("Player has not played on this server."),
UNKNOWN_PAGE_404("Make sure you're accessing a link given by a command, Examples:
/player/PlayerName /server/ServerName
"),
UNAUTHORIZED_401("Unauthorized"),
- AUTHENTICATION_FAIlED_401("Authentication Failed."),
+ AUTHENTICATION_FAILED_401("Authentication Failed."),
AUTH_FAIL_TIPS_401("- Ensure you have registered a user with /plan register - Check that the username and password are correct - Username and password are case-sensitive If you have forgotten your password, ask a staff member to delete your old user and re-register."),
FORBIDDEN_403("Forbidden"),
ACCESS_DENIED_403("Access Denied"),
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/PluginLang.java b/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/PluginLang.java
index 8bc27a971..2c71e1beb 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/PluginLang.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/PluginLang.java
@@ -35,7 +35,7 @@ public enum PluginLang implements Lang {
ENABLE_FAIL_DB("Enable FAIL - Database", "${0}-Database Connection failed: ${1}"),
ENABLE_FAIL_WRONG_DB("Enable FAIL - Wrong Database Type", "${0} is not a supported Database"),
ENABLE_FAIL_DB_PATCH("Enable FAIL - Database Patch", "Database Patching failed, plugin has to be disabled. Please report this issue"),
- ENABLE_FAIL_NO_WEB_SERVER_BUNGEE("Enable FAIL - WebServer (Bungee)", "WebServer did not initialize!"),
+ ENABLE_FAIL_NO_WEB_SERVER_PROXY("Enable FAIL - WebServer (Bungee)", "WebServer did not initialize!"),
ENABLE_FAIL_GEODB_WRITE("Enable FAIL - GeoDB Write", "Something went wrong saving the downloaded GeoLite2 Geolocation database"),
WEB_SERVER_FAIL_PORT_BIND("WebServer FAIL - Port Bind", "WebServer was not initialized successfully. Is the port (${0}) in use?"),
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/processing/Processing.java b/Plan/common/src/main/java/com/djrapitops/plan/system/processing/Processing.java
index 85e5dea18..ca08796ee 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/system/processing/Processing.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/system/processing/Processing.java
@@ -53,7 +53,7 @@ public class Processing implements SubSystem {
criticalExecutor = createExecutor(2, "Plan Critical-pool-%d");
}
- private ExecutorService createExecutor(int i, String s) {
+ protected ExecutorService createExecutor(int i, String s) {
return Executors.newFixedThreadPool(i, new ThreadFactoryBuilder().setNameFormat(s).build());
}
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/changes/ConfigChange.java b/Plan/common/src/main/java/com/djrapitops/plan/system/settings/changes/ConfigChange.java
index 3923d8fe0..32048a335 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/changes/ConfigChange.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/system/settings/changes/ConfigChange.java
@@ -41,7 +41,7 @@ public interface ConfigChange {
}
@Override
- public void apply(Config config) {
+ public synchronized void apply(Config config) {
if (!config.moveChild(oldPath, newPath)) {
throw new IllegalStateException("Failed to move config node from '" + oldPath + "' to '" + newPath + "'");
}
@@ -63,7 +63,7 @@ public interface ConfigChange {
}
@Override
- public void apply(Config config) {
+ public synchronized void apply(Config config) {
config.getNode(oldPath).ifPresent(oldNode -> config.addNode(newPath).copyAll(oldNode));
}
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/config/ConfigValueParser.java b/Plan/common/src/main/java/com/djrapitops/plan/system/settings/config/ConfigValueParser.java
index 2b84e08b6..ead1610bb 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/config/ConfigValueParser.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/system/settings/config/ConfigValueParser.java
@@ -76,6 +76,10 @@ public interface ConfigValueParser {
*/
String decompose(T ofValue);
+ static IllegalArgumentException nullInvalidException() {
+ return new IllegalArgumentException("Null value is not valid for saving");
+ }
+
class StringParser implements ConfigValueParser {
@Override
public String compose(String fromValue) {
@@ -89,7 +93,7 @@ public interface ConfigValueParser {
@Override
public String decompose(String value) {
- Verify.nullCheck(value, () -> new IllegalArgumentException("Null value is not valid for saving"));
+ Verify.nullCheck(value, ConfigValueParser::nullInvalidException);
boolean surroundedByQuotes = value.startsWith("'") || value.endsWith("'");
boolean surroundedByDoubleQuotes = value.startsWith("\"") || value.endsWith("\"");
@@ -116,7 +120,7 @@ public interface ConfigValueParser {
@Override
public String decompose(Integer ofValue) {
- Verify.nullCheck(ofValue, () -> new IllegalArgumentException("Null value is not valid for saving"));
+ Verify.nullCheck(ofValue, ConfigValueParser::nullInvalidException);
return Integer.toString(ofValue);
}
}
@@ -133,7 +137,7 @@ public interface ConfigValueParser {
@Override
public String decompose(Long ofValue) {
- Verify.nullCheck(ofValue, () -> new IllegalArgumentException("Null value is not valid for saving"));
+ Verify.nullCheck(ofValue, ConfigValueParser::nullInvalidException);
return Long.toString(ofValue);
}
}
@@ -146,7 +150,7 @@ public interface ConfigValueParser {
@Override
public String decompose(Boolean ofValue) {
- Verify.nullCheck(ofValue, () -> new IllegalArgumentException("Null value is not valid for saving"));
+ Verify.nullCheck(ofValue, ConfigValueParser::nullInvalidException);
return Boolean.toString(ofValue);
}
}
@@ -174,7 +178,7 @@ public interface ConfigValueParser {
@Override
public String decompose(List ofValue) {
- Verify.nullCheck(ofValue, () -> new IllegalArgumentException("Null value is not valid for saving"));
+ Verify.nullCheck(ofValue, ConfigValueParser::nullInvalidException);
StringBuilder decomposedString = new StringBuilder();
for (String value : ofValue) {
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/config/PlanConfig.java b/Plan/common/src/main/java/com/djrapitops/plan/system/settings/config/PlanConfig.java
index 606b8a368..7e8879f2a 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/config/PlanConfig.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/system/settings/config/PlanConfig.java
@@ -27,6 +27,7 @@ import javax.inject.Named;
import javax.inject.Singleton;
import java.io.File;
import java.util.List;
+import java.util.Objects;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
@@ -134,4 +135,9 @@ public class PlanConfig extends Config {
if (o == null) return false;
return super.equals(o);
}
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode());
+ }
}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/DataGatheringSettings.java b/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/DataGatheringSettings.java
index a6ced136e..e5f851c61 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/DataGatheringSettings.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/DataGatheringSettings.java
@@ -27,6 +27,7 @@ import com.djrapitops.plan.system.settings.paths.key.Setting;
public class DataGatheringSettings {
public static final Setting GEOLOCATIONS = new BooleanSetting("Data_gathering.Geolocations");
+ public static final Setting PING = new BooleanSetting("Data_gathering.Ping");
public static final Setting LOG_UNKNOWN_COMMANDS = new BooleanSetting("Data_gathering.Commands.Log_unknown");
public static final Setting COMBINE_COMMAND_ALIASES = new BooleanSetting("Data_gathering.Commands.Log_aliases_as_main_command");
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/TimeSettings.java b/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/TimeSettings.java
index 316600799..0eb48a5fc 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/TimeSettings.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/TimeSettings.java
@@ -35,7 +35,9 @@ public class TimeSettings {
public static final Setting AFK_THRESHOLD = new TimeSetting("Time.Thresholds.AFK_threshold");
public static final Setting ACTIVE_LOGIN_THRESHOLD = new IntegerSetting("Time.Thresholds.Activity_index.Login_threshold", Setting::timeValidator);
public static final Setting ACTIVE_PLAY_THRESHOLD = new TimeSetting("Time.Thresholds.Activity_index.Playtime_threshold");
- public static final Setting KEEP_INACTIVE_PLAYERS = new TimeSetting("Time.Thresholds.Remove_inactive_player_data_after");
+ public static final Setting DELETE_INACTIVE_PLAYERS_AFTER = new TimeSetting("Time.Thresholds.Remove_inactive_player_data_after");
+ public static final Setting DELETE_TPS_DATA_AFTER = new TimeSetting("Time.Thresholds.Remove_time_series_data_after");
+ public static final Setting DELETE_PING_DATA_AFTER = new TimeSetting("Time.Thresholds.Remove_ping_data_after");
public static final Setting ANALYSIS_REFRESH_PERIOD = new TimeSetting("Time.Periodic_tasks.Analysis_refresh_every");
public static final Setting EXTENSION_DATA_REFRESH_PERIOD = new TimeSetting("Time.Periodic_tasks.Extension_data_refresh_every");
public static final Setting CLEAN_CACHE_PERIOD = new TimeSetting("Time.Periodic_tasks.Clean_caches_every");
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/Request.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/Request.java
index 004be0f64..e0d7a2bd2 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/Request.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/Request.java
@@ -21,6 +21,7 @@ import com.djrapitops.plan.system.webserver.auth.Authentication;
import com.sun.net.httpserver.HttpExchange;
import java.io.InputStream;
+import java.net.URI;
import java.util.Optional;
/**
@@ -32,7 +33,9 @@ import java.util.Optional;
*/
public class Request {
private final String requestMethod;
- private final String target;
+
+ private URI requestURI;
+
private final HttpExchange exchange;
private final String remoteAddress;
private final Locale locale;
@@ -40,7 +43,7 @@ public class Request {
public Request(HttpExchange exchange, Locale locale) {
this.requestMethod = exchange.getRequestMethod();
- this.target = exchange.getRequestURI().getPath();
+ requestURI = exchange.getRequestURI();
remoteAddress = exchange.getRemoteAddress().getAddress().getHostAddress();
@@ -61,8 +64,12 @@ public class Request {
return requestMethod;
}
- public String getTarget() {
- return target;
+ public String getTargetString() {
+ return requestURI.getPath() + '?' + requestURI.getQuery();
+ }
+
+ public RequestTarget getTarget() {
+ return new RequestTarget(requestURI);
}
public InputStream getRequestBody() {
@@ -71,7 +78,7 @@ public class Request {
@Override
public String toString() {
- return "Request:" + requestMethod + " " + target;
+ return "Request:" + requestMethod + " " + requestURI.getPath();
}
public String getRemoteAddress() {
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/RequestHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/RequestHandler.java
index 3a69977b6..8d7e99cbc 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/RequestHandler.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/RequestHandler.java
@@ -89,6 +89,7 @@ public class RequestHandler implements HttpHandler {
public void handle(HttpExchange exchange) {
Headers requestHeaders = exchange.getRequestHeaders();
Headers responseHeaders = exchange.getResponseHeaders();
+
Request request = new Request(exchange, locale);
request.setAuth(getAuthorization(requestHeaders));
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/RequestTarget.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/RequestTarget.java
new file mode 100644
index 000000000..e4a5f359a
--- /dev/null
+++ b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/RequestTarget.java
@@ -0,0 +1,88 @@
+/*
+ * 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 GNU 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
+ * GNU 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 .
+ */
+package com.djrapitops.plan.system.webserver;
+
+import java.net.URI;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * Represents URI of a requested resource.
+ *
+ * @author Rsl1122
+ */
+public class RequestTarget {
+
+ private final String resourceString;
+ private final List resource;
+ private final Map parameters;
+
+ public RequestTarget(URI targetURI) {
+ resourceString = targetURI.getPath();
+ resource = Arrays.stream(resourceString.split("/")).filter(part -> !part.isEmpty()).collect(Collectors.toList());
+
+ parameters = new TreeMap<>();
+ parseParameters(targetURI.getQuery());
+ }
+
+ private void parseParameters(String parameterString) {
+ if (parameterString == null || parameterString.isEmpty()) {
+ return;
+ }
+
+ String[] keysAndValues = parameterString.split("&");
+ for (String kv : keysAndValues) {
+ if (kv.isEmpty()) {
+ continue;
+ }
+ String[] keyAndValue = kv.split("=", 2);
+ if (keyAndValue.length >= 2) {
+ parameters.put(keyAndValue[0], keyAndValue[1]);
+ }
+ }
+ }
+
+ public boolean isEmpty() {
+ return resource.isEmpty();
+ }
+
+ public int size() {
+ return resource.size();
+ }
+
+ public String get(int index) {
+ return resource.get(index);
+ }
+
+ public void removeFirst() {
+ if (!isEmpty()) {
+ resource.remove(0);
+ }
+ }
+
+ public boolean endsWith(String suffix) {
+ return resourceString.endsWith(suffix);
+ }
+
+ public Optional getParameter(String key) {
+ return Optional.ofNullable(parameters.get(key));
+ }
+
+ public String getResourceString() {
+ return resourceString;
+ }
+}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/ResponseHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/ResponseHandler.java
index 9814a9f6e..f36d6ba45 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/ResponseHandler.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/ResponseHandler.java
@@ -23,6 +23,7 @@ import com.djrapitops.plan.system.webserver.auth.Authentication;
import com.djrapitops.plan.system.webserver.cache.PageId;
import com.djrapitops.plan.system.webserver.cache.ResponseCache;
import com.djrapitops.plan.system.webserver.pages.*;
+import com.djrapitops.plan.system.webserver.pages.json.RootJSONHandler;
import com.djrapitops.plan.system.webserver.response.Response;
import com.djrapitops.plan.system.webserver.response.ResponseFactory;
import com.djrapitops.plan.system.webserver.response.errors.BadRequestResponse;
@@ -32,9 +33,6 @@ import dagger.Lazy;
import javax.inject.Inject;
import javax.inject.Singleton;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
import java.util.Optional;
/**
@@ -50,6 +48,7 @@ public class ResponseHandler extends TreePageHandler {
private final PlayerPageHandler playerPageHandler;
private final ServerPageHandler serverPageHandler;
private final InfoRequestPageHandler infoRequestPageHandler;
+ private final RootJSONHandler rootJSONHandler;
private final ErrorHandler errorHandler;
private Lazy webServer;
@@ -64,6 +63,7 @@ public class ResponseHandler extends TreePageHandler {
PlayerPageHandler playerPageHandler,
ServerPageHandler serverPageHandler,
InfoRequestPageHandler infoRequestPageHandler,
+ RootJSONHandler rootJSONHandler,
ErrorHandler errorHandler
) {
@@ -74,6 +74,7 @@ public class ResponseHandler extends TreePageHandler {
this.playerPageHandler = playerPageHandler;
this.serverPageHandler = serverPageHandler;
this.infoRequestPageHandler = infoRequestPageHandler;
+ this.rootJSONHandler = rootJSONHandler;
this.errorHandler = errorHandler;
}
@@ -92,16 +93,12 @@ public class ResponseHandler extends TreePageHandler {
}
registerPage("info", infoRequestPageHandler);
+ registerPage("json", rootJSONHandler);
}
public Response getResponse(Request request) {
- String targetString = request.getTarget();
- List target = new ArrayList<>(Arrays.asList(targetString.split("/")));
- if (!target.isEmpty()) {
- target.remove(0);
- }
try {
- return getResponse(request, targetString, target);
+ return tryToGetResponse(request);
} catch (NoServersException | NotFoundException e) {
return responseFactory.notFound404(e.getMessage());
} catch (WebUserAuthException e) {
@@ -109,45 +106,44 @@ public class ResponseHandler extends TreePageHandler {
} catch (ForbiddenException e) {
return responseFactory.forbidden403(e.getMessage());
} catch (BadRequestException e) {
- return new BadRequestResponse(e.getMessage());
+ return new BadRequestResponse(e.getMessage() + " (when requesting '" + request.getTargetString() + "')");
} catch (UnauthorizedServerException e) {
return responseFactory.unauthorizedServer(e.getMessage());
} catch (GatewayException e) {
return responseFactory.gatewayError504(e.getMessage());
} catch (InternalErrorException e) {
if (e.getCause() != null) {
- return responseFactory.internalErrorResponse(e.getCause(), request.getTarget());
+ return responseFactory.internalErrorResponse(e.getCause(), request.getTargetString());
} else {
- return responseFactory.internalErrorResponse(e, request.getTarget());
+ return responseFactory.internalErrorResponse(e, request.getTargetString());
}
} catch (Exception e) {
errorHandler.log(L.ERROR, this.getClass(), e);
- return responseFactory.internalErrorResponse(e, request.getTarget());
+ return responseFactory.internalErrorResponse(e, request.getTargetString());
}
}
- private Response getResponse(Request request, String targetString, List target) throws WebException {
- Optional authentication = Optional.empty();
+ private Response tryToGetResponse(Request request) throws WebException {
+ Optional authentication = request.getAuth();
+ RequestTarget target = request.getTarget();
+ String resource = target.getResourceString();
- if (targetString.endsWith(".css")) {
- return ResponseCache.loadResponse(PageId.CSS.of(targetString), () -> responseFactory.cssResponse(targetString));
+ if (target.endsWith(".css")) {
+ return ResponseCache.loadResponse(PageId.CSS.of(resource), () -> responseFactory.cssResponse(resource));
}
- if (targetString.endsWith(".js")) {
- return ResponseCache.loadResponse(PageId.JS.of(targetString), () -> responseFactory.javaScriptResponse(targetString));
+ if (target.endsWith(".js")) {
+ return ResponseCache.loadResponse(PageId.JS.of(resource), () -> responseFactory.javaScriptResponse(resource));
}
- if (targetString.endsWith("favicon.ico")) {
+ if (target.endsWith("favicon.ico")) {
return ResponseCache.loadResponse(PageId.FAVICON.id(), responseFactory::faviconResponse);
}
boolean isNotInfoRequest = target.isEmpty() || !target.get(0).equals("info");
boolean isAuthRequired = webServer.get().isAuthRequired() && isNotInfoRequest;
- if (isAuthRequired) {
- authentication = request.getAuth();
- if (!authentication.isPresent()) {
- if (webServer.get().isUsingHTTPS()) {
- return responseFactory.basicAuth();
- } else {
- return responseFactory.forbidden403();
- }
+ if (isAuthRequired && !authentication.isPresent()) {
+ if (webServer.get().isUsingHTTPS()) {
+ return responseFactory.basicAuth();
+ } else {
+ return responseFactory.forbidden403();
}
}
PageHandler pageHandler = getPageHandler(target);
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/WebServer.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/WebServer.java
index 649fbe9ce..7a66c17a5 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/WebServer.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/WebServer.java
@@ -19,6 +19,7 @@ package com.djrapitops.plan.system.webserver;
import com.djrapitops.plan.api.exceptions.EnableException;
import com.djrapitops.plan.system.SubSystem;
import com.djrapitops.plan.system.file.PlanFiles;
+import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.info.server.properties.ServerProperties;
import com.djrapitops.plan.system.locale.Locale;
import com.djrapitops.plan.system.locale.lang.PluginLang;
@@ -76,7 +77,7 @@ public class WebServer implements SubSystem {
Locale locale,
PlanFiles files,
PlanConfig config,
- ServerProperties serverProperties,
+ ServerInfo serverInfo,
PluginLogger logger,
ErrorHandler errorHandler,
RequestHandler requestHandler
@@ -84,7 +85,7 @@ public class WebServer implements SubSystem {
this.locale = locale;
this.files = files;
this.config = config;
- this.serverProperties = serverProperties;
+ this.serverProperties = serverInfo.getServerProperties();
this.requestHandler = requestHandler;
@@ -100,7 +101,7 @@ public class WebServer implements SubSystem {
if (!isEnabled()) {
if (Check.isBungeeAvailable() || Check.isVelocityAvailable()) {
- throw new EnableException(locale.getString(PluginLang.ENABLE_FAIL_NO_WEB_SERVER_BUNGEE));
+ throw new EnableException(locale.getString(PluginLang.ENABLE_FAIL_NO_WEB_SERVER_PROXY));
}
if (config.isTrue(WebserverSettings.DISABLED)) {
logger.warn(locale.getString(PluginLang.ENABLE_NOTIFY_WEB_SERVER_DISABLED));
@@ -116,8 +117,8 @@ public class WebServer implements SubSystem {
* Starts up the WebServer in a new Thread Pool.
*/
private void initServer() {
- if (!(Check.isBungeeAvailable() || Check.isVelocityAvailable()) && config.isTrue(WebserverSettings.DISABLED)) {
- // Bukkit WebServer has been disabled.
+ if ((Check.isBukkitAvailable() || Check.isSpongeAvailable()) && config.isTrue(WebserverSettings.DISABLED)) {
+ // Bukkit/Sponge WebServer has been disabled.
return;
}
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/cache/ResponseCache.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/cache/ResponseCache.java
index b1b36cb85..a48c51d68 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/cache/ResponseCache.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/cache/ResponseCache.java
@@ -82,7 +82,9 @@ public class ResponseCache {
*/
public static void cacheResponse(String identifier, Supplier loader) {
Response response = loader.get();
- cache.put(identifier, response);
+ if (response != null) {
+ cache.put(identifier, response);
+ }
}
/**
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/DebugPageHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/DebugPageHandler.java
index 456da2367..181a38a20 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/DebugPageHandler.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/DebugPageHandler.java
@@ -19,13 +19,13 @@ package com.djrapitops.plan.system.webserver.pages;
import com.djrapitops.plan.api.exceptions.WebUserAuthException;
import com.djrapitops.plan.data.WebUser;
import com.djrapitops.plan.system.webserver.Request;
+import com.djrapitops.plan.system.webserver.RequestTarget;
import com.djrapitops.plan.system.webserver.auth.Authentication;
import com.djrapitops.plan.system.webserver.response.Response;
import com.djrapitops.plan.system.webserver.response.ResponseFactory;
import javax.inject.Inject;
import javax.inject.Singleton;
-import java.util.List;
/**
* PageHandler for /debug page.
@@ -43,12 +43,12 @@ public class DebugPageHandler implements PageHandler {
}
@Override
- public Response getResponse(Request request, List target) {
+ public Response getResponse(Request request, RequestTarget target) {
return responseFactory.debugPageResponse();
}
@Override
- public boolean isAuthorized(Authentication auth, List target) throws WebUserAuthException {
+ public boolean isAuthorized(Authentication auth, RequestTarget target) throws WebUserAuthException {
WebUser webUser = auth.getWebUser();
return webUser.getPermLevel() <= 0;
}
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/PageHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/PageHandler.java
index 4c74b4feb..f76cf881a 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/PageHandler.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/PageHandler.java
@@ -19,11 +19,10 @@ package com.djrapitops.plan.system.webserver.pages;
import com.djrapitops.plan.api.exceptions.WebUserAuthException;
import com.djrapitops.plan.api.exceptions.connection.WebException;
import com.djrapitops.plan.system.webserver.Request;
+import com.djrapitops.plan.system.webserver.RequestTarget;
import com.djrapitops.plan.system.webserver.auth.Authentication;
import com.djrapitops.plan.system.webserver.response.Response;
-import java.util.List;
-
/**
* PageHandlers are used for easier Response management and authorization checking.
*
@@ -38,9 +37,9 @@ public interface PageHandler {
* @param target Rest of the target coordinates after this page has been solved.
* @return Response appropriate to the PageHandler.
*/
- Response getResponse(Request request, List target) throws WebException;
+ Response getResponse(Request request, RequestTarget target) throws WebException;
- default boolean isAuthorized(Authentication auth, List target) throws WebUserAuthException {
+ default boolean isAuthorized(Authentication auth, RequestTarget target) throws WebUserAuthException {
return true;
}
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/PlayerPageHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/PlayerPageHandler.java
index e5f10e2f7..0d449836e 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/PlayerPageHandler.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/PlayerPageHandler.java
@@ -26,6 +26,7 @@ import com.djrapitops.plan.db.access.queries.PlayerFetchQueries;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.info.InfoSystem;
import com.djrapitops.plan.system.webserver.Request;
+import com.djrapitops.plan.system.webserver.RequestTarget;
import com.djrapitops.plan.system.webserver.auth.Authentication;
import com.djrapitops.plan.system.webserver.cache.PageId;
import com.djrapitops.plan.system.webserver.cache.ResponseCache;
@@ -36,7 +37,6 @@ import com.djrapitops.plan.utilities.uuid.UUIDUtility;
import javax.inject.Inject;
import javax.inject.Singleton;
-import java.util.List;
import java.util.UUID;
/**
@@ -66,7 +66,7 @@ public class PlayerPageHandler implements PageHandler {
}
@Override
- public Response getResponse(Request request, List target) throws WebException {
+ public Response getResponse(Request request, RequestTarget target) throws WebException {
if (target.isEmpty()) {
return responseFactory.pageNotFound404();
}
@@ -105,11 +105,11 @@ public class PlayerPageHandler implements PageHandler {
infoSystem.generateAndCachePlayerPage(uuid);
response = ResponseCache.loadResponse(PageId.PLAYER.of(uuid));
}
- return response != null ? response : responseFactory.serverNotFound404();
+ return response != null ? response : responseFactory.playerNotFound404();
}
@Override
- public boolean isAuthorized(Authentication auth, List target) throws WebUserAuthException {
+ public boolean isAuthorized(Authentication auth, RequestTarget target) throws WebUserAuthException {
WebUser webUser = auth.getWebUser();
return webUser.getPermLevel() <= 1 || webUser.getName().equalsIgnoreCase(target.get(target.size() - 1));
}
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/PlayersPageHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/PlayersPageHandler.java
index 64c7acc4c..d1be11287 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/PlayersPageHandler.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/PlayersPageHandler.java
@@ -22,6 +22,7 @@ import com.djrapitops.plan.api.exceptions.connection.WebException;
import com.djrapitops.plan.db.Database;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.webserver.Request;
+import com.djrapitops.plan.system.webserver.RequestTarget;
import com.djrapitops.plan.system.webserver.auth.Authentication;
import com.djrapitops.plan.system.webserver.cache.PageId;
import com.djrapitops.plan.system.webserver.cache.ResponseCache;
@@ -30,7 +31,6 @@ import com.djrapitops.plan.system.webserver.response.ResponseFactory;
import javax.inject.Inject;
import javax.inject.Singleton;
-import java.util.List;
/**
* PageHandler for /players page.
@@ -53,7 +53,7 @@ public class PlayersPageHandler implements PageHandler {
}
@Override
- public Response getResponse(Request request, List target) throws WebException {
+ public Response getResponse(Request request, RequestTarget target) throws WebException {
Database.State dbState = dbSystem.getDatabase().getState();
if (dbState != Database.State.OPEN) {
throw new ForbiddenException("Database is " + dbState.name() + " - Please try again later. You can check database status with /plan info");
@@ -62,7 +62,7 @@ public class PlayersPageHandler implements PageHandler {
}
@Override
- public boolean isAuthorized(Authentication auth, List target) throws WebUserAuthException {
+ public boolean isAuthorized(Authentication auth, RequestTarget target) throws WebUserAuthException {
return auth.getWebUser().getPermLevel() <= 1;
}
}
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/RootPageHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/RootPageHandler.java
index 0391d2c72..47052fa27 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/RootPageHandler.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/RootPageHandler.java
@@ -19,12 +19,12 @@ package com.djrapitops.plan.system.webserver.pages;
import com.djrapitops.plan.api.exceptions.connection.WebException;
import com.djrapitops.plan.data.WebUser;
import com.djrapitops.plan.system.webserver.Request;
+import com.djrapitops.plan.system.webserver.RequestTarget;
import com.djrapitops.plan.system.webserver.auth.Authentication;
import com.djrapitops.plan.system.webserver.response.RedirectResponse;
import com.djrapitops.plan.system.webserver.response.Response;
import com.djrapitops.plan.system.webserver.response.ResponseFactory;
-import java.util.List;
import java.util.Optional;
/**
@@ -43,7 +43,7 @@ public class RootPageHandler implements PageHandler {
}
@Override
- public Response getResponse(Request request, List target) throws WebException {
+ public Response getResponse(Request request, RequestTarget target) throws WebException {
Optional auth = request.getAuth();
if (!auth.isPresent()) {
return responseFactory.basicAuth();
@@ -65,7 +65,7 @@ public class RootPageHandler implements PageHandler {
}
@Override
- public boolean isAuthorized(Authentication auth, List target) {
+ public boolean isAuthorized(Authentication auth, RequestTarget target) {
return true;
}
}
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/ServerPageHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/ServerPageHandler.java
index 5bc1d0045..b947bdbf1 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/ServerPageHandler.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/ServerPageHandler.java
@@ -29,16 +29,15 @@ import com.djrapitops.plan.system.info.server.Server;
import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.processing.Processing;
import com.djrapitops.plan.system.webserver.Request;
+import com.djrapitops.plan.system.webserver.RequestTarget;
import com.djrapitops.plan.system.webserver.auth.Authentication;
import com.djrapitops.plan.system.webserver.cache.PageId;
import com.djrapitops.plan.system.webserver.cache.ResponseCache;
import com.djrapitops.plan.system.webserver.response.Response;
import com.djrapitops.plan.system.webserver.response.ResponseFactory;
-import com.djrapitops.plugin.api.Check;
import javax.inject.Inject;
import javax.inject.Singleton;
-import java.util.List;
import java.util.Optional;
import java.util.UUID;
@@ -72,7 +71,7 @@ public class ServerPageHandler implements PageHandler {
}
@Override
- public Response getResponse(Request request, List target) throws WebException {
+ public Response getResponse(Request request, RequestTarget target) throws WebException {
UUID serverUUID = getServerUUID(target);
boolean raw = target.size() >= 2 && target.get(1).equalsIgnoreCase("raw");
@@ -87,7 +86,7 @@ public class ServerPageHandler implements PageHandler {
return response;
} else {
checkDBState();
- if ((Check.isBungeeAvailable() || Check.isVelocityAvailable()) && serverInfo.getServerUUID().equals(serverUUID)) {
+ if (serverInfo.getServer().isProxy() && serverInfo.getServerUUID().equals(serverUUID)) {
return ResponseCache.loadResponse(PageId.SERVER.of(serverUUID), responseFactory::networkPageResponse);
}
return refreshNow(serverUUID);
@@ -115,7 +114,7 @@ public class ServerPageHandler implements PageHandler {
return responseFactory.refreshingAnalysisResponse();
}
- private UUID getServerUUID(List target) {
+ private UUID getServerUUID(RequestTarget target) {
// Default to current server's page
UUID serverUUID = serverInfo.getServerUUID();
@@ -136,7 +135,7 @@ public class ServerPageHandler implements PageHandler {
}
@Override
- public boolean isAuthorized(Authentication auth, List target) throws WebUserAuthException {
+ public boolean isAuthorized(Authentication auth, RequestTarget target) throws WebUserAuthException {
return auth.getWebUser().getPermLevel() <= 0;
}
}
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/TreePageHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/TreePageHandler.java
index 3f2bd655a..f082456ba 100644
--- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/TreePageHandler.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/TreePageHandler.java
@@ -19,12 +19,12 @@ package com.djrapitops.plan.system.webserver.pages;
import com.djrapitops.plan.api.exceptions.WebUserAuthException;
import com.djrapitops.plan.api.exceptions.connection.WebException;
import com.djrapitops.plan.system.webserver.Request;
+import com.djrapitops.plan.system.webserver.RequestTarget;
import com.djrapitops.plan.system.webserver.auth.Authentication;
import com.djrapitops.plan.system.webserver.response.Response;
import com.djrapitops.plan.system.webserver.response.ResponseFactory;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
/**
@@ -50,31 +50,31 @@ public abstract class TreePageHandler implements PageHandler {
public void registerPage(String targetPage, Response response, int requiredPerm) {
pages.put(targetPage, new PageHandler() {
@Override
- public Response getResponse(Request request, List target) {
+ public Response getResponse(Request request, RequestTarget target) {
return response;
}
@Override
- public boolean isAuthorized(Authentication auth, List target) throws WebUserAuthException {
+ public boolean isAuthorized(Authentication auth, RequestTarget target) throws WebUserAuthException {
return auth.getWebUser().getPermLevel() <= requiredPerm;
}
});
}
@Override
- public Response getResponse(Request request, List target) throws WebException {
+ public Response getResponse(Request request, RequestTarget target) throws WebException {
PageHandler pageHandler = getPageHandler(target);
return pageHandler != null
? pageHandler.getResponse(request, target)
: responseFactory.pageNotFound404();
}
- public PageHandler getPageHandler(List target) {
+ public PageHandler getPageHandler(RequestTarget target) {
if (target.isEmpty()) {
return pages.get("");
}
String targetPage = target.get(0);
- target.remove(0);
+ target.removeFirst();
return pages.get(targetPage);
}
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/JSONFactory.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/JSONFactory.java
new file mode 100644
index 000000000..f42363709
--- /dev/null
+++ b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/JSONFactory.java
@@ -0,0 +1,71 @@
+/*
+ * 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 GNU 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
+ * GNU 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 .
+ */
+package com.djrapitops.plan.system.webserver.pages.json;
+
+import com.djrapitops.plan.db.Database;
+import com.djrapitops.plan.db.access.queries.containers.ServerPlayersTableContainersQuery;
+import com.djrapitops.plan.extension.implementation.storage.queries.ExtensionServerPlayerDataTableQuery;
+import com.djrapitops.plan.system.database.DBSystem;
+import com.djrapitops.plan.system.settings.config.PlanConfig;
+import com.djrapitops.plan.system.settings.paths.DisplaySettings;
+import com.djrapitops.plan.system.settings.paths.TimeSettings;
+import com.djrapitops.plan.utilities.formatting.Formatters;
+import com.djrapitops.plan.utilities.html.tables.PlayersTableJSONParser;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import java.util.UUID;
+
+/**
+ * Factory with different JSON parsing placed to a single class.
+ *
+ * @author Rsl1122
+ */
+@Singleton
+public class JSONFactory {
+
+ private final PlanConfig config;
+ private final DBSystem dbSystem;
+ private final Formatters formatters;
+
+ @Inject
+ public JSONFactory(
+ PlanConfig config,
+ DBSystem dbSystem,
+ Formatters formatters
+ ) {
+ this.config = config;
+ this.dbSystem = dbSystem;
+ this.formatters = formatters;
+ }
+
+ public String serverPlayersTableJSON(UUID serverUUID) {
+ Integer xMostRecentPlayers = config.get(DisplaySettings.PLAYERS_PER_SERVER_PAGE);
+ Integer loginThreshold = config.get(TimeSettings.ACTIVE_LOGIN_THRESHOLD);
+ Long playtimeThreshold = config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD);
+ Boolean openPlayerLinksInNewTab = config.get(DisplaySettings.OPEN_PLAYER_LINKS_IN_NEW_TAB);
+
+ Database database = dbSystem.getDatabase();
+
+ return new PlayersTableJSONParser(
+ database.query(new ServerPlayersTableContainersQuery(serverUUID)),
+ database.query(new ExtensionServerPlayerDataTableQuery(serverUUID, xMostRecentPlayers)),
+ xMostRecentPlayers, playtimeThreshold, loginThreshold, openPlayerLinksInNewTab,
+ formatters
+ ).toJSONString();
+ }
+}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/PlayersTableJSONHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/PlayersTableJSONHandler.java
new file mode 100644
index 000000000..c426605db
--- /dev/null
+++ b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/PlayersTableJSONHandler.java
@@ -0,0 +1,93 @@
+/*
+ * 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 GNU 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
+ * GNU 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 .
+ */
+package com.djrapitops.plan.system.webserver.pages.json;
+
+import com.djrapitops.plan.api.exceptions.WebUserAuthException;
+import com.djrapitops.plan.api.exceptions.connection.BadRequestException;
+import com.djrapitops.plan.api.exceptions.connection.WebException;
+import com.djrapitops.plan.db.access.queries.objects.ServerQueries;
+import com.djrapitops.plan.system.database.DBSystem;
+import com.djrapitops.plan.system.info.server.Server;
+import com.djrapitops.plan.system.webserver.Request;
+import com.djrapitops.plan.system.webserver.RequestTarget;
+import com.djrapitops.plan.system.webserver.auth.Authentication;
+import com.djrapitops.plan.system.webserver.pages.PageHandler;
+import com.djrapitops.plan.system.webserver.response.Response;
+import com.djrapitops.plan.system.webserver.response.data.JSONResponse;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import java.util.Optional;
+import java.util.UUID;
+
+/**
+ * JSON handler for different Player table JSON requests.
+ *
+ * @author Rsl1122
+ * @see com.djrapitops.plan.utilities.html.tables.PlayersTableJSONParser For JSON parsing of /server players table.
+ */
+@Singleton
+public class PlayersTableJSONHandler implements PageHandler {
+
+ private final DBSystem dbSystem;
+ private final JSONFactory jsonFactory;
+
+ @Inject
+ public PlayersTableJSONHandler(
+ DBSystem dbSystem,
+ JSONFactory jsonFactory
+ ) {
+ this.jsonFactory = jsonFactory;
+ this.dbSystem = dbSystem;
+ }
+
+ @Override
+ public Response getResponse(Request request, RequestTarget target) throws WebException {
+ UUID serverUUID = getServerUUID(target); // Can throw BadRequestException
+ return new JSONResponse(jsonFactory.serverPlayersTableJSON(serverUUID));
+ }
+
+ private UUID getServerUUID(RequestTarget target) throws BadRequestException {
+ Optional serverUUID = target.getParameter("serverUUID");
+ if (serverUUID.isPresent()) {
+ return getServerUUIDDirectly(serverUUID.get());
+ } else {
+ return getServerUUIDFromName(target); // Preferred
+ }
+ }
+
+ private UUID getServerUUIDFromName(RequestTarget target) throws BadRequestException {
+ String serverName = target.getParameter("serverName")
+ .orElseThrow(() -> new BadRequestException("'serverName' parameter was not defined."));
+ return dbSystem.getDatabase().query(ServerQueries.fetchServerMatchingIdentifier(serverName))
+ .map(Server::getUuid)
+ .orElseThrow(() -> new BadRequestException("'serverName' was not found in the database.: '" + serverName + "'"));
+ }
+
+ private UUID getServerUUIDDirectly(String serverUUIDString) throws BadRequestException {
+ try {
+ return UUID.fromString(serverUUIDString);
+ } catch (IllegalArgumentException malformedUUIDException) {
+ throw new BadRequestException("'serverName' was not a valid UUID: " + malformedUUIDException.getMessage());
+ }
+ }
+
+ @Override
+ public boolean isAuthorized(Authentication auth, RequestTarget target) throws WebUserAuthException {
+ return auth.getWebUser().getPermLevel() <= 0;
+ }
+}
\ No newline at end of file
diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/mcmmo/McmmoHook.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/RootJSONHandler.java
similarity index 50%
rename from PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/mcmmo/McmmoHook.java
rename to Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/RootJSONHandler.java
index 562baebcd..b5690a138 100644
--- a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/mcmmo/McmmoHook.java
+++ b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/RootJSONHandler.java
@@ -14,38 +14,37 @@
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see .
*/
-package com.djrapitops.pluginbridge.plan.mcmmo;
+package com.djrapitops.plan.system.webserver.pages.json;
-import com.djrapitops.plan.data.plugin.HookHandler;
-import com.djrapitops.plan.utilities.formatting.Formatter;
-import com.djrapitops.plan.utilities.formatting.Formatters;
-import com.djrapitops.pluginbridge.plan.Hook;
+import com.djrapitops.plan.api.exceptions.WebUserAuthException;
+import com.djrapitops.plan.system.webserver.RequestTarget;
+import com.djrapitops.plan.system.webserver.auth.Authentication;
+import com.djrapitops.plan.system.webserver.pages.TreePageHandler;
+import com.djrapitops.plan.system.webserver.response.ResponseFactory;
import javax.inject.Inject;
import javax.inject.Singleton;
/**
- * A Class responsible for hooking to MCMMO and registering data sources.
+ * Root handler for different JSON end points.
*
* @author Rsl1122
-
*/
@Singleton
-public class McmmoHook extends Hook {
-
- private final Formatter decimalFormatter;
+public class RootJSONHandler extends TreePageHandler {
@Inject
- public McmmoHook(
- Formatters formatters
+ public RootJSONHandler(
+ ResponseFactory responseFactory,
+ PlayersTableJSONHandler playersTableJSONHandler
) {
- super("com.gmail.nossr50.mcMMO");
- decimalFormatter = formatters.decimals();
+ super(responseFactory);
+
+ registerPage("players", playersTableJSONHandler);
}
- public void hook(HookHandler handler) throws NoClassDefFoundError {
- if (enabled) {
- handler.addPluginDataSource(new McMmoData(decimalFormatter));
- }
+ @Override
+ public boolean isAuthorized(Authentication auth, RequestTarget target) throws WebUserAuthException {
+ return true;
}
-}
+}
\ No newline at end of file
diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/data/JSONResponse.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/data/JSONResponse.java
new file mode 100644
index 000000000..b114caa83
--- /dev/null
+++ b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/data/JSONResponse.java
@@ -0,0 +1,51 @@
+/*
+ * 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 GNU 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
+ * GNU 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 .
+ */
+package com.djrapitops.plan.system.webserver.response.data;
+
+import com.djrapitops.plan.system.webserver.response.Response;
+import com.djrapitops.plan.system.webserver.response.ResponseType;
+
+/**
+ * Generic JSON response implemented using Gson.
+ *
+ * Returns a JSON version of the given object.
+ *
+ * @author Rsl1122
+ */
+public class JSONResponse