mirror of
https://github.com/plan-player-analytics/Plan.git
synced 2024-12-22 17:18:40 +01:00
Fabric Platform Implemenation (#2018)
Adds a fabric specific Plan module that builds a separate jar. Co-authored-by: Vankka <vankka.main@gmail.com> Co-authored-by: DrexHD <nicknamedrex@gmail.com> Affects issues: - Close #1956
This commit is contained in:
parent
284372613c
commit
67153e8fc5
3
.gitignore
vendored
3
.gitignore
vendored
@ -3,8 +3,9 @@ Plan.iml
|
||||
PlanPluginBridge.iml
|
||||
.sonar/
|
||||
builds/
|
||||
# Nukkit creates server.log during tests for some reason.
|
||||
# Nukkit & Fabric create log files during tests for some reason.
|
||||
server.log
|
||||
/Plan/fabric/logs/
|
||||
|
||||
*.db
|
||||
|
||||
|
@ -139,7 +139,7 @@ subprojects {
|
||||
}
|
||||
|
||||
checkstyle {
|
||||
toolVersion "8.33"
|
||||
toolVersion "8.44"
|
||||
getConfigDirectory().set file("$rootProject.projectDir/config/checkstyle")
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,7 @@ task updateVersion(type: Copy) {
|
||||
include 'plugin.yml'
|
||||
include 'bungee.yml'
|
||||
include 'nukkit.yml'
|
||||
include 'fabric.mod.json'
|
||||
}
|
||||
into 'build/sources/resources/'
|
||||
filter(ReplaceTokens, tokens: [version: '' + project.ext.fullVersion])
|
||||
|
@ -50,6 +50,10 @@ public class CommandWithSubcommands extends Subcommand {
|
||||
return subcommands.stream().filter(sender::hasAllPermissionsFor).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<Subcommand> getSubcommands() {
|
||||
return subcommands;
|
||||
}
|
||||
|
||||
public void onHelp(CMDSender sender, Arguments arguments) {
|
||||
List<Subcommand> hasPermissionFor = getPermittedSubcommands(sender);
|
||||
sender.buildMessage()
|
||||
|
@ -65,7 +65,7 @@ public class PlayersMutator {
|
||||
.map(sessions -> sessions.stream().anyMatch(session -> {
|
||||
long start = session.getStart();
|
||||
long end = session.getEnd();
|
||||
return (after <= start && start <= before) || (after <= end && end <= before);
|
||||
return after <= start && start <= before || after <= end && end <= before;
|
||||
})).orElse(false)
|
||||
);
|
||||
}
|
||||
|
@ -85,7 +85,8 @@ public class Contributors {
|
||||
new Contributor("mbax", CODE),
|
||||
new Contributor("rymiel", CODE),
|
||||
new Contributor("Perchun_Pak", LANG),
|
||||
new Contributor("HexedHero", CODE)
|
||||
new Contributor("HexedHero", CODE),
|
||||
new Contributor("DrexHD", CODE)
|
||||
};
|
||||
|
||||
private Contributors() {
|
||||
|
@ -156,7 +156,7 @@ public class RequestHandler implements HttpHandler {
|
||||
}
|
||||
if (response.getCode() != 401 // Not failed
|
||||
&& response.getCode() != 403 // Not blocked
|
||||
&& (request != null && request.getUser().isPresent()) // Logged in
|
||||
&& request != null && request.getUser().isPresent() // Logged in
|
||||
) {
|
||||
bruteForceGuard.resetAttemptCount(accessor);
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ public class PlayerPageResolver implements Resolver {
|
||||
return uuidUtility.getNameOf(nameOrUUID).map(user.getName()::equalsIgnoreCase) // uuid matches user
|
||||
.orElse(false); // uuid or name don't match
|
||||
}).orElse(true); // No name or UUID given
|
||||
return user.hasPermission("page.player.other") || (user.hasPermission("page.player.self") && isOwnPage);
|
||||
return user.hasPermission("page.player.other") || user.hasPermission("page.player.self") && isOwnPage;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.djrapitops.plan.settings;
|
||||
|
||||
import com.djrapitops.plan.settings.config.ConfigReader;
|
||||
import com.djrapitops.plan.settings.config.PlanConfig;
|
||||
import com.djrapitops.plan.settings.config.changes.ConfigUpdater;
|
||||
import com.djrapitops.plan.settings.config.paths.PluginSettings;
|
||||
import com.djrapitops.plan.settings.network.ServerSettingsManager;
|
||||
import com.djrapitops.plan.settings.theme.Theme;
|
||||
import com.djrapitops.plan.storage.file.PlanFiles;
|
||||
import com.djrapitops.plan.utilities.logging.ErrorLogger;
|
||||
import net.playeranalytics.plugin.server.PluginLogger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Config system for Fabric.
|
||||
*
|
||||
* @author Kopo942
|
||||
*/
|
||||
@Singleton
|
||||
public class FabricConfigSystem extends ConfigSystem {
|
||||
|
||||
private final ConfigUpdater configUpdater;
|
||||
private final ServerSettingsManager serverSettingsManager;
|
||||
|
||||
@Inject
|
||||
public FabricConfigSystem(
|
||||
PlanFiles files,
|
||||
PlanConfig config,
|
||||
ConfigUpdater configUpdater,
|
||||
ServerSettingsManager serverSettingsManager,
|
||||
Theme theme,
|
||||
PluginLogger logger,
|
||||
ErrorLogger errorLogger
|
||||
) {
|
||||
super(files, config, theme, logger, errorLogger);
|
||||
this.configUpdater = configUpdater;
|
||||
this.serverSettingsManager = serverSettingsManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enable() {
|
||||
super.enable();
|
||||
if (config.isTrue(PluginSettings.PROXY_COPY_CONFIG)) {
|
||||
serverSettingsManager.enable();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
serverSettingsManager.disable();
|
||||
super.disable();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void copyDefaults() throws IOException {
|
||||
configUpdater.applyConfigUpdate(config);
|
||||
try (ConfigReader reader = new ConfigReader(files.getResourceFromJar("config.yml").asInputStream())) {
|
||||
config.copyMissing(reader.read());
|
||||
}
|
||||
}
|
||||
}
|
@ -40,8 +40,8 @@ public class ExtensionTableRowValueLengthPatch extends Patch {
|
||||
@Override
|
||||
public boolean hasBeenApplied() {
|
||||
return dbType == DBType.SQLITE || // SQLite does not limit varchar lengths
|
||||
(columnVarcharLength(playerTable, ExtensionPlayerTableValueTable.VALUE_4) >= 250
|
||||
&& columnVarcharLength(serverTable, ExtensionServerTableValueTable.VALUE_5) >= 250);
|
||||
columnVarcharLength(playerTable, ExtensionPlayerTableValueTable.VALUE_4) >= 250
|
||||
&& columnVarcharLength(serverTable, ExtensionServerTableValueTable.VALUE_5) >= 250;
|
||||
}
|
||||
|
||||
private int columnVarcharLength(String table, String column) {
|
||||
|
@ -38,10 +38,10 @@ public class GeoInfoOptimizationPatch extends Patch {
|
||||
@Override
|
||||
public boolean hasBeenApplied() {
|
||||
return !hasTable(oldTableName)
|
||||
|| (hasColumn(oldTableName, GeoInfoTable.ID)
|
||||
|| hasColumn(oldTableName, GeoInfoTable.ID)
|
||||
&& hasColumn(oldTableName, GeoInfoTable.USER_UUID)
|
||||
&& !hasColumn(oldTableName, "user_id")
|
||||
&& !hasTable(tempTableName)); // If this table exists the patch has failed to finish.
|
||||
&& !hasTable(tempTableName); // If this table exists the patch has failed to finish.
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -41,7 +41,7 @@ public class KillsServerIDPatch extends Patch {
|
||||
|
||||
// KillsOptimizationPatch makes this patch incompatible with newer patch versions.
|
||||
return hasColumn(tableName, KillsTable.SERVER_UUID)
|
||||
|| (hasColumn(tableName, columnName) && allValuesHaveValueZero(tableName, columnName));
|
||||
|| hasColumn(tableName, columnName) && allValuesHaveValueZero(tableName, columnName);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
28
Plan/common/src/main/resources/fabric.mod.json
Normal file
28
Plan/common/src/main/resources/fabric.mod.json
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "plan",
|
||||
"name": "Plan",
|
||||
"version": "@version@",
|
||||
|
||||
"environment": "server",
|
||||
"entrypoints": {
|
||||
"server": [
|
||||
"net.playeranalytics.plan.PlanFabric"
|
||||
]
|
||||
},
|
||||
"mixins": [
|
||||
"plan.mixins.json"
|
||||
],
|
||||
"depends": {
|
||||
"minecraft": ">=1.17",
|
||||
"java": ">=16",
|
||||
"fabric-api-base": "*",
|
||||
"fabric-command-api-v1": "*",
|
||||
"fabric-entity-events-v1": "*",
|
||||
"fabric-lifecycle-events-v1": "*",
|
||||
"fabric-networking-v0": "*"
|
||||
},
|
||||
"suggests": {
|
||||
"fabric-permissions-api-v0": "*"
|
||||
}
|
||||
}
|
18
Plan/common/src/main/resources/plan.mixins.json
Normal file
18
Plan/common/src/main/resources/plan.mixins.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"required": true,
|
||||
"minVersion": "0.8",
|
||||
"package": "net.playeranalytics.plan.gathering.listeners.events.mixin",
|
||||
"compatibilityLevel": "JAVA_16",
|
||||
"mixins": [
|
||||
"EntityMixin",
|
||||
"KickCommandMixin",
|
||||
"PlayerEntityMixin",
|
||||
"PlayerManagerMixin",
|
||||
"ServerCommandSourceMixin",
|
||||
"ServerPlayerEntityMixin",
|
||||
"ServerPlayNetworkHandlerMixin"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
}
|
||||
}
|
63
Plan/fabric/build.gradle
Normal file
63
Plan/fabric/build.gradle
Normal file
@ -0,0 +1,63 @@
|
||||
plugins {
|
||||
id 'fabric-loom' version '0.8-SNAPSHOT'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
shadow "net.playeranalytics:platform-abstraction-layer-api:$palVersion"
|
||||
implementation project(path: ":common", configuration: 'shadow')
|
||||
shadow project(path: ":common", configuration: 'shadow')
|
||||
compileOnly project(":api")
|
||||
modImplementation('me.lucko:fabric-permissions-api:0.1-SNAPSHOT')
|
||||
|
||||
minecraft "com.mojang:minecraft:1.17.1"
|
||||
mappings "net.fabricmc:yarn:1.17.1+build.21:v2"
|
||||
modImplementation "net.fabricmc:fabric-loader:0.11.6"
|
||||
|
||||
// Fabric API
|
||||
Set<String> apiModules = [
|
||||
'fabric-api-base',
|
||||
'fabric-command-api-v1',
|
||||
'fabric-entity-events-v1',
|
||||
'fabric-lifecycle-events-v1',
|
||||
'fabric-networking-api-v1'
|
||||
]
|
||||
|
||||
apiModules.forEach {
|
||||
modImplementation(fabricApi.module(it, "0.37.0+1.17"))
|
||||
}
|
||||
|
||||
testImplementation project(path: ":common", configuration: 'testArtifacts')
|
||||
}
|
||||
|
||||
compileJava {
|
||||
options.release = 16
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
configurations = [project.configurations.shadow]
|
||||
exclude('net.fabricmc:*')
|
||||
exclude('/mappings/')
|
||||
|
||||
relocate('org.apache', 'plan.org.apache') {
|
||||
exclude 'org/apache/logging/**'
|
||||
}
|
||||
relocate 'dagger', 'plan.dagger'
|
||||
relocate 'com.mysql', 'plan.com.mysql'
|
||||
// Don't relocate SQLite since the org.sqlite.NativeDB class calls are not relocated properly
|
||||
// relocate 'org.sqlite', 'plan.org.sqlite'
|
||||
relocate 'javax.inject', 'plan.javax.inject'
|
||||
relocate 'com.github.benmanes', 'plan.com.github.benmanes'
|
||||
}
|
||||
|
||||
remapJar {
|
||||
dependsOn tasks.shadowJar
|
||||
mustRunAfter tasks.shadowJar
|
||||
input = shadowJar.archiveFile.get()
|
||||
addNestedDependencies = true
|
||||
|
||||
destinationDirectory.set(file("$rootDir/builds/"))
|
||||
archiveBaseName.set('PlanFabric')
|
||||
archiveClassifier.set('')
|
||||
}
|
||||
|
||||
shadowJar.finalizedBy(remapJar)
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.playeranalytics.plan;
|
||||
|
||||
import com.djrapitops.plan.gathering.ServerShutdownSave;
|
||||
import com.djrapitops.plan.settings.locale.Locale;
|
||||
import com.djrapitops.plan.storage.database.DBSystem;
|
||||
import com.djrapitops.plan.utilities.logging.ErrorLogger;
|
||||
import net.minecraft.server.dedicated.MinecraftDedicatedServer;
|
||||
import net.playeranalytics.plugin.server.PluginLogger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
/**
|
||||
* ServerShutdownSave implementation for Fabric-based servers.
|
||||
*
|
||||
* @author Kopo942
|
||||
*/
|
||||
@Singleton
|
||||
public class FabricServerShutdownSave extends ServerShutdownSave {
|
||||
|
||||
private final MinecraftDedicatedServer server;
|
||||
|
||||
@Inject
|
||||
public FabricServerShutdownSave(
|
||||
MinecraftDedicatedServer server,
|
||||
Locale locale,
|
||||
DBSystem dbSystem,
|
||||
PluginLogger logger,
|
||||
ErrorLogger errorLogger
|
||||
) {
|
||||
super(locale, dbSystem, logger, errorLogger);
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean checkServerShuttingDownStatus() {
|
||||
return !server.isRunning();
|
||||
}
|
||||
}
|
@ -0,0 +1,172 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.playeranalytics.plan;
|
||||
|
||||
import com.djrapitops.plan.PlanPlugin;
|
||||
import com.djrapitops.plan.PlanSystem;
|
||||
import com.djrapitops.plan.commands.use.ColorScheme;
|
||||
import com.djrapitops.plan.commands.use.Subcommand;
|
||||
import com.djrapitops.plan.exceptions.EnableException;
|
||||
import com.djrapitops.plan.gathering.ServerShutdownSave;
|
||||
import com.djrapitops.plan.settings.locale.Locale;
|
||||
import com.djrapitops.plan.settings.locale.lang.PluginLang;
|
||||
import com.djrapitops.plan.settings.theme.PlanColorScheme;
|
||||
import net.fabricmc.api.DedicatedServerModInitializer;
|
||||
import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.dedicated.MinecraftDedicatedServer;
|
||||
import net.playeranalytics.plan.commands.CommandManager;
|
||||
import net.playeranalytics.plugin.FabricPlatformLayer;
|
||||
import net.playeranalytics.plugin.PlatformAbstractionLayer;
|
||||
import net.playeranalytics.plugin.scheduling.RunnableFactory;
|
||||
import net.playeranalytics.plugin.server.PluginLogger;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
/**
|
||||
* Main class for Plan's Fabric version.
|
||||
*
|
||||
* @author Kopo942
|
||||
*/
|
||||
public class PlanFabric implements PlanPlugin, DedicatedServerModInitializer {
|
||||
|
||||
private MinecraftDedicatedServer server;
|
||||
private CommandManager commandManager;
|
||||
|
||||
private PlanSystem system;
|
||||
private Locale locale;
|
||||
private ServerShutdownSave serverShutdownSave;
|
||||
|
||||
private PluginLogger pluginLogger;
|
||||
private RunnableFactory runnableFactory;
|
||||
private PlatformAbstractionLayer abstractionLayer;
|
||||
|
||||
@Override
|
||||
public InputStream getResource(String resource) {
|
||||
return this.getClass().getResourceAsStream("/" + resource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColorScheme getColorScheme() {
|
||||
return PlanColorScheme.create(system.getConfigSystem().getConfig(), pluginLogger);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlanSystem getSystem() {
|
||||
return system;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerCommand(Subcommand command) {
|
||||
commandManager.registerRoot(command, runnableFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
abstractionLayer = new FabricPlatformLayer(this);
|
||||
pluginLogger = abstractionLayer.getPluginLogger();
|
||||
runnableFactory = abstractionLayer.getRunnableFactory();
|
||||
|
||||
PlanFabricComponent component = DaggerPlanFabricComponent.builder()
|
||||
.plan(this)
|
||||
.abstractionLayer(abstractionLayer)
|
||||
.server(server)
|
||||
.build();
|
||||
|
||||
try {
|
||||
system = component.system();
|
||||
serverShutdownSave = component.serverShutdownSave();
|
||||
locale = system.getLocaleSystem().getLocale();
|
||||
system.enable();
|
||||
|
||||
pluginLogger.info(locale.getString(PluginLang.ENABLED));
|
||||
} catch (AbstractMethodError e) {
|
||||
pluginLogger.error("Plugin ran into AbstractMethodError, server restart is required! This error is likely caused by updating the JAR without a restart.");
|
||||
} catch (EnableException e) {
|
||||
pluginLogger.error("----------------------------------------");
|
||||
pluginLogger.error("Error: " + e.getMessage());
|
||||
pluginLogger.error("----------------------------------------");
|
||||
pluginLogger.error("Plugin failed to initialize correctly. If this issue is caused by config settings you can use /plan reload");
|
||||
onDisable();
|
||||
} catch (Exception e) {
|
||||
String version = abstractionLayer.getPluginInformation().getVersion();
|
||||
pluginLogger.error(this.getClass().getSimpleName() + "-v" + version, e);
|
||||
pluginLogger.error("Plugin Failed to Initialize Correctly. If this issue is caused by config settings you can use /plan reload");
|
||||
pluginLogger.error("This error should be reported at https://github.com/plan-player-analytics/Plan/issues");
|
||||
onDisable();
|
||||
}
|
||||
registerCommand(component.planCommand().build());
|
||||
if (system != null) {
|
||||
system.getProcessing().submitNonCritical(() -> system.getListenerSystem().callEnableEvent(this));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
storeSessionsOnShutdown();
|
||||
runnableFactory.cancelAllKnownTasks();
|
||||
|
||||
if (system != null) system.disable();
|
||||
|
||||
pluginLogger.info(Locale.getStringNullSafe(locale, PluginLang.DISABLED));
|
||||
}
|
||||
|
||||
private void storeSessionsOnShutdown() {
|
||||
if (serverShutdownSave != null) {
|
||||
Optional<Future<?>> complete = serverShutdownSave.performSave();
|
||||
if (complete.isPresent()) {
|
||||
try {
|
||||
complete.get().get(4, TimeUnit.SECONDS); // wait for completion for 4s
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
} catch (ExecutionException e) {
|
||||
pluginLogger.error("Failed to save sessions to database on shutdown: " + e.getCause().getMessage());
|
||||
} catch (TimeoutException e) {
|
||||
pluginLogger.info(Locale.getStringNullSafe(locale, PluginLang.DISABLED_UNSAVED_SESSIONS_TIMEOUT));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getDataFolder() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitializeServer() {
|
||||
ServerLifecycleEvents.SERVER_STARTING.register(server -> {
|
||||
this.server = (MinecraftDedicatedServer) server;
|
||||
onEnable();
|
||||
});
|
||||
|
||||
CommandRegistrationCallback.EVENT.register((dispatcher, dedicated) -> commandManager = new CommandManager(dispatcher, system.getErrorLogger()));
|
||||
|
||||
ServerLifecycleEvents.SERVER_STOPPING.register(server -> onDisable());
|
||||
}
|
||||
|
||||
public MinecraftServer getServer() {
|
||||
return server;
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.playeranalytics.plan;
|
||||
|
||||
import com.djrapitops.plan.PlanPlugin;
|
||||
import com.djrapitops.plan.PlanSystem;
|
||||
import com.djrapitops.plan.commands.PlanCommand;
|
||||
import com.djrapitops.plan.gathering.ServerShutdownSave;
|
||||
import com.djrapitops.plan.modules.FiltersModule;
|
||||
import com.djrapitops.plan.modules.PlatformAbstractionLayerModule;
|
||||
import com.djrapitops.plan.modules.ServerCommandModule;
|
||||
import com.djrapitops.plan.modules.SystemObjectProvidingModule;
|
||||
import dagger.BindsInstance;
|
||||
import dagger.Component;
|
||||
import net.minecraft.server.dedicated.MinecraftDedicatedServer;
|
||||
import net.playeranalytics.plan.identification.properties.FabricServerProperties;
|
||||
import net.playeranalytics.plan.modules.fabric.FabricServerPropertiesModule;
|
||||
import net.playeranalytics.plan.modules.fabric.FabricSuperClassBindingModule;
|
||||
import net.playeranalytics.plan.modules.fabric.FabricTaskModule;
|
||||
import net.playeranalytics.plugin.PlatformAbstractionLayer;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
/**
|
||||
* Dagger component for constructing the required plugin systems on Fabric.
|
||||
*
|
||||
* @author Kopo942
|
||||
*/
|
||||
@Singleton
|
||||
@Component(modules = {
|
||||
SystemObjectProvidingModule.class,
|
||||
PlatformAbstractionLayerModule.class,
|
||||
FiltersModule.class,
|
||||
|
||||
ServerCommandModule.class,
|
||||
FabricServerPropertiesModule.class,
|
||||
FabricSuperClassBindingModule.class,
|
||||
FabricTaskModule.class
|
||||
})
|
||||
public interface PlanFabricComponent {
|
||||
|
||||
PlanCommand planCommand();
|
||||
|
||||
PlanSystem system();
|
||||
|
||||
ServerShutdownSave serverShutdownSave();
|
||||
|
||||
@Component.Builder
|
||||
interface Builder {
|
||||
|
||||
@BindsInstance
|
||||
Builder plan(PlanPlugin plan);
|
||||
|
||||
@BindsInstance
|
||||
Builder abstractionLayer(PlatformAbstractionLayer abstractionLayer);
|
||||
|
||||
@BindsInstance
|
||||
Builder server(MinecraftDedicatedServer server);
|
||||
|
||||
@BindsInstance
|
||||
Builder serverProperties(FabricServerProperties serverProperties);
|
||||
|
||||
PlanFabricComponent build();
|
||||
}
|
||||
}
|
@ -0,0 +1,148 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.playeranalytics.plan.commands;
|
||||
|
||||
import com.djrapitops.plan.commands.use.Arguments;
|
||||
import com.djrapitops.plan.commands.use.CMDSender;
|
||||
import com.djrapitops.plan.commands.use.CommandWithSubcommands;
|
||||
import com.djrapitops.plan.commands.use.Subcommand;
|
||||
import com.djrapitops.plan.utilities.logging.ErrorContext;
|
||||
import com.djrapitops.plan.utilities.logging.ErrorLogger;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.builder.ArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
import me.lucko.fabric.api.permissions.v0.Permissions;
|
||||
import net.minecraft.command.CommandSource;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.text.LiteralText;
|
||||
import net.playeranalytics.plugin.scheduling.RunnableFactory;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class CommandManager {
|
||||
|
||||
private final CommandDispatcher<ServerCommandSource> dispatcher;
|
||||
private RunnableFactory runnableFactory;
|
||||
private LiteralArgumentBuilder<ServerCommandSource> root;
|
||||
private final ErrorLogger errorLogger;
|
||||
|
||||
public CommandManager(CommandDispatcher<ServerCommandSource> dispatcher, ErrorLogger errorLogger) {
|
||||
this.dispatcher = dispatcher;
|
||||
this.errorLogger = errorLogger;
|
||||
}
|
||||
|
||||
public static boolean checkPermission(ServerCommandSource src, String permission) {
|
||||
if (isPermissionsApiAvailable()) {
|
||||
return Permissions.check(src, permission, 2);
|
||||
} else if (src.hasPermissionLevel(2)) {
|
||||
return true;
|
||||
} else {
|
||||
return switch (permission) {
|
||||
case "plan.player.self", "plan.ingame.self", "plan.register.self", "plan.unregister.self", "plan.json.self" -> true;
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isPermissionsApiAvailable() {
|
||||
try {
|
||||
Class.forName("me.lucko.fabric.api.permissions.v0.Permissions");
|
||||
return true;
|
||||
} catch (ClassNotFoundException e) {
|
||||
return false; // not available
|
||||
}
|
||||
}
|
||||
|
||||
public CompletableFuture<Suggestions> arguments(final Subcommand subcommand, final CommandContext<ServerCommandSource> ctx, final SuggestionsBuilder builder) {
|
||||
return CommandSource.suggestMatching(subcommand.getArgumentResolver().apply((CMDSender) ctx.getSource(), new Arguments(new String[0])), builder);
|
||||
}
|
||||
|
||||
private int execute(CommandContext<ServerCommandSource> ctx, Subcommand subcommand) {
|
||||
runnableFactory.create(() -> {
|
||||
try {
|
||||
subcommand.getExecutor().accept((CMDSender) ctx.getSource(), new Arguments(getCommandArguments(ctx)));
|
||||
} catch (Exception e) {
|
||||
ctx.getSource().sendError(new LiteralText("An internal error occurred, see the console for details."));
|
||||
errorLogger.error(e, ErrorContext.builder()
|
||||
.related(ctx.getSource().getClass())
|
||||
.related(subcommand.getPrimaryAlias() + " " + getCommandArguments(ctx))
|
||||
.build());
|
||||
}
|
||||
}).runTaskAsynchronously();
|
||||
return 1;
|
||||
}
|
||||
|
||||
private String getCommandArguments(CommandContext<ServerCommandSource> ctx) {
|
||||
String arguments;
|
||||
try {
|
||||
arguments = StringArgumentType.getString(ctx, "arguments");
|
||||
} catch (IllegalArgumentException e) {
|
||||
arguments = "";
|
||||
}
|
||||
return arguments;
|
||||
}
|
||||
|
||||
private void build() {
|
||||
dispatcher.register(root);
|
||||
}
|
||||
|
||||
public void registerRoot(Subcommand subcommand, RunnableFactory runnableFactory) {
|
||||
this.runnableFactory = runnableFactory;
|
||||
root = buildCommand(subcommand, subcommand.getPrimaryAlias());
|
||||
if (subcommand instanceof CommandWithSubcommands withSubcommands) {
|
||||
for (Subcommand cmd : withSubcommands.getSubcommands()) {
|
||||
registerChild(cmd, root);
|
||||
}
|
||||
}
|
||||
build();
|
||||
}
|
||||
|
||||
public void registerChild(Subcommand subcommand, ArgumentBuilder<ServerCommandSource, ?> parent) {
|
||||
for (String alias : subcommand.getAliases()) {
|
||||
LiteralArgumentBuilder<ServerCommandSource> argumentBuilder = buildCommand(subcommand, alias);
|
||||
if (subcommand instanceof CommandWithSubcommands withSubcommands) {
|
||||
for (Subcommand cmd : withSubcommands.getSubcommands()) {
|
||||
registerChild(cmd, argumentBuilder);
|
||||
}
|
||||
}
|
||||
parent.then(argumentBuilder);
|
||||
}
|
||||
}
|
||||
|
||||
private LiteralArgumentBuilder<ServerCommandSource> buildCommand(Subcommand subcommand, String alias) {
|
||||
RequiredArgumentBuilder<ServerCommandSource, String> arguments = RequiredArgumentBuilder.argument("arguments", StringArgumentType.greedyString());
|
||||
arguments.suggests((context, builder) -> arguments(subcommand, context, builder));
|
||||
arguments.executes(ctx -> execute(ctx, subcommand));
|
||||
LiteralArgumentBuilder<ServerCommandSource> literal = LiteralArgumentBuilder.literal(alias);
|
||||
literal.executes(ctx -> execute(ctx, subcommand));
|
||||
literal.requires(src -> {
|
||||
for (String permission : subcommand.getRequiredPermissions()) {
|
||||
if (!checkPermission(src, permission)) return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
literal.then(arguments);
|
||||
return literal;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.playeranalytics.plan.commands.use;
|
||||
|
||||
import com.djrapitops.plan.commands.use.CMDSender;
|
||||
import com.djrapitops.plan.commands.use.MessageBuilder;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.text.*;
|
||||
import org.apache.commons.text.TextStringBuilder;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class FabricMessageBuilder implements MessageBuilder {
|
||||
|
||||
private final ServerCommandSource sender;
|
||||
private final MutableText builder;
|
||||
private final FabricMessageBuilder previous;
|
||||
|
||||
public FabricMessageBuilder(ServerCommandSource sender) {
|
||||
this(sender, null);
|
||||
}
|
||||
|
||||
FabricMessageBuilder(ServerCommandSource sender, FabricMessageBuilder previous) {
|
||||
this.sender = sender;
|
||||
this.builder = new LiteralText("");
|
||||
this.previous = previous;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageBuilder addPart(String s) {
|
||||
FabricMessageBuilder newBuilder = new FabricMessageBuilder(sender, this);
|
||||
newBuilder.builder.append(Text.of(s));
|
||||
return newBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageBuilder newLine() {
|
||||
builder.append(Text.of("\n"));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageBuilder link(String url) {
|
||||
builder.styled(style -> style.withClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, url)));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageBuilder command(String command) {
|
||||
builder.styled(style -> style.withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, command.charAt(0) == '/' ? command : '/' + command)));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageBuilder hover(String message) {
|
||||
builder.styled(style -> style.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new LiteralText(message))));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageBuilder hover(String... lines) {
|
||||
builder.styled(style -> style.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new LiteralText(new TextStringBuilder().appendWithSeparators(lines, "\n").toString()))));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageBuilder hover(Collection<String> lines) {
|
||||
builder.styled(style -> style.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new LiteralText(new TextStringBuilder().appendWithSeparators(lines, "\n").toString()))));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageBuilder indent(int amount) {
|
||||
for (int i = 0; i < amount; i++) {
|
||||
builder.append(Text.of(" "));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageBuilder tabular(CharSequence charSequence) {
|
||||
addPart(((CMDSender) sender).getFormatter().table(charSequence.toString(), ":"));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send() {
|
||||
if (previous == null) {
|
||||
sender.sendFeedback(builder, false);
|
||||
} else {
|
||||
previous.builder.append(builder);
|
||||
previous.send();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.playeranalytics.plan.gathering;
|
||||
|
||||
import com.djrapitops.plan.gathering.ServerSensor;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.server.dedicated.MinecraftDedicatedServer;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Singleton
|
||||
public class FabricSensor implements ServerSensor<ServerWorld> {
|
||||
|
||||
private final MinecraftDedicatedServer server;
|
||||
|
||||
@Inject
|
||||
public FabricSensor(
|
||||
MinecraftDedicatedServer server
|
||||
) {
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getTPS() {
|
||||
//Returns the ticks per second of the last 100 ticks
|
||||
int length = server.lastTickLengths.length;
|
||||
double totalTickLength = 0;
|
||||
int count = 0;
|
||||
for (long tickLength : server.lastTickLengths) {
|
||||
if (tickLength == 0) continue; // Ignore uninitialized values in array
|
||||
totalTickLength += Math.max(tickLength, TimeUnit.MILLISECONDS.toNanos(50));
|
||||
count++;
|
||||
}
|
||||
double averageTickLength = totalTickLength / count;
|
||||
return count != 0 ? TimeUnit.SECONDS.toNanos(1) / averageTickLength : -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<ServerWorld> getWorlds() {
|
||||
return server.getWorlds();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEntityCount(ServerWorld world) {
|
||||
int entities = 0;
|
||||
for (Entity ignored : world.iterateEntities()) {
|
||||
entities++;
|
||||
}
|
||||
return entities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getChunkCount(ServerWorld world) {
|
||||
return world.getChunkManager().getLoadedChunkCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsDirectTPS() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOnlinePlayerCount() {
|
||||
return server.getCurrentPlayerCount();
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.playeranalytics.plan.gathering.listeners;
|
||||
|
||||
/**
|
||||
* Interface for all listeners on the Fabric platform.
|
||||
* Enables registering, enabling and disabling a listener.
|
||||
*/
|
||||
public interface FabricListener {
|
||||
|
||||
void register();
|
||||
|
||||
boolean isEnabled();
|
||||
|
||||
void enable();
|
||||
|
||||
void disable();
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.playeranalytics.plan.gathering.listeners;
|
||||
|
||||
import com.djrapitops.plan.PlanPlugin;
|
||||
import com.djrapitops.plan.capability.CapabilitySvc;
|
||||
import com.djrapitops.plan.gathering.listeners.ListenerSystem;
|
||||
import net.playeranalytics.plan.PlanFabric;
|
||||
import net.playeranalytics.plan.gathering.listeners.events.PlanFabricEvents;
|
||||
import net.playeranalytics.plan.gathering.listeners.fabric.*;
|
||||
import net.playeranalytics.plugin.server.Listeners;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* Listener system for the Fabric platform.
|
||||
*
|
||||
* @author Kopo942
|
||||
*/
|
||||
public class FabricListenerSystem extends ListenerSystem {
|
||||
|
||||
private final ChatListener chatListener;
|
||||
private final DeathEventListener deathEventListener;
|
||||
private final FabricAFKListener fabricAFKListener;
|
||||
private final GameModeChangeListener gameModeChangeListener;
|
||||
private final PlayerOnlineListener playerOnlineListener;
|
||||
private final WorldChangeListener worldChangeListener;
|
||||
private final Listeners listeners;
|
||||
|
||||
@Inject
|
||||
public FabricListenerSystem(
|
||||
ChatListener chatListener,
|
||||
DeathEventListener deathEventListener,
|
||||
FabricAFKListener fabricAFKListener,
|
||||
GameModeChangeListener gameModeChangeListener,
|
||||
PlayerOnlineListener playerOnlineListener,
|
||||
WorldChangeListener worldChangeListener,
|
||||
Listeners listeners
|
||||
) {
|
||||
this.chatListener = chatListener;
|
||||
this.deathEventListener = deathEventListener;
|
||||
this.fabricAFKListener = fabricAFKListener;
|
||||
this.playerOnlineListener = playerOnlineListener;
|
||||
this.gameModeChangeListener = gameModeChangeListener;
|
||||
this.worldChangeListener = worldChangeListener;
|
||||
this.listeners = listeners;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerListeners() {
|
||||
listeners.registerListener(chatListener);
|
||||
listeners.registerListener(deathEventListener);
|
||||
listeners.registerListener(fabricAFKListener);
|
||||
listeners.registerListener(gameModeChangeListener);
|
||||
listeners.registerListener(playerOnlineListener);
|
||||
listeners.registerListener(worldChangeListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void unregisterListeners() {
|
||||
listeners.unregisterListener(chatListener);
|
||||
listeners.unregisterListener(deathEventListener);
|
||||
listeners.unregisterListener(fabricAFKListener);
|
||||
listeners.unregisterListener(gameModeChangeListener);
|
||||
listeners.unregisterListener(playerOnlineListener);
|
||||
listeners.unregisterListener(worldChangeListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void callEnableEvent(PlanPlugin plugin) {
|
||||
boolean isEnabled = plugin.isSystemEnabled();
|
||||
PlanFabricEvents.ON_ENABLE.invoker().onEnable((PlanFabric) plugin);
|
||||
CapabilitySvc.notifyAboutEnable(isEnabled);
|
||||
}
|
||||
}
|
@ -0,0 +1,165 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.playeranalytics.plan.gathering.listeners.events;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import net.fabricmc.fabric.api.event.Event;
|
||||
import net.fabricmc.fabric.api.event.EventFactory;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.server.network.ServerPlayNetworkHandler;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.world.GameMode;
|
||||
import net.playeranalytics.plan.PlanFabric;
|
||||
|
||||
import java.net.SocketAddress;
|
||||
import java.util.Collection;
|
||||
|
||||
public class PlanFabricEvents {
|
||||
|
||||
public static final Event<OnKilled> ON_KILLED = EventFactory.createArrayBacked(OnKilled.class, callbacks -> (killed, killer) -> {
|
||||
for (OnKilled callback : callbacks) {
|
||||
callback.onKilled(killed, killer);
|
||||
}
|
||||
});
|
||||
|
||||
public static final Event<OnChat> ON_CHAT = EventFactory.createArrayBacked(OnChat.class, callbacks -> (handler, message) -> {
|
||||
for (OnChat callback : callbacks) {
|
||||
callback.onChat(handler, message);
|
||||
}
|
||||
});
|
||||
|
||||
public static final Event<OnMove> ON_MOVE = EventFactory.createArrayBacked(OnMove.class, callbacks -> (handler, packet) -> {
|
||||
for (OnMove callback : callbacks) {
|
||||
callback.onMove(handler, packet);
|
||||
}
|
||||
});
|
||||
|
||||
public static final Event<OnGameModeChange> ON_GAMEMODE_CHANGE = EventFactory.createArrayBacked(OnGameModeChange.class, callbacks -> (handler, packet) -> {
|
||||
for (OnGameModeChange callback : callbacks) {
|
||||
callback.onGameModeChange(handler, packet);
|
||||
}
|
||||
});
|
||||
|
||||
public static final Event<OnPlayerKicked> ON_KICKED = EventFactory.createArrayBacked(OnPlayerKicked.class, callbacks -> (source, targets, reason) -> {
|
||||
for (OnPlayerKicked callback : callbacks) {
|
||||
callback.onKicked(source, targets, reason);
|
||||
}
|
||||
});
|
||||
|
||||
public static final Event<OnLogin> ON_LOGIN = EventFactory.createArrayBacked(OnLogin.class, callbacks -> (address, profile, reason) -> {
|
||||
for (OnLogin callback : callbacks) {
|
||||
callback.onLogin(address, profile, reason);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Called when Plan is enabled.
|
||||
* <p>
|
||||
* This includes, but might not be limited to:
|
||||
* <ul>
|
||||
* <li>First time the plugin enables successfully</li>
|
||||
* <li>Plan is reloaded</li>
|
||||
* <li>Plan is enabled after it was disabled</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* This event provides full access to the Plan instance. However, <strong>it is advised to
|
||||
* only call {@link PlanFabric#isSystemEnabled} to determine if the enable was successful.</strong>
|
||||
* It is not guaranteed that this event is called when the plugin fails to enable properly.
|
||||
*/
|
||||
public static final Event<OnEnable> ON_ENABLE = EventFactory.createArrayBacked(OnEnable.class, callbacks -> plugin -> {
|
||||
for (OnEnable callback : callbacks) {
|
||||
callback.onEnable(plugin);
|
||||
}
|
||||
});
|
||||
|
||||
@FunctionalInterface
|
||||
public interface OnKilled {
|
||||
/**
|
||||
* Called when a living entity is killed
|
||||
*
|
||||
* @param killed the entity that died
|
||||
* @param killer the entity that killed
|
||||
*/
|
||||
void onKilled(LivingEntity killed, Entity killer);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface OnChat {
|
||||
/**
|
||||
* Called when a player sends a chat message / command
|
||||
*
|
||||
* @param handler the handler of the sending player
|
||||
* @param message the message sent (starts with "/" if it is a command)
|
||||
*/
|
||||
void onChat(ServerPlayNetworkHandler handler, String message);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface OnMove {
|
||||
/**
|
||||
* Called when a sends a valid movement packet
|
||||
*
|
||||
* @param handler the handler of the sending player
|
||||
* @param packet the send packet
|
||||
*/
|
||||
void onMove(ServerPlayNetworkHandler handler, PlayerMoveC2SPacket packet);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface OnGameModeChange {
|
||||
/**
|
||||
* Called when a player changes gamemode
|
||||
*
|
||||
* @param player the player that changed gamemodes
|
||||
* @param newGameMode the new gamemode
|
||||
*/
|
||||
void onGameModeChange(ServerPlayerEntity player, GameMode newGameMode);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface OnPlayerKicked {
|
||||
/**
|
||||
* Called when a player (or multiple) get kicked from the server
|
||||
*
|
||||
* @param source the source that initated the kick
|
||||
* @param targets the player(s) that got kicked
|
||||
* @param reason the provided kick reason
|
||||
*/
|
||||
void onKicked(ServerCommandSource source, Collection<ServerPlayerEntity> targets, Text reason);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface OnLogin {
|
||||
/**
|
||||
* Called when a player attempts to login
|
||||
*
|
||||
* @param address the address of the player
|
||||
* @param profile the profile of the player
|
||||
* @param reason the provided kick reason (null if player is permitted to join)
|
||||
*/
|
||||
void onLogin(SocketAddress address, GameProfile profile, Text reason);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface OnEnable {
|
||||
void onEnable(PlanFabric plugin);
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.playeranalytics.plan.gathering.listeners.events.mixin;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.playeranalytics.plan.gathering.listeners.events.PlanFabricEvents;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(Entity.class)
|
||||
public class EntityMixin {
|
||||
|
||||
@Inject(method = "onKilledOther", at = @At(value = "TAIL"))
|
||||
public void onDeath(ServerWorld world, LivingEntity other, CallbackInfo ci) {
|
||||
PlanFabricEvents.ON_KILLED.invoker().onKilled(other, (Entity) (Object) this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.playeranalytics.plan.gathering.listeners.events.mixin;
|
||||
|
||||
import net.minecraft.server.command.KickCommand;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.text.Text;
|
||||
import net.playeranalytics.plan.gathering.listeners.events.PlanFabricEvents;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@Mixin(KickCommand.class)
|
||||
public class KickCommandMixin {
|
||||
|
||||
@Inject(method = "execute", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/network/ServerPlayNetworkHandler;disconnect(Lnet/minecraft/text/Text;)V"))
|
||||
private static void onKickPlayer(ServerCommandSource source, Collection<ServerPlayerEntity> targets, Text reason, CallbackInfoReturnable<Integer> cir) {
|
||||
PlanFabricEvents.ON_KICKED.invoker().onKicked(source, targets, reason);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.playeranalytics.plan.gathering.listeners.events.mixin;
|
||||
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.playeranalytics.plan.gathering.listeners.events.PlanFabricEvents;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(PlayerEntity.class)
|
||||
public class PlayerEntityMixin {
|
||||
|
||||
@Inject(method = "onKilledOther", at = @At(value = "TAIL"))
|
||||
public void onDeath(ServerWorld world, LivingEntity other, CallbackInfo ci) {
|
||||
PlanFabricEvents.ON_KILLED.invoker().onKilled(other, (PlayerEntity) (Object) this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.playeranalytics.plan.gathering.listeners.events.mixin;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import net.minecraft.server.PlayerManager;
|
||||
import net.minecraft.text.Text;
|
||||
import net.playeranalytics.plan.gathering.listeners.events.PlanFabricEvents;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.net.SocketAddress;
|
||||
|
||||
@Mixin(PlayerManager.class)
|
||||
public class PlayerManagerMixin {
|
||||
|
||||
@Inject(method = "checkCanJoin", at = @At(value = "TAIL"))
|
||||
public void onLogin(SocketAddress address, GameProfile profile, CallbackInfoReturnable<Text> cir) {
|
||||
PlanFabricEvents.ON_LOGIN.invoker().onLogin(address, profile, cir.getReturnValue());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.playeranalytics.plan.gathering.listeners.events.mixin;
|
||||
|
||||
import com.djrapitops.plan.commands.use.*;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.text.LiteralText;
|
||||
import net.minecraft.text.Text;
|
||||
import net.playeranalytics.plan.commands.CommandManager;
|
||||
import net.playeranalytics.plan.commands.use.FabricMessageBuilder;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@Mixin(ServerCommandSource.class)
|
||||
public abstract class ServerCommandSourceMixin implements CMDSender {
|
||||
|
||||
@Override
|
||||
public boolean isPlayer() {
|
||||
return getPlayer().isPresent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsChatEvents() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Shadow
|
||||
public abstract void sendFeedback(Text message, boolean broadcastToOps);
|
||||
|
||||
@Shadow
|
||||
@Nullable
|
||||
public abstract Entity getEntity();
|
||||
|
||||
@Override
|
||||
public MessageBuilder buildMessage() {
|
||||
return new FabricMessageBuilder((ServerCommandSource) (Object) this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> getPlayerName() {
|
||||
return getPlayer().map(ServerPlayerEntity::getEntityName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return CommandManager.checkPermission((ServerCommandSource) (Object) this, permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<UUID> getUUID() {
|
||||
return Optional.ofNullable(isConsole() ? null : getEntity().getUuid());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(String message) {
|
||||
this.sendFeedback(new LiteralText(message), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatFormatter getFormatter() {
|
||||
return isConsole() ? new ConsoleChatFormatter() : new PlayerChatFormatter();
|
||||
}
|
||||
|
||||
private boolean isConsole() {
|
||||
return getEntity() == null;
|
||||
}
|
||||
|
||||
private Optional<ServerPlayerEntity> getPlayer() {
|
||||
if (getEntity() instanceof ServerPlayerEntity player) {
|
||||
return Optional.of(player);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.playeranalytics.plan.gathering.listeners.events.mixin;
|
||||
|
||||
import net.minecraft.network.packet.c2s.play.ChatMessageC2SPacket;
|
||||
import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket;
|
||||
import net.minecraft.server.network.ServerPlayNetworkHandler;
|
||||
import net.playeranalytics.plan.gathering.listeners.events.PlanFabricEvents;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(ServerPlayNetworkHandler.class)
|
||||
public class ServerPlayNetworkHandlerMixin {
|
||||
|
||||
@Inject(method = "onGameMessage", at = @At(value = "INVOKE", target = "Ljava/lang/String;startsWith(Ljava/lang/String;)Z"))
|
||||
public void onChatMessage(ChatMessageC2SPacket packet, CallbackInfo ci) {
|
||||
PlanFabricEvents.ON_CHAT.invoker().onChat((ServerPlayNetworkHandler) (Object) this, packet.getChatMessage());
|
||||
}
|
||||
|
||||
@Inject(method = "onPlayerMove", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/network/ServerPlayerEntity;getServerWorld()Lnet/minecraft/server/world/ServerWorld;"))
|
||||
public void onPlayerMove(PlayerMoveC2SPacket packet, CallbackInfo ci) {
|
||||
PlanFabricEvents.ON_MOVE.invoker().onMove((ServerPlayNetworkHandler) (Object) this, packet);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.playeranalytics.plan.gathering.listeners.events.mixin;
|
||||
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.world.GameMode;
|
||||
import net.playeranalytics.plan.gathering.listeners.events.PlanFabricEvents;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(ServerPlayerEntity.class)
|
||||
public class ServerPlayerEntityMixin {
|
||||
|
||||
@Inject(method = "changeGameMode", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/network/ServerPlayNetworkHandler;sendPacket(Lnet/minecraft/network/Packet;)V"))
|
||||
public void onGameModeChanged(GameMode gameMode, CallbackInfoReturnable<Boolean> cir) {
|
||||
PlanFabricEvents.ON_GAMEMODE_CHANGE.invoker().onGameModeChange((ServerPlayerEntity) (Object) this, gameMode);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.playeranalytics.plan.gathering.listeners.fabric;
|
||||
|
||||
import com.djrapitops.plan.delivery.domain.Nickname;
|
||||
import com.djrapitops.plan.gathering.cache.NicknameCache;
|
||||
import com.djrapitops.plan.identification.ServerInfo;
|
||||
import com.djrapitops.plan.storage.database.DBSystem;
|
||||
import com.djrapitops.plan.storage.database.transactions.events.NicknameStoreTransaction;
|
||||
import com.djrapitops.plan.utilities.logging.ErrorContext;
|
||||
import com.djrapitops.plan.utilities.logging.ErrorLogger;
|
||||
import net.minecraft.server.network.ServerPlayNetworkHandler;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.playeranalytics.plan.gathering.listeners.FabricListener;
|
||||
import net.playeranalytics.plan.gathering.listeners.events.PlanFabricEvents;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Event Listener for chat events.
|
||||
*
|
||||
* @author AuroraLS3
|
||||
*/
|
||||
public class ChatListener implements FabricListener {
|
||||
|
||||
private final ServerInfo serverInfo;
|
||||
private final DBSystem dbSystem;
|
||||
private final NicknameCache nicknameCache;
|
||||
private final ErrorLogger errorLogger;
|
||||
|
||||
private boolean isEnabled = false;
|
||||
|
||||
|
||||
@Inject
|
||||
public ChatListener(
|
||||
ServerInfo serverInfo,
|
||||
DBSystem dbSystem,
|
||||
NicknameCache nicknameCache,
|
||||
ErrorLogger errorLogger
|
||||
) {
|
||||
this.serverInfo = serverInfo;
|
||||
this.dbSystem = dbSystem;
|
||||
this.nicknameCache = nicknameCache;
|
||||
this.errorLogger = errorLogger;
|
||||
}
|
||||
|
||||
public void onChat(ServerPlayNetworkHandler handler, String message) {
|
||||
|
||||
try {
|
||||
actOnChatEvent(handler);
|
||||
} catch (Exception e) {
|
||||
errorLogger.error(e, ErrorContext.builder().related(handler, message).build());
|
||||
}
|
||||
}
|
||||
|
||||
private void actOnChatEvent(ServerPlayNetworkHandler handler) {
|
||||
long time = System.currentTimeMillis();
|
||||
ServerPlayerEntity player = handler.player;
|
||||
UUID uuid = player.getUuid();
|
||||
String displayName = player.getDisplayName().asString();
|
||||
|
||||
dbSystem.getDatabase().executeTransaction(new NicknameStoreTransaction(
|
||||
uuid, new Nickname(displayName, time, serverInfo.getServerUUID()),
|
||||
(playerUUID, name) -> nicknameCache.getDisplayName(playerUUID).map(name::equals).orElse(false)
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register() {
|
||||
PlanFabricEvents.ON_CHAT.register((handler, message) -> {
|
||||
if (!isEnabled) {
|
||||
return;
|
||||
}
|
||||
onChat(handler, message);
|
||||
|
||||
});
|
||||
|
||||
this.enable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return this.isEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enable() {
|
||||
this.isEnabled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
this.isEnabled = false;
|
||||
}
|
||||
}
|
||||
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.playeranalytics.plan.gathering.listeners.fabric;
|
||||
|
||||
import com.djrapitops.plan.delivery.formatting.EntityNameFormatter;
|
||||
import com.djrapitops.plan.gathering.cache.SessionCache;
|
||||
import com.djrapitops.plan.gathering.domain.ActiveSession;
|
||||
import com.djrapitops.plan.gathering.domain.PlayerKill;
|
||||
import com.djrapitops.plan.identification.ServerInfo;
|
||||
import com.djrapitops.plan.processing.Processing;
|
||||
import com.djrapitops.plan.processing.processors.player.MobKillProcessor;
|
||||
import com.djrapitops.plan.processing.processors.player.PlayerKillProcessor;
|
||||
import com.djrapitops.plan.utilities.logging.ErrorContext;
|
||||
import com.djrapitops.plan.utilities.logging.ErrorLogger;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.passive.TameableEntity;
|
||||
import net.minecraft.entity.projectile.ProjectileEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.playeranalytics.plan.gathering.listeners.FabricListener;
|
||||
import net.playeranalytics.plan.gathering.listeners.events.PlanFabricEvents;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Optional;
|
||||
|
||||
public class DeathEventListener implements FabricListener {
|
||||
|
||||
private final Processing processing;
|
||||
private final ErrorLogger errorLogger;
|
||||
private final ServerInfo serverInfo;
|
||||
|
||||
private boolean isEnabled = false;
|
||||
|
||||
@Inject
|
||||
public DeathEventListener(
|
||||
ServerInfo serverInfo,
|
||||
Processing processing,
|
||||
ErrorLogger errorLogger
|
||||
) {
|
||||
this.serverInfo = serverInfo;
|
||||
this.processing = processing;
|
||||
this.errorLogger = errorLogger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register() {
|
||||
PlanFabricEvents.ON_KILLED.register((victim, killer) -> {
|
||||
if (!this.isEnabled) {
|
||||
return;
|
||||
}
|
||||
long time = System.currentTimeMillis();
|
||||
if (victim instanceof ServerPlayerEntity) {
|
||||
// Process Death
|
||||
SessionCache.getCachedSession(victim.getUuid()).ifPresent(ActiveSession::addDeath);
|
||||
}
|
||||
|
||||
try {
|
||||
Optional<ServerPlayerEntity> foundKiller = getCause(killer);
|
||||
if (foundKiller.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ServerPlayerEntity player = foundKiller.get();
|
||||
|
||||
Runnable processor = victim instanceof ServerPlayerEntity
|
||||
? new PlayerKillProcessor(getKiller(player), getVictim((ServerPlayerEntity) victim), serverInfo.getServerIdentifier(), findWeapon(player), time)
|
||||
: new MobKillProcessor(player.getUuid());
|
||||
processing.submitCritical(processor);
|
||||
} catch (Exception e) {
|
||||
errorLogger.error(e, ErrorContext.builder().related(getClass(), victim, killer).build());
|
||||
}
|
||||
|
||||
});
|
||||
this.enable();
|
||||
}
|
||||
|
||||
private PlayerKill.Killer getKiller(ServerPlayerEntity killer) {
|
||||
return new PlayerKill.Killer(killer.getUuid(), killer.getName().asString());
|
||||
}
|
||||
|
||||
private PlayerKill.Victim getVictim(ServerPlayerEntity victim) {
|
||||
return new PlayerKill.Victim(victim.getUuid(), victim.getName().asString());
|
||||
}
|
||||
|
||||
public Optional<ServerPlayerEntity> getCause(Entity killer) {
|
||||
if (killer instanceof ServerPlayerEntity) return Optional.of((ServerPlayerEntity) killer);
|
||||
if (killer instanceof TameableEntity) return getOwner((TameableEntity) killer);
|
||||
if (killer instanceof ProjectileEntity) return getShooter((ProjectileEntity) killer);
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public String findWeapon(Entity killer) {
|
||||
if (killer instanceof ServerPlayerEntity) return getItemInHand((ServerPlayerEntity) killer);
|
||||
|
||||
// Projectile, EnderCrystal and all other causes that are not known yet
|
||||
return new EntityNameFormatter().apply(killer.getType().getName().asString());
|
||||
}
|
||||
|
||||
private String getItemInHand(ServerPlayerEntity killer) {
|
||||
ItemStack itemInHand = killer.getMainHandStack();
|
||||
return itemInHand.getItem().getName().asString();
|
||||
}
|
||||
|
||||
private Optional<ServerPlayerEntity> getShooter(ProjectileEntity projectile) {
|
||||
Entity source = projectile.getOwner();
|
||||
if (source instanceof ServerPlayerEntity) {
|
||||
return Optional.of((ServerPlayerEntity) source);
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private Optional<ServerPlayerEntity> getOwner(TameableEntity tameable) {
|
||||
if (!tameable.isTamed()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
Entity owner = tameable.getOwner();
|
||||
if (owner instanceof ServerPlayerEntity) {
|
||||
return Optional.of((ServerPlayerEntity) owner);
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return this.isEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enable() {
|
||||
this.isEnabled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
this.isEnabled = false;
|
||||
}
|
||||
}
|
@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.playeranalytics.plan.gathering.listeners.fabric;
|
||||
|
||||
import com.djrapitops.plan.gathering.afk.AFKTracker;
|
||||
import com.djrapitops.plan.settings.config.PlanConfig;
|
||||
import com.djrapitops.plan.utilities.logging.ErrorContext;
|
||||
import com.djrapitops.plan.utilities.logging.ErrorLogger;
|
||||
import me.lucko.fabric.api.permissions.v0.Permissions;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.playeranalytics.plan.commands.CommandManager;
|
||||
import net.playeranalytics.plan.gathering.listeners.FabricListener;
|
||||
import net.playeranalytics.plan.gathering.listeners.events.PlanFabricEvents;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class FabricAFKListener implements FabricListener {
|
||||
|
||||
// Static so that /reload does not cause afk tracking to fail.
|
||||
static AFKTracker afkTracker;
|
||||
private final Map<UUID, Boolean> ignorePermissionInfo;
|
||||
private final ErrorLogger errorLogger;
|
||||
private boolean isEnabled = false;
|
||||
|
||||
@Inject
|
||||
public FabricAFKListener(PlanConfig config, ErrorLogger errorLogger) {
|
||||
this.errorLogger = errorLogger;
|
||||
this.ignorePermissionInfo = new HashMap<>();
|
||||
|
||||
FabricAFKListener.assignAFKTracker(config);
|
||||
}
|
||||
|
||||
private static void assignAFKTracker(PlanConfig config) {
|
||||
if (afkTracker == null) {
|
||||
afkTracker = new AFKTracker(config);
|
||||
}
|
||||
}
|
||||
|
||||
private void event(ServerPlayerEntity player) {
|
||||
try {
|
||||
UUID uuid = player.getUuid();
|
||||
long time = System.currentTimeMillis();
|
||||
|
||||
boolean ignored = ignorePermissionInfo.computeIfAbsent(uuid, keyUUID -> checkPermission(player, com.djrapitops.plan.settings.Permissions.IGNORE_AFK.getPermission()));
|
||||
if (ignored) {
|
||||
afkTracker.hasIgnorePermission(uuid);
|
||||
ignorePermissionInfo.put(uuid, true);
|
||||
return;
|
||||
} else {
|
||||
ignorePermissionInfo.put(uuid, false);
|
||||
}
|
||||
|
||||
afkTracker.performedAction(uuid, time);
|
||||
} catch (Exception e) {
|
||||
errorLogger.error(e, ErrorContext.builder().related(getClass(), player).build());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkPermission(ServerPlayerEntity player, String permission) {
|
||||
if (CommandManager.isPermissionsApiAvailable()) {
|
||||
return Permissions.check(player, permission);
|
||||
} else return player.hasPermissionLevel(2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register() {
|
||||
this.enable();
|
||||
PlanFabricEvents.ON_CHAT.register((handler, message) -> {
|
||||
if (!isEnabled) {
|
||||
return;
|
||||
}
|
||||
event(handler.player);
|
||||
boolean isAfkCommand = message.substring(1).toLowerCase().startsWith("afk");
|
||||
if (isAfkCommand) {
|
||||
UUID uuid = handler.player.getUuid();
|
||||
afkTracker.usedAfkCommand(uuid, System.currentTimeMillis());
|
||||
}
|
||||
});
|
||||
ServerPlayConnectionEvents.DISCONNECT.register((handler, server) -> {
|
||||
if (!this.isEnabled) {
|
||||
return;
|
||||
}
|
||||
ignorePermissionInfo.remove(handler.player.getUuid());
|
||||
});
|
||||
PlanFabricEvents.ON_MOVE.register((handler, packet) -> event(handler.player));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return this.isEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enable() {
|
||||
this.isEnabled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
this.isEnabled = false;
|
||||
}
|
||||
}
|
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.playeranalytics.plan.gathering.listeners.fabric;
|
||||
|
||||
import com.djrapitops.plan.gathering.cache.SessionCache;
|
||||
import com.djrapitops.plan.gathering.domain.ActiveSession;
|
||||
import com.djrapitops.plan.identification.ServerInfo;
|
||||
import com.djrapitops.plan.settings.config.WorldAliasSettings;
|
||||
import com.djrapitops.plan.storage.database.DBSystem;
|
||||
import com.djrapitops.plan.storage.database.transactions.events.WorldNameStoreTransaction;
|
||||
import com.djrapitops.plan.utilities.logging.ErrorContext;
|
||||
import com.djrapitops.plan.utilities.logging.ErrorLogger;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.world.GameMode;
|
||||
import net.playeranalytics.plan.gathering.listeners.FabricListener;
|
||||
import net.playeranalytics.plan.gathering.listeners.events.PlanFabricEvents;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Event Listener for PlayerGameModeChangeEvents.
|
||||
*
|
||||
* @author AuroraLS3
|
||||
*/
|
||||
public class GameModeChangeListener implements FabricListener {
|
||||
|
||||
private final WorldAliasSettings worldAliasSettings;
|
||||
private final ServerInfo serverInfo;
|
||||
private final DBSystem dbSystem;
|
||||
private final ErrorLogger errorLogger;
|
||||
|
||||
private boolean isEnabled = false;
|
||||
|
||||
@Inject
|
||||
public GameModeChangeListener(
|
||||
WorldAliasSettings worldAliasSettings,
|
||||
ServerInfo serverInfo,
|
||||
DBSystem dbSystem,
|
||||
ErrorLogger errorLogger
|
||||
) {
|
||||
this.worldAliasSettings = worldAliasSettings;
|
||||
this.serverInfo = serverInfo;
|
||||
this.dbSystem = dbSystem;
|
||||
this.errorLogger = errorLogger;
|
||||
}
|
||||
|
||||
public void onGameModeChange(ServerPlayerEntity player, GameMode newGameMode) {
|
||||
try {
|
||||
actOnEvent(player, newGameMode);
|
||||
} catch (Exception e) {
|
||||
errorLogger.error(e, ErrorContext.builder().related(getClass(), player, newGameMode).build());
|
||||
}
|
||||
}
|
||||
|
||||
private void actOnEvent(ServerPlayerEntity player, GameMode newGameMode) {
|
||||
UUID uuid = player.getUuid();
|
||||
long time = System.currentTimeMillis();
|
||||
String gameMode = newGameMode.name();
|
||||
String worldName = player.getServerWorld().getRegistryKey().getValue().toString();
|
||||
|
||||
dbSystem.getDatabase().executeTransaction(new WorldNameStoreTransaction(serverInfo.getServerUUID(), worldName));
|
||||
worldAliasSettings.addWorld(worldName);
|
||||
|
||||
Optional<ActiveSession> cachedSession = SessionCache.getCachedSession(uuid);
|
||||
cachedSession.ifPresent(session -> session.changeState(worldName, gameMode, time));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register() {
|
||||
if (!this.isEnabled) {
|
||||
return;
|
||||
}
|
||||
PlanFabricEvents.ON_GAMEMODE_CHANGE.register(this::onGameModeChange);
|
||||
this.enable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return this.isEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enable() {
|
||||
this.isEnabled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
this.isEnabled = false;
|
||||
}
|
||||
}
|
@ -0,0 +1,276 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.playeranalytics.plan.gathering.listeners.fabric;
|
||||
|
||||
import com.djrapitops.plan.delivery.domain.Nickname;
|
||||
import com.djrapitops.plan.delivery.domain.PlayerName;
|
||||
import com.djrapitops.plan.delivery.domain.ServerName;
|
||||
import com.djrapitops.plan.delivery.export.Exporter;
|
||||
import com.djrapitops.plan.extension.CallEvents;
|
||||
import com.djrapitops.plan.extension.ExtensionSvc;
|
||||
import com.djrapitops.plan.gathering.cache.NicknameCache;
|
||||
import com.djrapitops.plan.gathering.cache.SessionCache;
|
||||
import com.djrapitops.plan.gathering.domain.ActiveSession;
|
||||
import com.djrapitops.plan.gathering.geolocation.GeolocationCache;
|
||||
import com.djrapitops.plan.identification.ServerInfo;
|
||||
import com.djrapitops.plan.identification.ServerUUID;
|
||||
import com.djrapitops.plan.processing.Processing;
|
||||
import com.djrapitops.plan.settings.config.PlanConfig;
|
||||
import com.djrapitops.plan.settings.config.paths.DataGatheringSettings;
|
||||
import com.djrapitops.plan.settings.config.paths.ExportSettings;
|
||||
import com.djrapitops.plan.storage.database.DBSystem;
|
||||
import com.djrapitops.plan.storage.database.Database;
|
||||
import com.djrapitops.plan.storage.database.transactions.events.*;
|
||||
import com.djrapitops.plan.utilities.logging.ErrorContext;
|
||||
import com.djrapitops.plan.utilities.logging.ErrorLogger;
|
||||
import com.google.common.net.InetAddresses;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
|
||||
import net.minecraft.server.dedicated.MinecraftDedicatedServer;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.playeranalytics.plan.gathering.listeners.FabricListener;
|
||||
import net.playeranalytics.plan.gathering.listeners.events.PlanFabricEvents;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class PlayerOnlineListener implements FabricListener {
|
||||
|
||||
private final PlanConfig config;
|
||||
private final Processing processing;
|
||||
private final ServerInfo serverInfo;
|
||||
private final DBSystem dbSystem;
|
||||
private final ExtensionSvc extensionService;
|
||||
private final Exporter exporter;
|
||||
private final GeolocationCache geolocationCache;
|
||||
private final NicknameCache nicknameCache;
|
||||
private final SessionCache sessionCache;
|
||||
private final ErrorLogger errorLogger;
|
||||
private final MinecraftDedicatedServer server;
|
||||
|
||||
private final Map<UUID, String> joinAddresses;
|
||||
|
||||
private boolean isEnabled = false;
|
||||
|
||||
@Inject
|
||||
public PlayerOnlineListener(
|
||||
PlanConfig config,
|
||||
Processing processing,
|
||||
ServerInfo serverInfo,
|
||||
DBSystem dbSystem,
|
||||
ExtensionSvc extensionService,
|
||||
Exporter exporter,
|
||||
GeolocationCache geolocationCache,
|
||||
NicknameCache nicknameCache,
|
||||
SessionCache sessionCache,
|
||||
ErrorLogger errorLogger,
|
||||
MinecraftDedicatedServer server
|
||||
) {
|
||||
this.config = config;
|
||||
this.processing = processing;
|
||||
this.serverInfo = serverInfo;
|
||||
this.dbSystem = dbSystem;
|
||||
this.extensionService = extensionService;
|
||||
this.exporter = exporter;
|
||||
this.geolocationCache = geolocationCache;
|
||||
this.nicknameCache = nicknameCache;
|
||||
this.sessionCache = sessionCache;
|
||||
this.errorLogger = errorLogger;
|
||||
this.server = server;
|
||||
|
||||
joinAddresses = new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register() {
|
||||
ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> {
|
||||
if (!this.isEnabled) {
|
||||
return;
|
||||
}
|
||||
onPlayerJoin(handler.player);
|
||||
});
|
||||
ServerPlayConnectionEvents.DISCONNECT.register((handler, server) -> {
|
||||
if (!this.isEnabled) {
|
||||
return;
|
||||
}
|
||||
onPlayerQuit(handler.player);
|
||||
});
|
||||
PlanFabricEvents.ON_KICKED.register((source, targets, reason) -> {
|
||||
if (!this.isEnabled) {
|
||||
return;
|
||||
}
|
||||
for (ServerPlayerEntity target : targets) {
|
||||
onPlayerKick(target);
|
||||
}
|
||||
});
|
||||
PlanFabricEvents.ON_LOGIN.register((address, profile, reason) -> {
|
||||
if (!this.isEnabled) {
|
||||
return;
|
||||
}
|
||||
onPlayerLogin(address, profile, reason != null);
|
||||
});
|
||||
this.enable();
|
||||
}
|
||||
|
||||
public void onPlayerLogin(SocketAddress address, GameProfile profile, boolean banned) {
|
||||
try {
|
||||
UUID playerUUID = profile.getId();
|
||||
ServerUUID serverUUID = serverInfo.getServerUUID();
|
||||
String joinAddress = address.toString();
|
||||
if (!joinAddress.isEmpty()) {
|
||||
joinAddresses.put(playerUUID, joinAddress.substring(0, joinAddress.lastIndexOf(':')));
|
||||
}
|
||||
dbSystem.getDatabase().executeTransaction(new BanStatusTransaction(playerUUID, serverUUID, () -> banned));
|
||||
} catch (Exception e) {
|
||||
errorLogger.error(e, ErrorContext.builder().related(getClass(), address, profile, banned).build());
|
||||
}
|
||||
}
|
||||
|
||||
public void onPlayerKick(ServerPlayerEntity player) {
|
||||
try {
|
||||
UUID uuid = player.getUuid();
|
||||
if (FabricAFKListener.afkTracker.isAfk(uuid)) {
|
||||
return;
|
||||
}
|
||||
|
||||
dbSystem.getDatabase().executeTransaction(new KickStoreTransaction(uuid));
|
||||
} catch (Exception e) {
|
||||
errorLogger.error(e, ErrorContext.builder().related(getClass(), player).build());
|
||||
}
|
||||
}
|
||||
|
||||
public void onPlayerJoin(ServerPlayerEntity player) {
|
||||
try {
|
||||
actOnJoinEvent(player);
|
||||
} catch (Exception e) {
|
||||
errorLogger.error(e, ErrorContext.builder().related(getClass(), player).build());
|
||||
}
|
||||
}
|
||||
|
||||
private void actOnJoinEvent(ServerPlayerEntity player) {
|
||||
|
||||
UUID playerUUID = player.getUuid();
|
||||
ServerUUID serverUUID = serverInfo.getServerUUID();
|
||||
long time = System.currentTimeMillis();
|
||||
|
||||
FabricAFKListener.afkTracker.performedAction(playerUUID, time);
|
||||
|
||||
String world = player.getServerWorld().getRegistryKey().getValue().toString();
|
||||
String gm = player.interactionManager.getGameMode().name();
|
||||
|
||||
Database database = dbSystem.getDatabase();
|
||||
database.executeTransaction(new WorldNameStoreTransaction(serverUUID, world));
|
||||
|
||||
InetSocketAddress socketAddress = (InetSocketAddress) player.networkHandler.connection.getAddress();
|
||||
InetAddress address = InetAddresses.forString(socketAddress.getAddress().toString().replace("/", ""));
|
||||
Supplier<String> getHostName = () -> getHostname(player);
|
||||
|
||||
String playerName = player.getEntityName();
|
||||
String displayName = player.getDisplayName().asString();
|
||||
|
||||
boolean gatheringGeolocations = config.isTrue(DataGatheringSettings.GEOLOCATIONS);
|
||||
if (gatheringGeolocations) {
|
||||
database.executeTransaction(
|
||||
new GeoInfoStoreTransaction(playerUUID, address, time, geolocationCache::getCountry)
|
||||
);
|
||||
}
|
||||
|
||||
database.executeTransaction(new PlayerServerRegisterTransaction(playerUUID,
|
||||
System::currentTimeMillis, playerName, serverUUID, getHostName));
|
||||
database.executeTransaction(new OperatorStatusTransaction(playerUUID, serverUUID, server.getPlayerManager().getOpList().isOp(player.getGameProfile())));
|
||||
|
||||
ActiveSession session = new ActiveSession(playerUUID, serverUUID, time, world, gm);
|
||||
session.getExtraData().put(PlayerName.class, new PlayerName(playerName));
|
||||
session.getExtraData().put(ServerName.class, new ServerName(serverInfo.getServer().getIdentifiableName()));
|
||||
sessionCache.cacheSession(playerUUID, session)
|
||||
.ifPresent(previousSession -> database.executeTransaction(new SessionEndTransaction(previousSession)));
|
||||
|
||||
database.executeTransaction(new NicknameStoreTransaction(
|
||||
playerUUID, new Nickname(displayName, time, serverUUID),
|
||||
(uuid, name) -> nicknameCache.getDisplayName(playerUUID).map(name::equals).orElse(false)
|
||||
));
|
||||
|
||||
processing.submitNonCritical(() -> extensionService.updatePlayerValues(playerUUID, playerName, CallEvents.PLAYER_JOIN));
|
||||
if (config.isTrue(ExportSettings.EXPORT_ON_ONLINE_STATUS_CHANGE)) {
|
||||
processing.submitNonCritical(() -> exporter.exportPlayerPage(playerUUID, playerName));
|
||||
}
|
||||
}
|
||||
|
||||
private String getHostname(ServerPlayerEntity player) {
|
||||
return joinAddresses.get(player.getUuid());
|
||||
}
|
||||
|
||||
// No event priorities on Fabric, so this has to be called with onPlayerQuit()
|
||||
public void beforePlayerQuit(ServerPlayerEntity player) {
|
||||
UUID playerUUID = player.getUuid();
|
||||
String playerName = player.getEntityName();
|
||||
processing.submitNonCritical(() -> extensionService.updatePlayerValues(playerUUID, playerName, CallEvents.PLAYER_LEAVE));
|
||||
}
|
||||
|
||||
public void onPlayerQuit(ServerPlayerEntity player) {
|
||||
beforePlayerQuit(player);
|
||||
try {
|
||||
actOnQuitEvent(player);
|
||||
} catch (Exception e) {
|
||||
errorLogger.error(e, ErrorContext.builder().related(getClass(), player).build());
|
||||
}
|
||||
}
|
||||
|
||||
private void actOnQuitEvent(ServerPlayerEntity player) {
|
||||
long time = System.currentTimeMillis();
|
||||
String playerName = player.getEntityName();
|
||||
UUID playerUUID = player.getUuid();
|
||||
ServerUUID serverUUID = serverInfo.getServerUUID();
|
||||
|
||||
FabricAFKListener.afkTracker.loggedOut(playerUUID, time);
|
||||
|
||||
joinAddresses.remove(playerUUID);
|
||||
nicknameCache.removeDisplayName(playerUUID);
|
||||
|
||||
dbSystem.getDatabase().executeTransaction(new BanStatusTransaction(playerUUID, serverUUID, () -> server.getPlayerManager().getUserBanList().contains(player.getGameProfile())));
|
||||
|
||||
sessionCache.endSession(playerUUID, time)
|
||||
.ifPresent(endedSession -> dbSystem.getDatabase().executeTransaction(new SessionEndTransaction(endedSession)));
|
||||
|
||||
if (config.isTrue(ExportSettings.EXPORT_ON_ONLINE_STATUS_CHANGE)) {
|
||||
processing.submitNonCritical(() -> exporter.exportPlayerPage(playerUUID, playerName));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return this.isEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enable() {
|
||||
this.isEnabled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
this.isEnabled = false;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.playeranalytics.plan.gathering.listeners.fabric;
|
||||
|
||||
import com.djrapitops.plan.gathering.cache.SessionCache;
|
||||
import com.djrapitops.plan.gathering.domain.ActiveSession;
|
||||
import com.djrapitops.plan.identification.ServerInfo;
|
||||
import com.djrapitops.plan.settings.config.WorldAliasSettings;
|
||||
import com.djrapitops.plan.storage.database.DBSystem;
|
||||
import com.djrapitops.plan.storage.database.transactions.events.WorldNameStoreTransaction;
|
||||
import com.djrapitops.plan.utilities.logging.ErrorContext;
|
||||
import com.djrapitops.plan.utilities.logging.ErrorLogger;
|
||||
import net.fabricmc.fabric.api.entity.event.v1.ServerEntityWorldChangeEvents;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.playeranalytics.plan.gathering.listeners.FabricListener;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public class WorldChangeListener implements FabricListener {
|
||||
|
||||
private final WorldAliasSettings worldAliasSettings;
|
||||
private final ServerInfo serverInfo;
|
||||
private final DBSystem dbSystem;
|
||||
private final ErrorLogger errorLogger;
|
||||
|
||||
private boolean isEnabled = false;
|
||||
|
||||
@Inject
|
||||
public WorldChangeListener(
|
||||
WorldAliasSettings worldAliasSettings,
|
||||
ServerInfo serverInfo,
|
||||
DBSystem dbSystem,
|
||||
ErrorLogger errorLogger
|
||||
) {
|
||||
this.worldAliasSettings = worldAliasSettings;
|
||||
this.serverInfo = serverInfo;
|
||||
this.dbSystem = dbSystem;
|
||||
this.errorLogger = errorLogger;
|
||||
}
|
||||
|
||||
public void onWorldChange(ServerPlayerEntity player) {
|
||||
try {
|
||||
actOnEvent(player);
|
||||
} catch (Exception e) {
|
||||
errorLogger.error(e, ErrorContext.builder().related(getClass(), player).build());
|
||||
}
|
||||
}
|
||||
|
||||
private void actOnEvent(ServerPlayerEntity player) {
|
||||
long time = System.currentTimeMillis();
|
||||
|
||||
UUID uuid = player.getUuid();
|
||||
|
||||
String worldName = player.getServerWorld().getRegistryKey().getValue().toString();
|
||||
String gameMode = player.interactionManager.getGameMode().name();
|
||||
|
||||
dbSystem.getDatabase().executeTransaction(new WorldNameStoreTransaction(serverInfo.getServerUUID(), worldName));
|
||||
worldAliasSettings.addWorld(worldName);
|
||||
|
||||
Optional<ActiveSession> cachedSession = SessionCache.getCachedSession(uuid);
|
||||
cachedSession.ifPresent(session -> session.changeState(worldName, gameMode, time));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register() {
|
||||
this.enable();
|
||||
if (!isEnabled) {
|
||||
return;
|
||||
}
|
||||
ServerEntityWorldChangeEvents.AFTER_PLAYER_CHANGE_WORLD.register((player, origin, destination) -> onWorldChange(player));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return this.isEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enable() {
|
||||
this.isEnabled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
this.isEnabled = false;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,188 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2016-2018
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package net.playeranalytics.plan.gathering.timed;
|
||||
|
||||
import com.djrapitops.plan.TaskSystem;
|
||||
import com.djrapitops.plan.delivery.domain.DateObj;
|
||||
import com.djrapitops.plan.identification.ServerInfo;
|
||||
import com.djrapitops.plan.settings.config.PlanConfig;
|
||||
import com.djrapitops.plan.settings.config.paths.DataGatheringSettings;
|
||||
import com.djrapitops.plan.settings.config.paths.TimeSettings;
|
||||
import com.djrapitops.plan.storage.database.DBSystem;
|
||||
import com.djrapitops.plan.storage.database.transactions.events.PingStoreTransaction;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
|
||||
import net.minecraft.server.dedicated.MinecraftDedicatedServer;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.playeranalytics.plan.gathering.listeners.FabricListener;
|
||||
import net.playeranalytics.plugin.scheduling.RunnableFactory;
|
||||
import net.playeranalytics.plugin.scheduling.TimeAmount;
|
||||
import net.playeranalytics.plugin.server.Listeners;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Task that handles player ping calculation on Fabric based servers.
|
||||
*
|
||||
* @author BrainStone
|
||||
* @author DrexHD
|
||||
*/
|
||||
public class FabricPingCounter extends TaskSystem.Task implements FabricListener {
|
||||
|
||||
private final Map<UUID, List<DateObj<Integer>>> playerHistory;
|
||||
|
||||
private final Listeners listeners;
|
||||
private final PlanConfig config;
|
||||
private final DBSystem dbSystem;
|
||||
private final ServerInfo serverInfo;
|
||||
private final RunnableFactory runnableFactory;
|
||||
private final MinecraftDedicatedServer server;
|
||||
|
||||
private boolean isEnabled = false;
|
||||
|
||||
@Inject
|
||||
public FabricPingCounter(
|
||||
Listeners listeners,
|
||||
PlanConfig config,
|
||||
DBSystem dbSystem,
|
||||
ServerInfo serverInfo,
|
||||
RunnableFactory runnableFactory,
|
||||
MinecraftDedicatedServer server
|
||||
) {
|
||||
this.listeners = listeners;
|
||||
this.config = config;
|
||||
this.dbSystem = dbSystem;
|
||||
this.serverInfo = serverInfo;
|
||||
this.runnableFactory = runnableFactory;
|
||||
this.server = server;
|
||||
playerHistory = new HashMap<>();
|
||||
ServerPlayConnectionEvents.JOIN.register((handler, sender, minecraftServer) -> onPlayerJoin(handler.player));
|
||||
ServerPlayConnectionEvents.DISCONNECT.register((handler, minecraftServer) -> onPlayerQuit(handler.player));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (!this.isEnabled) {
|
||||
return;
|
||||
}
|
||||
long time = System.currentTimeMillis();
|
||||
Iterator<Map.Entry<UUID, List<DateObj<Integer>>>> iterator = playerHistory.entrySet().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Map.Entry<UUID, List<DateObj<Integer>>> entry = iterator.next();
|
||||
UUID uuid = entry.getKey();
|
||||
List<DateObj<Integer>> history = entry.getValue();
|
||||
ServerPlayerEntity player = server.getPlayerManager().getPlayer(uuid);
|
||||
if (player != null) {
|
||||
int ping = getPing(player);
|
||||
if (ping <= -1 || ping > TimeUnit.SECONDS.toMillis(8L)) {
|
||||
// Don't accept bad values
|
||||
continue;
|
||||
}
|
||||
history.add(new DateObj<>(time, ping));
|
||||
if (history.size() >= 30) {
|
||||
dbSystem.getDatabase().executeTransaction(
|
||||
new PingStoreTransaction(uuid, serverInfo.getServerUUID(), new ArrayList<>(history))
|
||||
);
|
||||
history.clear();
|
||||
}
|
||||
} else {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(RunnableFactory runnableFactory) {
|
||||
Long startDelay = config.get(TimeSettings.PING_SERVER_ENABLE_DELAY);
|
||||
if (startDelay < TimeUnit.HOURS.toMillis(1L) && config.isTrue(DataGatheringSettings.PING)) {
|
||||
listeners.registerListener(this);
|
||||
long delay = TimeAmount.toTicks(startDelay, TimeUnit.MILLISECONDS);
|
||||
long period = 40L;
|
||||
runnableFactory.create(this).runTaskTimer(delay, period);
|
||||
}
|
||||
this.enable();
|
||||
}
|
||||
|
||||
public void addPlayer(ServerPlayerEntity player) {
|
||||
playerHistory.put(player.getUuid(), new ArrayList<>());
|
||||
}
|
||||
|
||||
public void removePlayer(ServerPlayerEntity player) {
|
||||
playerHistory.remove(player.getUuid());
|
||||
}
|
||||
|
||||
private int getPing(ServerPlayerEntity player) {
|
||||
return player.pingMilliseconds;
|
||||
}
|
||||
|
||||
public void onPlayerJoin(ServerPlayerEntity player) {
|
||||
if (!this.isEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
Long pingDelay = config.get(TimeSettings.PING_PLAYER_LOGIN_DELAY);
|
||||
if (pingDelay >= TimeUnit.HOURS.toMillis(2L)) {
|
||||
return;
|
||||
}
|
||||
runnableFactory.create(() -> {
|
||||
if (server.getPlayerManager().getPlayerList().contains(player)) {
|
||||
addPlayer(player);
|
||||
}
|
||||
}).runTaskLater(TimeAmount.toTicks(pingDelay, TimeUnit.MILLISECONDS));
|
||||
}
|
||||
|
||||
public void onPlayerQuit(ServerPlayerEntity player) {
|
||||
if (!this.isEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
removePlayer(player);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
playerHistory.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register() {
|
||||
this.enable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return this.isEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enable() {
|
||||
this.isEnabled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
this.isEnabled = false;
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.playeranalytics.plan.identification.properties;
|
||||
|
||||
import com.djrapitops.plan.identification.properties.ServerProperties;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.minecraft.server.dedicated.MinecraftDedicatedServer;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* server.properties fetcher for Fabric
|
||||
*
|
||||
* @author Kopo942
|
||||
*/
|
||||
public class FabricServerProperties extends ServerProperties {
|
||||
|
||||
@Inject
|
||||
public FabricServerProperties(MinecraftDedicatedServer server) {
|
||||
super(
|
||||
"Fabric",
|
||||
server.getServerPort(),
|
||||
server.getVersion(),
|
||||
FabricLoader.getInstance().getModContainer("fabric").get().getMetadata().getVersion().getFriendlyString() +
|
||||
" (API), " +
|
||||
FabricLoader.getInstance().getModContainer("fabricloader").get().getMetadata().getVersion().getFriendlyString() +
|
||||
" (loader)",
|
||||
() -> (server.getServerIp() == null) ? "" : server.getServerIp(),
|
||||
server.getProperties().maxPlayers
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.playeranalytics.plan.modules.fabric;
|
||||
|
||||
import com.djrapitops.plan.identification.properties.ServerProperties;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import net.playeranalytics.plan.identification.properties.FabricServerProperties;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
/**
|
||||
* Dagger module for providing ServerProperties on Fabric.
|
||||
*
|
||||
* @author Kopo942
|
||||
*/
|
||||
@Module
|
||||
public class FabricServerPropertiesModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
ServerProperties provideServerProperties(FabricServerProperties serverProperties) {
|
||||
return serverProperties;
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.playeranalytics.plan.modules.fabric;
|
||||
|
||||
import com.djrapitops.plan.gathering.ServerSensor;
|
||||
import com.djrapitops.plan.gathering.ServerShutdownSave;
|
||||
import com.djrapitops.plan.gathering.listeners.ListenerSystem;
|
||||
import com.djrapitops.plan.identification.ServerInfo;
|
||||
import com.djrapitops.plan.identification.ServerServerInfo;
|
||||
import com.djrapitops.plan.settings.ConfigSystem;
|
||||
import com.djrapitops.plan.settings.FabricConfigSystem;
|
||||
import com.djrapitops.plan.storage.database.DBSystem;
|
||||
import dagger.Binds;
|
||||
import dagger.Module;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.playeranalytics.plan.FabricServerShutdownSave;
|
||||
import net.playeranalytics.plan.gathering.FabricSensor;
|
||||
import net.playeranalytics.plan.gathering.listeners.FabricListenerSystem;
|
||||
import net.playeranalytics.plan.storage.database.FabricDBSystem;
|
||||
|
||||
/**
|
||||
* Module for binding Fabric-specific classes as interface implementations.
|
||||
*
|
||||
* @author Kopo942
|
||||
*/
|
||||
@Module
|
||||
public interface FabricSuperClassBindingModule {
|
||||
|
||||
@Binds
|
||||
ServerInfo bindServerInfo(ServerServerInfo serverInfo);
|
||||
|
||||
@Binds
|
||||
DBSystem bindDBSystem(FabricDBSystem dbSystem);
|
||||
|
||||
@Binds
|
||||
ConfigSystem bindConfigSystem(FabricConfigSystem configSystem);
|
||||
|
||||
@Binds
|
||||
ListenerSystem bindListenerSystem(FabricListenerSystem listenerSystem);
|
||||
|
||||
@Binds
|
||||
ServerShutdownSave bindServerShutdownSave(FabricServerShutdownSave shutdownSave);
|
||||
|
||||
@Binds
|
||||
ServerSensor<ServerWorld> bindServerSensor(FabricSensor sensor);
|
||||
|
||||
@Binds
|
||||
ServerSensor<?> bindGenericsServerSensor(ServerSensor<ServerWorld> sensor);
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.playeranalytics.plan.modules.fabric;
|
||||
|
||||
import com.djrapitops.plan.TaskSystem;
|
||||
import com.djrapitops.plan.delivery.web.ResourceWriteTask;
|
||||
import com.djrapitops.plan.delivery.webserver.cache.JSONFileStorage;
|
||||
import com.djrapitops.plan.extension.ExtensionServerDataUpdater;
|
||||
import com.djrapitops.plan.gathering.ShutdownDataPreservation;
|
||||
import com.djrapitops.plan.gathering.ShutdownHook;
|
||||
import com.djrapitops.plan.gathering.timed.ServerTPSCounter;
|
||||
import com.djrapitops.plan.gathering.timed.SystemUsageBuffer;
|
||||
import com.djrapitops.plan.settings.upkeep.ConfigStoreTask;
|
||||
import com.djrapitops.plan.storage.upkeep.DBCleanTask;
|
||||
import com.djrapitops.plan.storage.upkeep.LogsFolderCleanTask;
|
||||
import com.djrapitops.plan.storage.upkeep.OldDependencyCacheDeletionTask;
|
||||
import dagger.Binds;
|
||||
import dagger.Module;
|
||||
import dagger.multibindings.IntoSet;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.playeranalytics.plan.gathering.timed.FabricPingCounter;
|
||||
|
||||
@Module
|
||||
public interface FabricTaskModule {
|
||||
|
||||
@Binds
|
||||
@IntoSet
|
||||
TaskSystem.Task bindTPSCounter(ServerTPSCounter<ServerWorld> tpsCounter);
|
||||
|
||||
@Binds
|
||||
@IntoSet
|
||||
TaskSystem.Task bindPingCounter(FabricPingCounter pingCounter);
|
||||
|
||||
@Binds
|
||||
@IntoSet
|
||||
TaskSystem.Task bindExtensionServerDataUpdater(ExtensionServerDataUpdater extensionServerDataUpdater);
|
||||
|
||||
@Binds
|
||||
@IntoSet
|
||||
TaskSystem.Task bindLogCleanTask(LogsFolderCleanTask logsFolderCleanTask);
|
||||
|
||||
@Binds
|
||||
@IntoSet
|
||||
TaskSystem.Task bindConfigStoreTask(ConfigStoreTask configStoreTask);
|
||||
|
||||
@Binds
|
||||
@IntoSet
|
||||
TaskSystem.Task bindDBCleanTask(DBCleanTask cleanTask);
|
||||
|
||||
@Binds
|
||||
@IntoSet
|
||||
TaskSystem.Task bindRamAndCpuTask(SystemUsageBuffer.RamAndCpuTask ramAndCpuTask);
|
||||
|
||||
@Binds
|
||||
@IntoSet
|
||||
TaskSystem.Task bindDiskTask(SystemUsageBuffer.DiskTask diskTask);
|
||||
|
||||
@Binds
|
||||
@IntoSet
|
||||
TaskSystem.Task bindShutdownHookRegistration(ShutdownHook.Registrar registrar);
|
||||
|
||||
@Binds
|
||||
@IntoSet
|
||||
TaskSystem.Task bindJSONFileStorageCleanTask(JSONFileStorage.CleanTask cleanTask);
|
||||
|
||||
@Binds
|
||||
@IntoSet
|
||||
TaskSystem.Task bindShutdownDataPreservation(ShutdownDataPreservation dataPreservation);
|
||||
|
||||
@Binds
|
||||
@IntoSet
|
||||
TaskSystem.Task bindOldDependencyCacheDeletion(OldDependencyCacheDeletionTask deletionTask);
|
||||
|
||||
@Binds
|
||||
@IntoSet
|
||||
TaskSystem.Task bindResourceWriteTask(ResourceWriteTask resourceWriteTask);
|
||||
}
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.playeranalytics.plan.storage.database;
|
||||
|
||||
import com.djrapitops.plan.settings.config.PlanConfig;
|
||||
import com.djrapitops.plan.settings.config.paths.DatabaseSettings;
|
||||
import com.djrapitops.plan.settings.locale.Locale;
|
||||
import com.djrapitops.plan.storage.database.DBSystem;
|
||||
import com.djrapitops.plan.storage.database.MySQLDB;
|
||||
import com.djrapitops.plan.storage.database.SQLiteDB;
|
||||
import net.playeranalytics.plugin.server.PluginLogger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
|
||||
/**
|
||||
* Fabric database system that initializes the required SQLite and MySQL database objects.
|
||||
*
|
||||
* @author Kopo942
|
||||
*/
|
||||
@Singleton
|
||||
public class FabricDBSystem extends DBSystem {
|
||||
|
||||
private final PlanConfig config;
|
||||
|
||||
@Inject
|
||||
public FabricDBSystem(
|
||||
Locale locale,
|
||||
MySQLDB mySQLDB,
|
||||
SQLiteDB.Factory sqLiteDB,
|
||||
PlanConfig config,
|
||||
PluginLogger logger
|
||||
) {
|
||||
super(locale, sqLiteDB, logger);
|
||||
this.config = config;
|
||||
|
||||
databases.add(mySQLDB);
|
||||
databases.add(sqLiteDB.usingDefaultFile());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enable() {
|
||||
String dbType = config.get(DatabaseSettings.TYPE).toLowerCase().trim();
|
||||
db = getActiveDatabaseByName(dbType);
|
||||
super.enable();
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.playeranalytics.plugin;
|
||||
|
||||
import net.fabricmc.api.DedicatedServerModInitializer;
|
||||
import net.playeranalytics.plugin.scheduling.FabricRunnableFactory;
|
||||
import net.playeranalytics.plugin.scheduling.RunnableFactory;
|
||||
import net.playeranalytics.plugin.server.FabricListeners;
|
||||
import net.playeranalytics.plugin.server.FabricPluginLogger;
|
||||
import net.playeranalytics.plugin.server.Listeners;
|
||||
import net.playeranalytics.plugin.server.PluginLogger;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
|
||||
public class FabricPlatformLayer implements PlatformAbstractionLayer {
|
||||
|
||||
private final DedicatedServerModInitializer plugin;
|
||||
|
||||
private PluginLogger pluginLogger;
|
||||
private Listeners listeners;
|
||||
private PluginInformation pluginInformation;
|
||||
private RunnableFactory runnableFactory;
|
||||
|
||||
public FabricPlatformLayer(DedicatedServerModInitializer plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginLogger getPluginLogger() {
|
||||
if (pluginLogger == null) {
|
||||
pluginLogger = new FabricPluginLogger(LogManager.getLogger("Plan"));
|
||||
}
|
||||
return pluginLogger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Listeners getListeners() {
|
||||
if (this.listeners == null) {
|
||||
this.listeners = new FabricListeners();
|
||||
}
|
||||
return listeners;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RunnableFactory getRunnableFactory() {
|
||||
if (this.runnableFactory == null) {
|
||||
this.runnableFactory = new FabricRunnableFactory();
|
||||
}
|
||||
return runnableFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginInformation getPluginInformation() {
|
||||
if (this.pluginInformation == null) {
|
||||
this.pluginInformation = new FabricPluginInformation(this.plugin);
|
||||
}
|
||||
return pluginInformation;
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.playeranalytics.plugin;
|
||||
|
||||
import net.fabricmc.api.DedicatedServerModInitializer;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class FabricPluginInformation implements PluginInformation {
|
||||
|
||||
private final DedicatedServerModInitializer plugin;
|
||||
|
||||
public FabricPluginInformation(DedicatedServerModInitializer plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getResourceFromJar(String resource) {
|
||||
return this.getClass().getResourceAsStream("/" + resource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getDataFolder() {
|
||||
return FabricLoader.getInstance().getGameDir().resolve("mods").resolve("Plan").toFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return FabricLoader.getInstance().getModContainer("plan").get().getMetadata().getVersion().getFriendlyString();
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.playeranalytics.plugin.scheduling;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
public class FabricRunnableFactory implements RunnableFactory {
|
||||
|
||||
private final ScheduledExecutorService executorService;
|
||||
private final Set<FabricTask> tasks;
|
||||
|
||||
public FabricRunnableFactory() {
|
||||
this.executorService = Executors.newScheduledThreadPool(8);
|
||||
this.tasks = Collections.newSetFromMap(new ConcurrentHashMap<>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnscheduledTask create(Runnable runnable) {
|
||||
return new UnscheduledFabricTask(executorService, runnable, task -> {
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnscheduledTask create(PluginRunnable runnable) {
|
||||
return new UnscheduledFabricTask(executorService, runnable, runnable::setCancellable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelAllKnownTasks() {
|
||||
this.tasks.forEach(Task::cancel);
|
||||
this.tasks.clear();
|
||||
executorService.shutdown();
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.playeranalytics.plugin.scheduling;
|
||||
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
public class FabricTask implements Task {
|
||||
|
||||
private final Future<?> task;
|
||||
|
||||
public FabricTask(Future<?> task) {
|
||||
this.task = task;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGameThread() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
task.cancel(false);
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.playeranalytics.plugin.scheduling;
|
||||
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class UnscheduledFabricTask implements UnscheduledTask {
|
||||
|
||||
private final ScheduledExecutorService scheduler;
|
||||
private final Runnable runnable;
|
||||
private final Consumer<Task> cancellableConsumer;
|
||||
|
||||
public UnscheduledFabricTask(ScheduledExecutorService scheduler, Runnable runnable, Consumer<Task> cancellableConsumer) {
|
||||
this.scheduler = scheduler;
|
||||
this.runnable = runnable;
|
||||
this.cancellableConsumer = cancellableConsumer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task runTaskAsynchronously() {
|
||||
FabricTask task = new FabricTask(this.scheduler.submit(this.runnable));
|
||||
cancellableConsumer.accept(task);
|
||||
return task;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task runTaskLaterAsynchronously(long delayTicks) {
|
||||
FabricTask task = new FabricTask(this.scheduler.schedule(
|
||||
this.runnable,
|
||||
delayTicks * 50,
|
||||
TimeUnit.MILLISECONDS
|
||||
));
|
||||
cancellableConsumer.accept(task);
|
||||
return task;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task runTaskTimerAsynchronously(long delayTicks, long periodTicks) {
|
||||
FabricTask task = new FabricTask(this.scheduler.scheduleAtFixedRate(
|
||||
runnable,
|
||||
delayTicks * 50,
|
||||
periodTicks * 50,
|
||||
TimeUnit.MILLISECONDS
|
||||
));
|
||||
cancellableConsumer.accept(task);
|
||||
return task;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task runTask() {
|
||||
return runTaskAsynchronously();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task runTaskLater(long delayTicks) {
|
||||
return runTaskLaterAsynchronously(delayTicks);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task runTaskTimer(long delayTicks, long periodTicks) {
|
||||
return runTaskTimerAsynchronously(delayTicks, periodTicks);
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.playeranalytics.plugin.server;
|
||||
|
||||
import net.playeranalytics.plan.gathering.listeners.FabricListener;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class FabricListeners implements Listeners {
|
||||
|
||||
private final Set<FabricListener> listeners;
|
||||
|
||||
public FabricListeners() {
|
||||
this.listeners = Collections.newSetFromMap(new ConcurrentHashMap<>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerListener(Object listener) {
|
||||
if (listener == null) {
|
||||
throw new IllegalArgumentException("Listener can not be null!");
|
||||
} else if (!(listener instanceof FabricListener)) {
|
||||
throw new IllegalArgumentException("Listener needs to be of type " + listener.getClass().getName() + ", but was " + listener.getClass());
|
||||
} else {
|
||||
if (!((FabricListener) listener).isEnabled()) {
|
||||
((FabricListener) listener).register();
|
||||
listeners.add((FabricListener) listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterListener(Object listener) {
|
||||
((FabricListener) listener).disable();
|
||||
listeners.remove(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterListeners() {
|
||||
listeners.forEach(FabricListener::disable);
|
||||
listeners.clear();
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.playeranalytics.plugin.server;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
public class FabricPluginLogger implements PluginLogger {
|
||||
|
||||
private final Logger logger;
|
||||
|
||||
public FabricPluginLogger(Logger logger) {
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginLogger info(String message) {
|
||||
logger.info("[Plan] " + message);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginLogger warn(String message) {
|
||||
logger.warn("[Plan] " + message);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginLogger error(String message) {
|
||||
logger.error("[Plan] " + message);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginLogger warn(String message, Throwable throwable) {
|
||||
logger.warn("[Plan] " + message, throwable);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginLogger error(String message, Throwable throwable) {
|
||||
logger.error("[Plan] " + message, throwable);
|
||||
return this;
|
||||
}
|
||||
}
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.playeranalytics.plan;
|
||||
|
||||
import com.djrapitops.plan.PlanSystem;
|
||||
import com.djrapitops.plan.settings.config.PlanConfig;
|
||||
import com.djrapitops.plan.settings.config.paths.WebserverSettings;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import utilities.RandomData;
|
||||
import utilities.TestSettings;
|
||||
import utilities.mocks.FabricMockComponent;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
|
||||
/**
|
||||
* Test for Fabric PlanSystem.
|
||||
*
|
||||
* @author AuroraLS3
|
||||
* @author Kopo942
|
||||
*/
|
||||
class FabricSystemTest {
|
||||
|
||||
private final int TEST_PORT_NUMBER = RandomData.randomInt(9005, 9500);
|
||||
private PlanSystem system;
|
||||
|
||||
@BeforeEach
|
||||
void prepareSystem(@TempDir Path temp) throws Exception {
|
||||
system = new FabricMockComponent(temp).getPlanSystem();
|
||||
system.getConfigSystem().getConfig()
|
||||
.set(WebserverSettings.PORT, TEST_PORT_NUMBER);
|
||||
}
|
||||
|
||||
@Test
|
||||
void fabricSystemEnables() {
|
||||
try {
|
||||
system.enable();
|
||||
assertTrue(system.isEnabled());
|
||||
} finally {
|
||||
system.disable();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void fabricSystemHasDefaultConfigValuesAfterEnable() throws IllegalAccessException {
|
||||
try {
|
||||
system.enable();
|
||||
PlanConfig config = system.getConfigSystem().getConfig();
|
||||
|
||||
TestSettings.assertValidDefaultValuesForAllSettings(config, TestSettings.getServerSettings());
|
||||
} finally {
|
||||
system.disable();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package utilities.mocks;
|
||||
|
||||
import com.djrapitops.plan.PlanPlugin;
|
||||
import com.djrapitops.plan.PlanSystem;
|
||||
import net.minecraft.server.dedicated.MinecraftDedicatedServer;
|
||||
import net.playeranalytics.plan.DaggerPlanFabricComponent;
|
||||
import net.playeranalytics.plan.PlanFabricComponent;
|
||||
import net.playeranalytics.plan.identification.properties.FabricServerProperties;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
|
||||
/**
|
||||
* Test utility for creating a dagger PlanComponent using a mocked Plan.
|
||||
*
|
||||
* @author AuroraLS3
|
||||
* @author Kopo942
|
||||
*/
|
||||
public class FabricMockComponent {
|
||||
|
||||
private final Path tempDir;
|
||||
|
||||
private PlanPlugin planMock;
|
||||
private PlanFabricComponent component;
|
||||
|
||||
public FabricMockComponent(Path tempDir) {
|
||||
this.tempDir = tempDir;
|
||||
}
|
||||
|
||||
public PlanPlugin getPlanMock() {
|
||||
if (planMock == null) {
|
||||
planMock = PlanFabricMocker.setUp()
|
||||
.withDataFolder(tempDir.resolve("data").toFile())
|
||||
.getPlanMock();
|
||||
}
|
||||
return planMock;
|
||||
}
|
||||
|
||||
public PlanSystem getPlanSystem() {
|
||||
if (component == null) {
|
||||
PlanPlugin planMock = getPlanMock();
|
||||
component = DaggerPlanFabricComponent.builder()
|
||||
.plan(planMock)
|
||||
.abstractionLayer(new TestPlatformAbstractionLayer(planMock))
|
||||
.server(mockServer())
|
||||
.serverProperties(mockServerProperties())
|
||||
.build();
|
||||
}
|
||||
return component.system();
|
||||
}
|
||||
|
||||
private MinecraftDedicatedServer mockServer() {
|
||||
MinecraftDedicatedServer serverMock = Mockito.mock(MinecraftDedicatedServer.class);
|
||||
doReturn("").when(serverMock).getServerIp();
|
||||
doReturn(25565).when(serverMock).getPort();
|
||||
doReturn("1.17.1").when(serverMock).getVersion();
|
||||
return serverMock;
|
||||
}
|
||||
|
||||
private FabricServerProperties mockServerProperties() {
|
||||
FabricServerProperties propertiesMock = Mockito.mock(FabricServerProperties.class);
|
||||
doReturn("").when(propertiesMock).getIp();
|
||||
return propertiesMock;
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package utilities.mocks;
|
||||
|
||||
import com.djrapitops.plan.commands.use.ColorScheme;
|
||||
import net.playeranalytics.plan.PlanFabric;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
|
||||
/**
|
||||
* Mocking utility for Fabric version of Plan.
|
||||
*
|
||||
* @author AuroraLS3
|
||||
* @author Kopo942
|
||||
*/
|
||||
public class PlanFabricMocker extends Mocker {
|
||||
|
||||
private PlanFabric planMock;
|
||||
|
||||
private PlanFabricMocker() {
|
||||
}
|
||||
|
||||
public static PlanFabricMocker setUp() {
|
||||
return new PlanFabricMocker().mockPlugin();
|
||||
}
|
||||
|
||||
private PlanFabricMocker mockPlugin() {
|
||||
planMock = Mockito.mock(PlanFabric.class);
|
||||
super.planMock = planMock;
|
||||
|
||||
doReturn(new ColorScheme("§1", "§2", "§3")).when(planMock).getColorScheme();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public PlanFabricMocker withDataFolder(File tempFolder) {
|
||||
doReturn(tempFolder).when(planMock).getDataFolder();
|
||||
return this;
|
||||
}
|
||||
|
||||
PlanFabric getPlanMock() {
|
||||
return planMock;
|
||||
}
|
||||
}
|
@ -1,3 +1,13 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
maven {
|
||||
name = 'Fabric'
|
||||
url = 'https://maven.fabricmc.net/'
|
||||
}
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = 'Plan'
|
||||
|
||||
include 'api'
|
||||
@ -8,4 +18,5 @@ include 'nukkit'
|
||||
include 'bungeecord'
|
||||
include 'velocity'
|
||||
include 'plugin'
|
||||
include 'extensions'
|
||||
include 'extensions'
|
||||
include 'fabric'
|
||||
|
Loading…
Reference in New Issue
Block a user