From 2d615d717d58ecdd7822816176b0f384b2bafcd0 Mon Sep 17 00:00:00 2001 From: JeremyStarTM Date: Thu, 14 Nov 2024 20:07:26 +0100 Subject: [PATCH] Updated too many things to count --- .gitignore | 178 ++++----------- README.md | 2 +- build.gradle | 4 +- common/build.gradle | 86 +++++++ common/gradle | 1 + common/gradlew | 1 + common/gradlew.bat | 1 + .../pickshadow/common/CommonLibrary.java | 66 ++++++ .../pickshadow/common/PSSKInformation.java | 37 ++-- .../pickshadow/common/package-info.java | 25 +++ .../jeremystartm/pickshadow/package-info.java | 25 +++ common/src/main/java/module-info.java | 15 ++ common/src/main/javadoc/theme.css | 1 + common/src/main/resources/.gitignore | 1 + docker-compose.yml | 23 ++ extension/build.gradle | 64 ++---- .../pickshadow/extension/BuildOptions.java | 108 +++++++++ .../pickshadow/extension/Extension.java | 111 ++++++++-- .../extension/ExtensionConfiguration.java | 59 +++-- .../extension/api/command/CommandBase.java | 116 +++++++++- .../api/command/CommandBaseWithNull.java | 60 +++++ .../completion/StaticTabCompletion.java | 33 ++- .../api/entity/player/PlayerData.java | 115 ++++++++-- .../api/entity/player/PlayerDataFactory.java | 17 +- .../api/translation/LanguageString.java | 36 ++- .../api/translation/TranslationManager.java | 50 +++-- .../extension/command/TemplateCommand.java | 74 +++++++ .../command/general/AnnounceCommand.java | 29 ++- .../command/general/ClearChatCommand.java | 2 +- .../command/general/ExtensionCommand.java | 45 ++-- .../command/general/LanguageCommand.java | 119 ++++++++++ .../command/general/LinkCommand.java | 3 +- .../command/general/TrollCommand.java | 2 +- .../general/replacement/GamemodeCommand.java | 183 +++++++++++++++ .../general/replacement/HelpCommand.java | 69 ++++++ .../{ => replacement}/MessageCommand.java | 38 +++- .../general/replacement/PluginsCommand.java | 94 ++++++++ .../general/replacement/ReloadCommand.java | 53 +++++ .../general/replacement/package-info.java | 27 +++ .../command/survival/HomeCommand.java | 68 +++++- .../command/survival/package-info.java | 3 +- .../command/survivalcheat/FeedCommand.java | 128 +++++++++++ .../command/survivalcheat/HealCommand.java | 153 +++++++++++++ .../ToggleDownfallCommand.java | 4 +- .../command/survivalcheat/package-info.java | 27 +++ .../extension/listener/ChatListener.java | 68 +++++- .../listener/ConnectionListener.java | 51 +++-- .../extension/misc/BukkitLoggingAdapter.java | 54 +++++ .../pickshadow/extension/misc/Scheduler.java | 109 +++++++++ .../extension/misc/TabListHandler.java | 15 +- extension/src/main/resources/plugin.yml | 209 ++++++++++++++++-- .../src/main/resources/translations/de.json | 45 +++- .../src/main/resources/translations/en.json | 51 ++++- gradle.properties | 36 +-- servermanager/build.gradle | 96 ++++++++ servermanager/gradle | 1 + servermanager/gradlew | 1 + servermanager/gradlew.bat | 1 + servermanager/src/main/javadoc/theme.css | 1 + settings.gradle | 4 +- 60 files changed, 2642 insertions(+), 456 deletions(-) create mode 100644 common/build.gradle create mode 120000 common/gradle create mode 120000 common/gradlew create mode 120000 common/gradlew.bat create mode 100644 common/src/main/java/de/jeremystartm/pickshadow/common/CommonLibrary.java rename extension/src/main/java/de/jeremystartm/pickshadow/extension/ExtensionInformation.java => common/src/main/java/de/jeremystartm/pickshadow/common/PSSKInformation.java (89%) create mode 100644 common/src/main/java/de/jeremystartm/pickshadow/common/package-info.java create mode 100644 common/src/main/java/de/jeremystartm/pickshadow/package-info.java create mode 100644 common/src/main/java/module-info.java create mode 120000 common/src/main/javadoc/theme.css create mode 100644 common/src/main/resources/.gitignore create mode 100644 docker-compose.yml create mode 100644 extension/src/main/java/de/jeremystartm/pickshadow/extension/BuildOptions.java create mode 100644 extension/src/main/java/de/jeremystartm/pickshadow/extension/api/command/CommandBaseWithNull.java create mode 100644 extension/src/main/java/de/jeremystartm/pickshadow/extension/command/TemplateCommand.java create mode 100644 extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/LanguageCommand.java create mode 100644 extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/replacement/GamemodeCommand.java create mode 100644 extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/replacement/HelpCommand.java rename extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/{ => replacement}/MessageCommand.java (71%) create mode 100644 extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/replacement/PluginsCommand.java create mode 100644 extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/replacement/ReloadCommand.java create mode 100644 extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/replacement/package-info.java create mode 100644 extension/src/main/java/de/jeremystartm/pickshadow/extension/command/survivalcheat/FeedCommand.java create mode 100644 extension/src/main/java/de/jeremystartm/pickshadow/extension/command/survivalcheat/HealCommand.java rename extension/src/main/java/de/jeremystartm/pickshadow/extension/command/{general => survivalcheat}/ToggleDownfallCommand.java (96%) create mode 100644 extension/src/main/java/de/jeremystartm/pickshadow/extension/command/survivalcheat/package-info.java create mode 100644 extension/src/main/java/de/jeremystartm/pickshadow/extension/misc/BukkitLoggingAdapter.java create mode 100644 extension/src/main/java/de/jeremystartm/pickshadow/extension/misc/Scheduler.java create mode 100644 servermanager/build.gradle create mode 120000 servermanager/gradle create mode 120000 servermanager/gradlew create mode 120000 servermanager/gradlew.bat create mode 120000 servermanager/src/main/javadoc/theme.css diff --git a/.gitignore b/.gitignore index 3734b6f..b51359d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,140 +1,50 @@ -# User-specific stuff -.idea/ - -*.iml -*.ipr -*.iws - -# IntelliJ -out/ -# mpeltonen/sbt-idea plugin -.idea_modules/ - -# JIRA plugin -atlassian-ide-plugin.xml - -# Compiled class file -*.class - -# Log file -*.log - -# BlueJ files -*.ctxt - -# Package Files # -*.jar -*.war -*.nar -*.ear -*.zip -*.tar.gz -*.rar - -# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml -hs_err_pid* - -*~ - -# temporary files which can be created if a process still has a handle open of a deleted file -.fuse_hidden* - -# KDE directory preferences -.directory - -# Linux trash folder which might appear on any partition or disk -.Trash-* - -# .nfs files are created when an open file is removed but is still being accessed -.nfs* - -# General -.DS_Store -.AppleDouble -.LSOverride - -# Icon must end with two \r -Icon - -# Thumbnails -._* - -# Files that might appear in the root of a volume -.DocumentRevisions-V100 -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes -.VolumeIcon.icns -.com.apple.timemachine.donotpresent - -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk - -# Windows thumbnail cache files -Thumbs.db -Thumbs.db:encryptable -ehthumbs.db -ehthumbs_vista.db - -# Dump file -*.stackdump - -# Folder config file -[Dd]esktop.ini - -# Recycle Bin used on file shares -$RECYCLE.BIN/ - -# Windows Installer files -*.cab -*.msi -*.msix -*.msm -*.msp - -# Windows shortcuts -*.lnk - +### Gradle ### .gradle build/ - -# Ignore Gradle GUI config -gradle-app.setting - -# Cache of project -.gradletasknamecache - -**/build/ - -# Common working directory run/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ -# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) -!gradle-wrapper.jar +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +.idea/* +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ -# Avoid committing large parts of the server -server/.console_history -server/banned-ips.json -server/banned-players.json -server/commands.yml -server/config -server/help.yml -server/logs -server/ops.json -server/permissions.yml -server/plugins/spark/tmp -server/usercache.json -server/version_history.json -server/whitelist.json -server/world -server/world_nether -server/world_the_end +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ -# nothing to see here :3 -NEVEREVERCOMMITTHIS.sh -uwu.txt +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store + +### Java ### +hs_err_pid*.log + +## Docker ## +dist/volumes diff --git a/README.md b/README.md index 047a8d7..a425a25 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# PickShadow's server code +# PickShadow Server Kit (PSSK) This repository contains PickShadow's server-side code. ## Index diff --git a/build.gradle b/build.gradle index cd81c7b..1de2316 100644 --- a/build.gradle +++ b/build.gradle @@ -28,8 +28,8 @@ allprojects { repositories { mavenCentral() maven { - name = "staropensource-sosengine" - url = "https://mvn.staropensource.de/sosengine" + name = "staropensource-engine" + url = "https://mvn.staropensource.de/engine" } maven { name = "papermc-repo" diff --git a/common/build.gradle b/common/build.gradle new file mode 100644 index 0000000..bf91047 --- /dev/null +++ b/common/build.gradle @@ -0,0 +1,86 @@ + +/* + * PICKSHADOW SERVER KIT SOURCE FILE + * Copyright (c) 2024 The PickShadow Server Kit authors + * Licensed under the GNU Affero General Public License v3 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import java.nio.file.Files + + + +// Plugins +plugins { + id("java") + id("io.freefair.lombok") version("${pluginLombok}") + id("com.gorylenko.gradle-git-properties") version("${pluginGitProperties}") +} + +// Dependencies +dependencies { + // Lombok + compileOnly("org.projectlombok:lombok:${dependencyLombok}") + annotationProcessor("org.projectlombok:lombok:${dependencyLombok}") + + // JetBrains Annotations + compileOnly("org.jetbrains:annotations:${dependencyJetbrainsAnnotations}") + + // Libraries + compileOnly("de.staropensource.engine:base:${dependencyStarOpenSourceEngine}") +} + +// Set Java version +java { + toolchain.languageVersion.set(JavaLanguageVersion.of("${javaTarget}")) +} + +// Git properties configuration +// Allows us to embed git commit information in the plugin build +gitProperties { + dotGitDirectory = file("${rootProject.rootDir}/.git") + failOnNoGitDirectory = false // Allow continuing if .git directory is missing for the few who use tarballs + extProperty = "gitProps" + + dateFormat = "yyyy-MM-dd'T'HH:mmZ" + dateFormatTimeZone = "UTC" +} + +tasks.register("writeGitProperties") { // This task's only purpose is to copy the git.properties from our git properties plugin to the resources directory so it's included in the final build + doLast { + File target = file("${project.projectDir}/src/main/resources/pickshadow-git.properties") + File source = file("${project.projectDir}/build/resources/main/git.properties") + + target.delete() + source.renameTo(target) + } + + outputs.upToDateWhen({ false }) // Force task execution +} +generateGitProperties.outputs.upToDateWhen({ false }) // Force task execution +processResources.dependsOn(writeGitProperties) // Ensure git.properties file is present + +// Copy gradle.properties file for inclusion in final build +tasks.register("copyGradleProperties") { + doFirst { + File target = file("${project.projectDir}/src/main/resources/pickshadow-gradle.properties") + File source = file(project(":").projectDir.getPath() + "/gradle.properties") + target.delete() + Files.copy(source.toPath(), target.toPath()) + } + + outputs.upToDateWhen({ false }) // Force task execution +} +processResources.dependsOn(copyGradleProperties) diff --git a/common/gradle b/common/gradle new file mode 120000 index 0000000..3337596 --- /dev/null +++ b/common/gradle @@ -0,0 +1 @@ +../gradle \ No newline at end of file diff --git a/common/gradlew b/common/gradlew new file mode 120000 index 0000000..502f5a2 --- /dev/null +++ b/common/gradlew @@ -0,0 +1 @@ +../gradlew \ No newline at end of file diff --git a/common/gradlew.bat b/common/gradlew.bat new file mode 120000 index 0000000..2840132 --- /dev/null +++ b/common/gradlew.bat @@ -0,0 +1 @@ +../gradlew.bat \ No newline at end of file diff --git a/common/src/main/java/de/jeremystartm/pickshadow/common/CommonLibrary.java b/common/src/main/java/de/jeremystartm/pickshadow/common/CommonLibrary.java new file mode 100644 index 0000000..a23be4a --- /dev/null +++ b/common/src/main/java/de/jeremystartm/pickshadow/common/CommonLibrary.java @@ -0,0 +1,66 @@ +/* + * PICKSHADOW SERVER KIT SOURCE FILE + * Copyright (c) 2024 The PickShadow Server Kit authors + * Licensed under the GNU Affero General Public License v3 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.jeremystartm.pickshadow.common; + +import de.staropensource.engine.base.logging.Logger; +import lombok.Getter; + +/** + * Initializes and manages the PickShadow Common Library. + * + * @since v1-release0 + */ +@SuppressWarnings({ "JavadocDeclaration" }) +public final class CommonLibrary { + /** + * Contains the global instance of this class. + * + * @since v1-release0 + * -- GETTER -- + * Returns the global instance of this class. + * + * @return global instance + * @since v1-release0 + */ + @Getter + private static CommonLibrary instance = null; + + /** + * Creates and initializes an instance of this class. + * + * @since v1-release0 + */ + private CommonLibrary() { + Logger.verb("Initializing the PickShadow Common Library"); + + PSSKInformation.update(); + } + + /** + * Initializes the common library, + * if it isn't already. + * + * @since v1-release0 + */ + public static void initialize() { + if (instance == null) + instance = new CommonLibrary(); + } +} diff --git a/extension/src/main/java/de/jeremystartm/pickshadow/extension/ExtensionInformation.java b/common/src/main/java/de/jeremystartm/pickshadow/common/PSSKInformation.java similarity index 89% rename from extension/src/main/java/de/jeremystartm/pickshadow/extension/ExtensionInformation.java rename to common/src/main/java/de/jeremystartm/pickshadow/common/PSSKInformation.java index 894eca6..61cba18 100644 --- a/extension/src/main/java/de/jeremystartm/pickshadow/extension/ExtensionInformation.java +++ b/common/src/main/java/de/jeremystartm/pickshadow/common/PSSKInformation.java @@ -17,14 +17,13 @@ * along with this program. If not, see . */ -package de.jeremystartm.pickshadow.extension; +package de.jeremystartm.pickshadow.common; -import de.staropensource.sosengine.base.logging.LoggerInstance; -import de.staropensource.sosengine.base.type.VersionType; -import de.staropensource.sosengine.base.utility.PropertiesReader; +import de.staropensource.engine.base.logging.Logger; +import de.staropensource.engine.base.type.VersionType; +import de.staropensource.engine.base.utility.PropertiesReader; import lombok.Getter; -import org.jetbrains.annotations.NotNull; import java.io.IOException; import java.io.InputStream; @@ -42,15 +41,7 @@ import java.util.Properties; * @since v1-alpha0 */ @SuppressWarnings({ "JavadocDeclaration" }) -public final class ExtensionInformation { - /** - * Contains the {@link LoggerInstance} for this instance. - * - * @see LoggerInstance - * @since v1-alpha2 - */ - private static final @NotNull LoggerInstance logger = new LoggerInstance.Builder().setClazz(ExtensionInformation.class).setOrigin("PSSE").build(); - +public final class PSSKInformation { /** * Contains the extensions's version codename. * @@ -284,7 +275,7 @@ public final class ExtensionInformation { * * @since v1-alpha6 */ - private ExtensionInformation() {} + private PSSKInformation() {} /** * Updates all variables. @@ -295,14 +286,14 @@ public final class ExtensionInformation { * @since v1-alpha1 */ public static synchronized void update() { - logger.diag("Updating extension information"); + Logger.diag("Updating extension information"); // Load properties from bundled gradle.properties Properties gradleProperties = new Properties(); - InputStream gradleStream = ExtensionInformation.class.getClassLoader().getResourceAsStream("psse-gradle.properties"); + InputStream gradleStream = PSSKInformation.class.getClassLoader().getResourceAsStream("psse-gradle.properties"); if (gradleStream == null) { - logger.crash("Unable to load build information: The bundled gradle.properties file could not be found."); + Logger.crash("Unable to load build information: The bundled gradle.properties file could not be found."); return; } @@ -310,16 +301,16 @@ public final class ExtensionInformation { gradleProperties.load(gradleStream); gradleStream.close(); } catch (IOException exception) { - logger.crash("Unable to load build information: InputStream 'gradleStream' failed", exception); + Logger.crash("Unable to load build information: InputStream 'gradleStream' failed", exception); return; } // Load properties from bundled git.properties // or fill in blank information if file missing Properties gitProperties = new Properties(); - InputStream gitStream = ExtensionInformation.class.getClassLoader().getResourceAsStream("psse-git.properties"); + InputStream gitStream = PSSKInformation.class.getClassLoader().getResourceAsStream("psse-git.properties"); if (gitStream == null) { - logger.error("Unable to load build information: The bundled git.properties file could not be found. Did you download a tarball?"); + Logger.error("Unable to load build information: The bundled git.properties file could not be found. Did you download a tarball?"); // Fake information gitProperties.setProperty("git.total.commit.count", "0"); @@ -337,7 +328,7 @@ public final class ExtensionInformation { gitProperties.load(gitStream); gitStream.close(); } catch (IOException exception) { - logger.crash("Unable to load build information: InputStream 'gitStream' failed", exception); + Logger.crash("Unable to load build information: InputStream 'gitStream' failed", exception); return; } } @@ -369,7 +360,7 @@ public final class ExtensionInformation { calendar.setTime(date); gitCommitTime = calendar.toZonedDateTime(); } catch (ParseException exception) { - logger.crash("Unable to load build information: Can't parse \"" + gitParser.getString("git.commit.time") + "\" using format \"yyyy-MM-dd'T'HH:mmZ\"", exception); + Logger.crash("Unable to load build information: Can't parse \"" + gitParser.getString("git.commit.time") + "\" using format \"yyyy-MM-dd'T'HH:mmZ\"", exception); } gitCommitterName = gitParser.getString("git.commit.user.name"); gitCommitterEmail = gitParser.getString("git.commit.user.email"); diff --git a/common/src/main/java/de/jeremystartm/pickshadow/common/package-info.java b/common/src/main/java/de/jeremystartm/pickshadow/common/package-info.java new file mode 100644 index 0000000..4c04f4f --- /dev/null +++ b/common/src/main/java/de/jeremystartm/pickshadow/common/package-info.java @@ -0,0 +1,25 @@ +/* + * PICKSHADOW SERVER KIT SOURCE FILE + * Copyright (c) 2024 The PickShadow Server Kit authors + * Licensed under the GNU Affero General Public License v3 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +/** + * Code commonly used across PSSK's codebase. + * + * @since v1-release0 + */ +package de.jeremystartm.pickshadow.common; diff --git a/common/src/main/java/de/jeremystartm/pickshadow/package-info.java b/common/src/main/java/de/jeremystartm/pickshadow/package-info.java new file mode 100644 index 0000000..3625640 --- /dev/null +++ b/common/src/main/java/de/jeremystartm/pickshadow/package-info.java @@ -0,0 +1,25 @@ +/* + * PICKSHADOW SERVER KIT SOURCE FILE + * Copyright (c) 2024 The PickShadow Server Kit authors + * Licensed under the GNU Affero General Public License v3 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +/** + * Everything related to the PickShadow Server Kit. + * + * @since v1-release0 + */ +package de.jeremystartm.pickshadow; diff --git a/common/src/main/java/module-info.java b/common/src/main/java/module-info.java new file mode 100644 index 0000000..209731e --- /dev/null +++ b/common/src/main/java/module-info.java @@ -0,0 +1,15 @@ +/** + * Code commonly used across PSSK's codebase. + * + * @since v1-release0 + */ +module pickshadow.common { + // Libraries + requires sosengine.base; + + // API access + exports de.jeremystartm.pickshadow.common; + + // Reflection access + opens de.jeremystartm.pickshadow.common; +} diff --git a/common/src/main/javadoc/theme.css b/common/src/main/javadoc/theme.css new file mode 120000 index 0000000..681484a --- /dev/null +++ b/common/src/main/javadoc/theme.css @@ -0,0 +1 @@ +../../../../src/main/javadoc/theme.css \ No newline at end of file diff --git a/common/src/main/resources/.gitignore b/common/src/main/resources/.gitignore new file mode 100644 index 0000000..0567e20 --- /dev/null +++ b/common/src/main/resources/.gitignore @@ -0,0 +1 @@ +*.properties diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..6562e9b --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,23 @@ +services: + postgresql-dev: + container_name: postgresql-dev + image: bitnami/postgresql:latest + restart: always + stop_grace_period: 10s + user: 0:0 + environment: + - ALLOW_EMPTY_PASSWORD=yes + - POSTGRESQL_USERNAME=postgres + - POSTGRESQL_PASSWORD=postgres + - POSTGRESQL_POSTGRES_PASSWORD=postgres + volumes: + - ./dist/volumes/postgresql-dev/data:/bitnami/postgresql/data + networks: + - development + ports: + - 127.0.0.1:5432:5432 + +networks: + development: + name: development + external: false diff --git a/extension/build.gradle b/extension/build.gradle index 75ad362..9a8bc80 100644 --- a/extension/build.gradle +++ b/extension/build.gradle @@ -1,7 +1,3 @@ -import io.papermc.paperweight.userdev.ReobfArtifactConfiguration - -import java.nio.file.Files - /* * PICKSHADOW SERVER KIT SOURCE FILE * Copyright (c) 2024 The PickShadow Server Kit authors @@ -21,13 +17,14 @@ import java.nio.file.Files * along with this program. If not, see . */ +import io.papermc.paperweight.userdev.ReobfArtifactConfiguration + // Plugins plugins { id("java") id("io.freefair.lombok") version("${pluginLombok}") id("io.papermc.paperweight.userdev") version("${pluginPaperweight}") id("io.github.goooler.shadow") version("${pluginShadow}") - id("com.gorylenko.gradle-git-properties") version("${pluginGitProperties}") id("xyz.jpenilla.run-paper") version("${pluginRunTask}") } @@ -41,7 +38,8 @@ dependencies { compileOnly("org.jetbrains:annotations:${dependencyJetbrainsAnnotations}") // Libraries - compileOnly("de.staropensource.sosengine:base:${dependencyStarOpenSourceEngine}") + compileOnly("de.staropensource.engine:base:${dependencyStarOpenSourceEngine}") + runtimeOnly("de.staropensource.enginemc:platform-bukkit:${dependencyStarOpenSourceEngineMC}") implementation("com.google.code.gson:gson:${dependencyGson}") // Server @@ -54,6 +52,9 @@ dependencies { // Bukkit libraries implementation("fr.mrmicky:fastboard:${dependencyFastboard}") + + // Project + implementation(project(":common")) } // Set Java version @@ -68,44 +69,6 @@ processResources { } } -// Git properties configuration -// Allows us to embed git commit information in the plugin build -gitProperties { - dotGitDirectory = file("${rootProject.rootDir}/.git") - failOnNoGitDirectory = false // Allow continuing if .git directory is missing for the few who use tarballs - extProperty = "gitProps" - - dateFormat = "yyyy-MM-dd'T'HH:mmZ" - dateFormatTimeZone = "UTC" -} - -tasks.register("writeGitProperties") { // This task's only purpose is to copy the git.properties from our git properties plugin to the resources directory so it's included in the final build - doLast { - File target = file("${project.projectDir}/src/main/resources/psse-git.properties") - File source = file("${project.projectDir}/build/resources/main/git.properties") - - target.delete() - source.renameTo(target) - } - - outputs.upToDateWhen({ false }) // Force task execution -} -generateGitProperties.outputs.upToDateWhen({ false }) // Force task execution -processResources.dependsOn(writeGitProperties) // Ensure git.properties file is present - -// Copy gradle.properties file for inclusion in final build -tasks.register("copyGradleProperties") { - doFirst { - File target = file("${project.projectDir}/src/main/resources/psse-gradle.properties") - File source = file(project(":").projectDir.getPath() + "/gradle.properties") - target.delete() - Files.copy(source.toPath(), target.toPath()) - } - - outputs.upToDateWhen({ false }) // Force task execution -} -processResources.dependsOn(copyGradleProperties) - // Configure paperweight paperweight { reobfArtifactConfiguration = ReobfArtifactConfiguration.getMOJANG_PRODUCTION() @@ -114,14 +77,19 @@ paperweight { // Configure server runServer { minecraftVersion("${minecraftVersion}") - systemProperty("file.encoding", "UTF-8") - systemProperty("com.mojang.eula.agree", "true") - systemProperty("sosengine.base.loggerLevel", "diagnostic") + + jvmArguments.addAll( + "-Dfile.encoding=UTF-8", + "-Dcom.mojang.eula.agree=true", + "-Dsosengine.base.logLevel=diagnostic", + "-Dpickshadow.extension.enabledModes=general,survival,survivalcheat" + ) downloadPlugins { - modrinth("freedomchat", "${downloadFreedomChat}") url("${downloadEngineMC}") url("${downloadLuckPerms}") + url("${downloadSpark}") + modrinth("freedomchat", "${downloadFreedomChat}") } doFirst { diff --git a/extension/src/main/java/de/jeremystartm/pickshadow/extension/BuildOptions.java b/extension/src/main/java/de/jeremystartm/pickshadow/extension/BuildOptions.java new file mode 100644 index 0000000..a18a2fc --- /dev/null +++ b/extension/src/main/java/de/jeremystartm/pickshadow/extension/BuildOptions.java @@ -0,0 +1,108 @@ +/* + * PICKSHADOW SERVER KIT SOURCE FILE + * Copyright (c) 2024 The PickShadow Server Kit authors + * Licensed under the GNU Affero General Public License v3 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.jeremystartm.pickshadow.extension; + +import org.bukkit.potion.PotionEffectType; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * Provides an ugly way to choose + * what to put into the final, + * compiled bytecode. + *

+ * Seriously, why doesn't Java + * have a macro processor. It + * would've been so useful! + * + * @since v1-release0 + */ +public final class BuildOptions { + // -----> Settings + /** + * Contains an array of all bad effects. + * + * @since v1-release0 + */ + public static final @NotNull List<@NotNull PotionEffectType> SETTINGS_EFFECTS_BAD = List.of( + PotionEffectType.SLOWNESS, + PotionEffectType.MINING_FATIGUE, + PotionEffectType.INSTANT_DAMAGE, + PotionEffectType.NAUSEA, + PotionEffectType.BLINDNESS, + PotionEffectType.HUNGER, + PotionEffectType.WEAKNESS, + PotionEffectType.POISON, + PotionEffectType.UNLUCK, + PotionEffectType.BAD_OMEN, + PotionEffectType.DARKNESS, + PotionEffectType.TRIAL_OMEN, + PotionEffectType.RAID_OMEN, + PotionEffectType.INFESTED + ); + /** + * Contains an array of all damaging effects. + * + * @since v1-release0 + */ + public static final @NotNull List<@NotNull PotionEffectType> SETTINGS_EFFECTS_DAMAGING = List.of( + PotionEffectType.INSTANT_DAMAGE, + PotionEffectType.HUNGER, + PotionEffectType.POISON + ); + + // -----> Fixes and unfixes + /** + * Unfixes MC-212 (fixed in 24w45a), + * which allows players to avoid fall + * damage if they disconnect and then + * reconnect before hitting the ground. + * + * @see MC-212 on the Minecraft bug tracker + * @see 24w45a on the Minecraft Wiki + * @since v1-release0 + */ + public static final boolean UNFIX_FALLDAMAGE_CANCELLING = true; + + // -----> Small stuff + /** + * Hides all messages starting with {@code #}. + *

+ * This allows commenting commands and + * messages easily. To circumvent it, + * just type {@code #} instead. + * + * @since v1-release0 + */ + public static final boolean SMALLSTUFF_CHAT_COMMENTS = true; + + /** + * Enables stonecutter damage. + *

+ * I mean seriously, this thing has rotating blades. + * That Mojang decided not to add damage to it is + * embarrassing. Or can it cause animal cruelty, + * is it that Mojang? + * + * @since v1-release0 + */ + public static final boolean SMALLSTUFF_STONECUTTER_DAMAGE = true; +} diff --git a/extension/src/main/java/de/jeremystartm/pickshadow/extension/Extension.java b/extension/src/main/java/de/jeremystartm/pickshadow/extension/Extension.java index a1d2353..5c48bbe 100644 --- a/extension/src/main/java/de/jeremystartm/pickshadow/extension/Extension.java +++ b/extension/src/main/java/de/jeremystartm/pickshadow/extension/Extension.java @@ -19,14 +19,24 @@ package de.jeremystartm.pickshadow.extension; +import de.jeremystartm.pickshadow.common.CommonLibrary; import de.jeremystartm.pickshadow.extension.api.entity.player.PlayerDataFactory; import de.jeremystartm.pickshadow.extension.api.translation.TranslationManager; +import de.jeremystartm.pickshadow.extension.command.general.AnnounceCommand; +import de.jeremystartm.pickshadow.extension.command.general.replacement.GamemodeCommand; +import de.jeremystartm.pickshadow.extension.command.general.replacement.HelpCommand; +import de.jeremystartm.pickshadow.extension.command.general.replacement.MessageCommand; +import de.jeremystartm.pickshadow.extension.command.survivalcheat.FeedCommand; +import de.jeremystartm.pickshadow.extension.command.survivalcheat.HealCommand; +import de.jeremystartm.pickshadow.extension.command.survivalcheat.ToggleDownfallCommand; import de.jeremystartm.pickshadow.extension.command.general.*; import de.jeremystartm.pickshadow.extension.command.survival.HomeCommand; import de.jeremystartm.pickshadow.extension.listener.ChatListener; import de.jeremystartm.pickshadow.extension.listener.ConnectionListener; -import de.staropensource.sosengine.base.logging.LoggerInstance; -import de.staropensource.sosengine.base.utility.Miscellaneous; +import de.jeremystartm.pickshadow.extension.misc.BukkitLoggingAdapter; +import de.jeremystartm.pickshadow.extension.misc.Scheduler; +import de.staropensource.engine.base.logging.Logger; +import de.staropensource.engine.base.utility.Miscellaneous; import lombok.Getter; import org.bukkit.Bukkit; import org.bukkit.plugin.java.JavaPlugin; @@ -51,13 +61,6 @@ public final class Extension extends JavaPlugin { @Getter private static Extension instance = null; - /** - * Contains the logger instance for this instance. - * - * @since v1-release0 - */ - private final LoggerInstance logger = new LoggerInstance.Builder().setClazz(getClass()).setOrigin("PSSE").build(); - /** * Creates and initializes an instance of this class. * @@ -67,6 +70,7 @@ public final class Extension extends JavaPlugin { instance = this; } + // -----> Extension (de)initialization /** * Called after the plugin has been loaded. * @@ -74,25 +78,28 @@ public final class Extension extends JavaPlugin { */ @Override public void onLoad() { - logger.info("Bootstrapping"); + // Fix logging adapter + Logger.setLoggingAdapter(new BukkitLoggingAdapter()); + + Logger.info("Bootstrapping"); // Check for NMS try { Class.forName("net.minecraft.server.MinecraftServer"); } catch (Exception exception) { - logger.crash("This Bukkit implementation does not run on NMS.\nPSSE is designed to only run on NMS-based server software.\nYou may need to rewrite certain parts of PSSE so it can run on non-NMS server software.", exception); + Logger.crash("This Bukkit implementation does not run on NMS.\nPSSE is designed to only run on NMS-based server software.\nYou may need to rewrite certain parts of PSSE so it can run on non-NMS server software.", exception); return; } // Load classes + CommonLibrary.initialize(); new ExtensionConfiguration().loadConfiguration(); - ExtensionInformation.update(); TranslationManager.loadTranslations(); TranslationManager.processTranslations(); //TabListHandler.initialize(); PlayerDataFactory.initialize(); - logger.info("Bootstrapped in " + Miscellaneous.measureExecutionTime(() -> {}) + "ms"); + Logger.info("Bootstrapped in " + Miscellaneous.measureExecutionTime(() -> {}) + "ms"); } /** @@ -102,25 +109,49 @@ public final class Extension extends JavaPlugin { */ @Override public void onEnable() { - logger.info("Initializing"); + Logger.info("Initializing"); try { - logger.info("Initialized in " + Miscellaneous.measureExecutionTime(() -> { - logger.verb("Registering commands"); + Logger.info("Initialized in " + Miscellaneous.measureExecutionTime(() -> { + Logger.verb("Checking environment"); + // Check for PaperMC + if (!isPaper()) + Logger.crash("PaperMC classes weren't detected.\nYour server likely isn't running PaperMC or a fork of it.\nPSSE only supports PaperMC-based Minecraft servers."); + // Check for Folia + if (isFolia()) + Logger.warn("Folia classes were detected.\nFolia support is experimental. Everything *should* work, but nothing is guaranteed. Here be dragons!"); + else + Logger.verb("Folia classes weren't detected.\nFolia or a fork of it doesn't seem to be installed."); + + Logger.verb("Registering commands"); + + new GamemodeCommand(); + new HelpCommand(); + new MessageCommand(); + new AnnounceCommand(); new ClearChatCommand(); new ExtensionCommand(); - new HomeCommand(); + new LanguageCommand(); new LinkCommand(); - new MessageCommand(); - new ToggleDownfallCommand(); new TrollCommand(); - logger.verb("Registering listeners"); + + new HomeCommand(); + + + new FeedCommand(); + new HealCommand(); + new ToggleDownfallCommand(); + + Logger.verb("Registering listeners"); Bukkit.getPluginManager().registerEvents(new ConnectionListener(), this); Bukkit.getPluginManager().registerEvents(new ChatListener(), this); + + Logger.verb("Starting schedulers"); + Bukkit.getServer().getGlobalRegionScheduler().runAtFixedRate(Extension.getInstance(), Scheduler::server, 1L, 1L); }) + "ms"); } catch (Exception exception) { - logger.crash("Initialization failed", exception); + Logger.crash("Initialization failed", exception); } } @@ -131,7 +162,41 @@ public final class Extension extends JavaPlugin { */ @Override public void onDisable() { - logger.info("Shutting down"); - logger.info("Shut down in " + Miscellaneous.measureExecutionTime(() -> {}) + "ms"); + Logger.info("Shutting down"); + Logger.info("Shut down in " + Miscellaneous.measureExecutionTime(() -> {}) + "ms"); + } + + // -----> Utility methods + /** + * Checks if PaperMC's + * {@code PaperBootstrap} + * class is present. + * + * @return {@code true} if present, {@code false} otherwise + * @since v1-release0 + */ + public static boolean isPaper() { + try { + Class.forName("io.papermc.paper.PaperBootstrap"); + return true; + } catch (ClassNotFoundException exception) { + return false; + } + } + /** + * Checks if Folia's + * {@code RegionizedServer} + * class is present. + * + * @return {@code true} if present, {@code false} otherwise + * @since v1-release0 + */ + public static boolean isFolia() { + try { + Class.forName("io.papermc.paper.threadedregions.RegionizedServer"); + return true; + } catch (ClassNotFoundException exception) { + return false; + } } } diff --git a/extension/src/main/java/de/jeremystartm/pickshadow/extension/ExtensionConfiguration.java b/extension/src/main/java/de/jeremystartm/pickshadow/extension/ExtensionConfiguration.java index 08c7eb4..bf551bf 100644 --- a/extension/src/main/java/de/jeremystartm/pickshadow/extension/ExtensionConfiguration.java +++ b/extension/src/main/java/de/jeremystartm/pickshadow/extension/ExtensionConfiguration.java @@ -20,8 +20,8 @@ package de.jeremystartm.pickshadow.extension; import de.jeremystartm.pickshadow.extension.api.command.completion.StaticTabCompletion; -import de.staropensource.sosengine.base.implementable.Configuration; -import de.staropensource.sosengine.base.utility.PropertiesReader; +import de.staropensource.engine.base.implementable.Configuration; +import de.staropensource.engine.base.utility.PropertiesReader; import lombok.Getter; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -56,33 +56,62 @@ public final class ExtensionConfiguration extends Configuration { /** - * If enabled, allows for unintentional behaviour - * and excess logging. Unless you want to debug or work + * Contains whether to allow unintentional behaviour + * and excess logging. + *

+ * Unless you want to debug or work * on a sensitive part of the engine, don't enable this! * * @since v1-release0 * -- GETTER -- - * Gets the value for {@link #debug}. + * Returns whether to allow unintentional behaviour + * and excess logging. + *

+ * Unless you want to debug or work + * on a sensitive part of the engine, don't enable this! * - * @return variable value - * @see #debug + * @return debugging flag state * @since v1-release0 */ private boolean debug; /** - * Causes {@link StaticTabCompletion} to print lots debugging information. + * Contains whether or not the tab completioning process + * should be logged by {@link StaticTabCompletion}. * * @since v1-release0 * -- GETTER -- - * Gets the value for {@link #debugStaticCompletion}. + * Contains whether or not the tab completioning process + * should be logged by {@link StaticTabCompletion}. * - * @return variable value - * @see #debugStaticCompletion + * @return detailed tab completioning logging * @since v1-release0 */ private boolean debugStaticCompletion; + + /** + * Contains an array of all + * enabled extension modes. + *

+ * Extension modes control what methods + * will be available and how the server + * will be controlled by the extension. + * + * @since v1-release0 + * -- GETTER -- + * Returns an array of all + * enabled extension modes. + *

+ * Extension modes control what methods + * will be available and how the server + * will be controlled by the extension. + * + * @return enabled extension modes + * @since v1-release0 + */ + private String[] enabledModes; + /** * Creates and initializes an instance of this class. * @@ -90,8 +119,6 @@ public final class ExtensionConfiguration extends Configuration { * @since v1-release0 */ ExtensionConfiguration() { - super("PSSE"); - instance = this; // Load default configuration @@ -105,6 +132,8 @@ public final class ExtensionConfiguration extends Configuration { switch (property) { case "debug" -> debug = parser.getBoolean(group + property); case "debugStaticCompletion" -> debugStaticCompletion = parser.getBoolean(group + property); + + case "enabledModes" -> enabledModes = parser.getString(group + property).split(","); } } catch (NullPointerException ignored) {} } @@ -123,6 +152,8 @@ public final class ExtensionConfiguration extends Configuration { public void loadDefaultConfiguration() { debug = false; debugStaticCompletion = false; + + enabledModes = new String[]{ "general" }; } /** {@inheritDoc} */ @@ -131,6 +162,8 @@ public final class ExtensionConfiguration extends Configuration { return switch (setting) { case "debug" -> debug; case "debugStaticCompletion" -> debugStaticCompletion; + + case "enabledModes" -> enabledModes; default -> null; }; } diff --git a/extension/src/main/java/de/jeremystartm/pickshadow/extension/api/command/CommandBase.java b/extension/src/main/java/de/jeremystartm/pickshadow/extension/api/command/CommandBase.java index a9c5abb..dc7b836 100644 --- a/extension/src/main/java/de/jeremystartm/pickshadow/extension/api/command/CommandBase.java +++ b/extension/src/main/java/de/jeremystartm/pickshadow/extension/api/command/CommandBase.java @@ -19,48 +19,129 @@ package de.jeremystartm.pickshadow.extension.api.command; +import de.jeremystartm.pickshadow.extension.ExtensionConfiguration; import de.jeremystartm.pickshadow.extension.api.translation.LanguageString; import de.jeremystartm.pickshadow.extension.api.translation.TranslationManager; +import de.staropensource.engine.base.logging.Logger; +import lombok.Getter; import org.bukkit.Bukkit; import org.bukkit.command.*; +import org.bukkit.craftbukkit.command.ServerCommandSender; import org.bukkit.util.StringUtil; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.List; /** * Abstract class for implementing commands. + * + * @since v1-release0 */ +@Getter public abstract class CommandBase implements CommandExecutor { + /** + * Contains a list of all registered commands. + * + * @since v1-release0 + */ + private static final @NotNull List<@NotNull CommandBase> REGISTERED = new ArrayList<>(); + + /** + * Contains a {@link CommandExecutor} implementation + * for disabled commands (caused by a disabled mode). + * + * @see ExtensionConfiguration#getEnabledModes() + * @since v1-release0 + */ + public static final @NotNull CommandExecutor disallowedExecutor = (sender, command, alias, arguments) -> { + sender.sendRichMessage(TranslationManager.get(LanguageString.ERROR_INVALID_MODE, sender, true)); + return true; + }; + + /** + * Contains a list of all {@link Command}s this + * command has been registered for. + * + * @since v1-release0 + */ + private final @NotNull List<@NotNull PluginCommand> commands = new ArrayList<>(); + /** * Initializes this abstract class * and registers the command. * + * @param mode mode to register this command in * @param commands all commands this class should handle * @throws IllegalArgumentException if a command does not exist * @since v1-release0 */ - public CommandBase(@NotNull String... commands) throws IllegalArgumentException { + public CommandBase(@NotNull String mode, @NotNull String... commands) throws IllegalArgumentException { + boolean disallowedByMode = !Arrays.stream(ExtensionConfiguration.getInstance().getEnabledModes()).toList().contains(mode); + for (String command : commands) { PluginCommand pluginCommand = Bukkit.getPluginCommand(command); if (pluginCommand == null) throw new IllegalArgumentException("Command registration failed: The command \"" + command + "\" does not exist"); - pluginCommand.setExecutor(this); - pluginCommand.setTabCompleter(new TabCompleter() { - @Override - public @NotNull List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { - return StringUtil.copyPartialMatches(args[args.length - 1], getCompletion().complete(sender, label, args), new ArrayList<>()); - } - }); + if (disallowedByMode) { + pluginCommand.setExecutor(disallowedExecutor); + pluginCommand.setTabCompleter(null); + } else { + pluginCommand.setExecutor(this); + pluginCommand.setTabCompleter(new TabCompleter() { + @Override + public @NotNull List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { + return StringUtil.copyPartialMatches(args[args.length - 1], getCompletion().complete(sender, label, args), new ArrayList<>()); + } + }); + } + + this.commands.add(pluginCommand); } + + REGISTERED.add(this); + } + + /** + * Initializes this abstract class + * and registers the command. + *

+ * Using this constructor instead of + * {@link #CommandBase(String, String...)} + * causes the creation of a dummy command + * which must be registered manually. + * Not recommended, use only when needed. + * + * @since v1-release0 + */ + public CommandBase() { + REGISTERED.add(this); } /** {@inheritDoc} */ @Override public final boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) { - invoke(sender, command, alias, args); + try { + invoke(sender, command, alias, args); + } catch (Exception exception) { + Logger.crash( + "Command /" + + command.getName() + + " (under alias /" + + alias + + ") failed for sender " + + sender.getName() + + " (console=" + + (sender instanceof ConsoleCommandSender) + + ") with the following arguments:\n" + + Arrays.toString(args), + exception, + false); + sender.sendRichMessage(TranslationManager.get(LanguageString.ERROR_UNKNOWN, sender, true)); + } return true; } @@ -69,11 +150,13 @@ public abstract class CommandBase implements CommandExecutor { * * @since v1-release0 */ + @SuppressWarnings("NullableProblems") // intentional, see CommandBaseWithNull public abstract void invoke(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] arguments); /** * Provides tab completions for this command. * + * @return completion * @since v1-release0 */ public abstract @NotNull TabCompletion getCompletion(); @@ -85,10 +168,13 @@ public abstract class CommandBase implements CommandExecutor { * * @param sender sender to check * @param permission permission to check for - * @return return? + * @return {@code true} if the permission is missing, {@code false} otherwise * @since v1-release0 */ protected static boolean checkPermission(@NotNull CommandSender sender, @NotNull String permission) { + if (sender instanceof ServerCommandSender) + return false; + if (!sender.hasPermission(permission)) { sender.sendRichMessage( TranslationManager.get(LanguageString.ERROR_MISSING_PERM, sender, true) @@ -99,4 +185,14 @@ public abstract class CommandBase implements CommandExecutor { return false; } + + /** + * Returns a list of all registered commands. + * + * @return list of registered commands + * @since v1-release0 + */ + public static List<@NotNull CommandBase> getREGISTERED() { + return Collections.unmodifiableList(REGISTERED); + } } diff --git a/extension/src/main/java/de/jeremystartm/pickshadow/extension/api/command/CommandBaseWithNull.java b/extension/src/main/java/de/jeremystartm/pickshadow/extension/api/command/CommandBaseWithNull.java new file mode 100644 index 0000000..5fa09d0 --- /dev/null +++ b/extension/src/main/java/de/jeremystartm/pickshadow/extension/api/command/CommandBaseWithNull.java @@ -0,0 +1,60 @@ +/* + * PICKSHADOW SERVER KIT SOURCE FILE + * Copyright (c) 2024 The PickShadow Server Kit authors + * Licensed under the GNU Affero General Public License v3 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.jeremystartm.pickshadow.extension.api.command; + +import de.jeremystartm.pickshadow.extension.api.command.completion.StubTabCompletion; +import lombok.Getter; +import org.bukkit.command.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Abstract class for implementing commands + * without the {@code command} parameter. + * + * @since v1-release0 + */ +@Getter +public abstract class CommandBaseWithNull extends CommandBase { + /** + * Initializes this abstract class + * and registers the command. + * + * @since v1-release0 + */ + public CommandBaseWithNull() {} + + /** + * Executes this command. + * + * @since v1-release0 + */ + public abstract void invoke(@NotNull CommandSender sender, @Nullable Command command, @NotNull String alias, @NotNull String[] arguments); + + /** + * Provides tab completions for this command. + * + * @return completion + * @since v1-release0 + */ + public @NotNull TabCompletion getCompletion() { + return StubTabCompletion.completion(); + } +} diff --git a/extension/src/main/java/de/jeremystartm/pickshadow/extension/api/command/completion/StaticTabCompletion.java b/extension/src/main/java/de/jeremystartm/pickshadow/extension/api/command/completion/StaticTabCompletion.java index 524566c..09257db 100644 --- a/extension/src/main/java/de/jeremystartm/pickshadow/extension/api/command/completion/StaticTabCompletion.java +++ b/extension/src/main/java/de/jeremystartm/pickshadow/extension/api/command/completion/StaticTabCompletion.java @@ -21,7 +21,7 @@ package de.jeremystartm.pickshadow.extension.api.command.completion; import de.jeremystartm.pickshadow.extension.ExtensionConfiguration; import de.jeremystartm.pickshadow.extension.api.command.TabCompletion; -import de.staropensource.sosengine.base.logging.LoggerInstance; +import de.staropensource.engine.base.logging.Logger; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -37,13 +37,6 @@ import java.util.*; * @since v1-release0 */ public final class StaticTabCompletion implements TabCompletion { - /** - * Contains the logger instance for this instance. - * - * @since v1-release0 - */ - private final @NotNull LoggerInstance logger = new LoggerInstance.Builder().setClazz(getClass()).setOrigin("PSSE").setMetadata(String.valueOf(hashCode())).build(); - /** * Creates and initializes an instance of this class. * @@ -68,7 +61,7 @@ public final class StaticTabCompletion implements TabCompletion { @Override public @NotNull List<@NotNull String> complete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] arguments) { if (ExtensionConfiguration.getInstance().isDebugStaticCompletion()) - logger.diag("complete(sender=" + sender.getName() + " alias=" + alias + " arguments=" + Arrays.toString(arguments) + ")"); + Logger.diag("complete(sender=" + sender.getName() + " alias=" + alias + " arguments=" + Arrays.toString(arguments) + ")"); try { // Get correct alias or return empty list @@ -79,26 +72,26 @@ public final class StaticTabCompletion implements TabCompletion { return List.of(); if (ExtensionConfiguration.getInstance().isDebugStaticCompletion() && alias.isEmpty()) - logger.diag("Alias has been corrected"); + Logger.diag("Alias has been corrected"); // Get and add completions if (ExtensionConfiguration.getInstance().isDebugStaticCompletion()) - logger.diag("Compiling completions"); + Logger.diag("Compiling completions"); List<@NotNull String> output = new ArrayList<>(getCompletions(sender, alias, arguments.length - 1)); // Get and add alias-wide completions if (completions.get(alias).containsKey(-1)) { if (ExtensionConfiguration.getInstance().isDebugStaticCompletion()) - logger.diag("Compiling alias-wide completions"); + Logger.diag("Compiling alias-wide completions"); output.addAll(getCompletions(sender, alias, -1)); } if (ExtensionConfiguration.getInstance().isDebugStaticCompletion()) - logger.diag("=== Completion finished"); + Logger.diag("=== Completion finished"); return output; } catch (Exception exception) { if (ExtensionConfiguration.getInstance().isDebugStaticCompletion()) - logger.diag("Caught " + exception.getClass().getName() + ", returning error list"); + Logger.diag("Caught " + exception.getClass().getName() + ", returning error list"); return Arrays.stream(new String[]{ "An error occurred" }).toList(); } } @@ -116,7 +109,7 @@ public final class StaticTabCompletion implements TabCompletion { List<@NotNull String> output = new ArrayList<>(); if (ExtensionConfiguration.getInstance().isDebugStaticCompletion()) - logger.diag("getCompletions(sender=" + sender.getName() + " alias=" + alias + " index=" + index + ")"); + Logger.diag("getCompletions(sender=" + sender.getName() + " alias=" + alias + " index=" + index + ")"); if (!completions.get(alias).containsKey(index)) return List.of(); @@ -126,7 +119,7 @@ public final class StaticTabCompletion implements TabCompletion { // Empty index, display player list if (ExtensionConfiguration.getInstance().isDebugStaticCompletion()) - logger.diag("Index is null, getting player list"); + Logger.diag("Index is null, getting player list"); for (Player player : Bukkit.getOnlinePlayers()) output.add(player.getName()); @@ -136,15 +129,15 @@ public final class StaticTabCompletion implements TabCompletion { // put these lines into Objects#requireNonNull so that the code is still fairly readable if (ExtensionConfiguration.getInstance().isDebugStaticCompletion()) - logger.diag("Index is map, getting arguments"); + Logger.diag("Index is map, getting arguments"); for (String argument : completions.get(alias).get(index).keySet()) if (sender.hasPermission(completions.get(alias).get(index).get(argument))) { if (ExtensionConfiguration.getInstance().isDebugStaticCompletion()) - logger.diag("Adding completion argument \"" + argument + "\""); + Logger.diag("Adding completion argument \"" + argument + "\""); output.add(argument); } else if (ExtensionConfiguration.getInstance().isDebugStaticCompletion()) - logger.diag("Permission is missing, skipping completion argument \"" + argument + "\""); + Logger.diag("Permission is missing, skipping completion argument \"" + argument + "\""); } return output; @@ -230,7 +223,7 @@ public final class StaticTabCompletion implements TabCompletion { */ private void createIndex(@NotNull String alias, @Range(from = -1, to = Integer.MAX_VALUE) int index) { if (ExtensionConfiguration.getInstance().isDebugStaticCompletion()) - logger.diag("createIndex(alias=" + alias + " index=" + index + ")"); + Logger.diag("createIndex(alias=" + alias + " index=" + index + ")"); try { if (completions.get(alias).get(index) == null) throw new IndexOutOfBoundsException(); diff --git a/extension/src/main/java/de/jeremystartm/pickshadow/extension/api/entity/player/PlayerData.java b/extension/src/main/java/de/jeremystartm/pickshadow/extension/api/entity/player/PlayerData.java index 7dea5ce..efa3d37 100644 --- a/extension/src/main/java/de/jeremystartm/pickshadow/extension/api/entity/player/PlayerData.java +++ b/extension/src/main/java/de/jeremystartm/pickshadow/extension/api/entity/player/PlayerData.java @@ -19,12 +19,11 @@ package de.jeremystartm.pickshadow.extension.api.entity.player; - import com.google.gson.Gson; import com.google.gson.JsonSyntaxException; import com.google.gson.reflect.TypeToken; -import de.jeremystartm.pickshadow.extension.misc.TabListHandler; -import de.staropensource.sosengine.base.logging.LoggerInstance; +import de.jeremystartm.pickshadow.extension.misc.Scheduler; +import de.staropensource.engine.base.logging.Logger; import fr.mrmicky.fastboard.adventure.FastBoard; import lombok.AccessLevel; import lombok.Getter; @@ -33,6 +32,7 @@ import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; @@ -45,19 +45,16 @@ import static java.util.Map.entry; @Getter @SuppressWarnings({ "JavadocDeclaration" }) public final class PlayerData { - /** - * Contains the logger instance for this instance. - * - * @since v1-release0 - */ - private final LoggerInstance logger; - /** * Contains the associated {@link Player}. + *

+ * Will be saved. * * @since v1-release0 * -- GETTER -- * Returns the associated {@link Player}. + *

+ * Will be saved. * * @return associated {@link Player} * @since v1-release0 @@ -67,15 +64,21 @@ public final class PlayerData { /** * Contains the player list scoreboard. + *

+ * Will not be saved. * * @since v1-release0 * -- GETTER -- * Returns the player list scoreboard. + *

+ * Will not be saved. * * @return player list scoreboard * @since v1-release0 * -- SETTER -- * Sets the player list scoreboard. + *

+ * Will not be saved. * * @param playerListScoreboard new player list scoreboard * @since v1-release0 @@ -83,20 +86,88 @@ public final class PlayerData { @ApiStatus.Experimental private @NotNull FastBoard playerListScoreboard; + /** + * Contains the preferred language + * the player has set. + *

+ * Will be saved. + * + * @since v1-release0 + * -- GETTER -- + * Returns the preferred language + * the player has set. + *

+ * Will be saved. + * + * @return preferred language + * @since v1-release0 + * -- SETTER -- + * Sets the preferred language + * the player has set. + *

+ * Will be saved. + * + * @param language new preferred language + * @since v1-release0 + */ + private String language; + /** * Contains when the player has * first been seen on the server. + *

+ * Will be saved. * * @since v1-release0 * -- GETTER -- * Returns when the player has * first been seen on the server. + *

+ * Will be saved. * * @return first played date and time * @since v1-release0 */ private ZonedDateTime firstSeen; + /** + * Contains the UUID of the player + * this player has messaged last. + *

+ * Will be saved. + * + * @since v1-release0 + * -- GETTER -- + * Returns the UUID of the player + * this player has messaged last. + *

+ * Will be saved. + * + * @return UUID of the player last messaged + * @since v1-release0 + */ + private UUID lastMessaged; + + /** + * Contains the state for + * stone cutter damage, as + * assigned by the {@link Scheduler}. + *

+ * Will not be saved. + * + * @since v1-release0 + * -- GETTER -- + * Returns the state for + * stone cutter damage, as + * assigned by the {@link Scheduler}. + *

+ * Will not be saved. + * + * @return stone cutter damage state + * @since v1-release0 + */ + private int stonecutterDamageState = -1; + /** * Creates and initializes an instance of this class. *

@@ -107,11 +178,13 @@ public final class PlayerData { * @since v1-release0 */ PlayerData(@NotNull Player player) { - this.player = player; - this.logger = new LoggerInstance.Builder().setClazz(getClass()).setOrigin("PSSE").setMetadata(player.getName()).build(); + Logger.info("Initializing fresh player data for player " + player.getName() + " [" + player.getUniqueId() + "]"); + this.player = player; //TabListHandler.getInstance().initializeTabList(this); + language = "en"; firstSeen = ZonedDateTime.now(); + lastMessaged = null; } /** @@ -124,12 +197,14 @@ public final class PlayerData { * @param firstSeen when the player was first seen * @since v1-release0 */ - private PlayerData(@NotNull Player player, @NotNull ZonedDateTime firstSeen) { - this.player = player; - this.logger = new LoggerInstance.Builder().setClazz(getClass()).setOrigin("PSSE").setMetadata(player.getName()).build(); + private PlayerData(@NotNull Player player, @NotNull String language, @NotNull ZonedDateTime firstSeen, @Nullable UUID lastMessaged) { + Logger.info("Initializing player data from existing data for player " + player.getName() + " [" + player.getUniqueId() + "]"); - TabListHandler.getInstance().initializeTabList(this); + this.player = player; + //TabListHandler.getInstance().initializeTabList(this); + this.language = language; this.firstSeen = firstSeen; + this.lastMessaged = lastMessaged; } /** @@ -152,7 +227,9 @@ public final class PlayerData { return new PlayerData( player, - ZonedDateTime.parse(data.get("firstPlayed")) + data.get("language"), + ZonedDateTime.parse(data.get("firstPlayed")), + data.get("lastMessaged").isBlank() ? null : UUID.fromString(data.get("lastMessaged")) ); } catch (Exception exception) { if (exception instanceof JsonSyntaxException) @@ -172,7 +249,9 @@ public final class PlayerData { return new Gson().toJson( Map.ofEntries( entry("playerUUID", player.getUniqueId().toString()), - entry("firstPlayed", firstSeen.format(DateTimeFormatter.ISO_ZONED_DATE_TIME)) + entry("language", language), + entry("firstPlayed", firstSeen.format(DateTimeFormatter.ISO_ZONED_DATE_TIME)), + entry("lastMessaged", lastMessaged == null ? "" : lastMessaged.toString()) ), new TypeToken>(){}.getType()); } diff --git a/extension/src/main/java/de/jeremystartm/pickshadow/extension/api/entity/player/PlayerDataFactory.java b/extension/src/main/java/de/jeremystartm/pickshadow/extension/api/entity/player/PlayerDataFactory.java index 6b60916..7c57f77 100644 --- a/extension/src/main/java/de/jeremystartm/pickshadow/extension/api/entity/player/PlayerDataFactory.java +++ b/extension/src/main/java/de/jeremystartm/pickshadow/extension/api/entity/player/PlayerDataFactory.java @@ -19,7 +19,7 @@ package de.jeremystartm.pickshadow.extension.api.entity.player; -import de.staropensource.sosengine.base.logging.LoggerInstance; +import de.staropensource.engine.base.logging.Logger; import lombok.Getter; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; @@ -29,7 +29,7 @@ import java.util.List; import java.util.UUID; /** - * A factory for {@link Player}s. + * A factory for {@link PlayerData} instances. * * @since v1-release0 */ @@ -48,13 +48,6 @@ public final class PlayerDataFactory { @Getter private static PlayerDataFactory instance; - /** - * Contains the logger instance of this instance. - * - * @since v1-release0 - */ - private final @NotNull LoggerInstance logger = new LoggerInstance.Builder().setClazz(getClass()).setOrigin("PSSE").build(); - /** * Contains all registered {@link PlayerData} instances. * @@ -136,12 +129,12 @@ public final class PlayerDataFactory { * @since v1-release0 */ public void registerPlayer(@NotNull Player player) throws Exception { - logger.verb("Registering player " + player.getName() + " (" + player.getUniqueId() + ")"); + Logger.verb("Registering player " + player.getName() + " (" + player.getUniqueId() + ")"); try { playerList.add(new PlayerData(player)); } catch (Exception exception) { - logger.crash("Unable to create PlayerData instance for player " + player.getName() + " (" + player.getUniqueId() + ")", exception, true); + Logger.crash("Unable to create PlayerData instance for player " + player.getName() + " (" + player.getUniqueId() + ")", exception, true); throw exception; } } @@ -153,7 +146,7 @@ public final class PlayerDataFactory { * @since v1-release0 */ public void unregisterPlayer(@NotNull PlayerData playerData) { - logger.verb("Unregistering player " + playerData.getPlayer().getName() + " (" + playerData.getPlayer().getUniqueId() + ")"); + Logger.verb("Unregistering player " + playerData.getPlayer().getName() + " (" + playerData.getPlayer().getUniqueId() + ")"); playerList.remove(playerData); } diff --git a/extension/src/main/java/de/jeremystartm/pickshadow/extension/api/translation/LanguageString.java b/extension/src/main/java/de/jeremystartm/pickshadow/extension/api/translation/LanguageString.java index 23706df..01d7429 100644 --- a/extension/src/main/java/de/jeremystartm/pickshadow/extension/api/translation/LanguageString.java +++ b/extension/src/main/java/de/jeremystartm/pickshadow/extension/api/translation/LanguageString.java @@ -43,6 +43,7 @@ public enum LanguageString { // Errors ERROR_UNKNOWN, ERROR_UNIMPLEMENTED, + ERROR_INVALID_MODE, ERROR_NOT_A_PLAYER, ERROR_MISSING_PERM, ERROR_TOO_FEW_ARGUMENTS, @@ -56,6 +57,7 @@ public enum LanguageString { CONNECTION_DISCONNECT, CONNECTION_ERROR_REGISTRATION, CONNECTION_ERROR_TABLISTHANDLER, + CONNECTION_ERROR_SCHEDULER, // Command /psse EXTENSIONCMD_GREETER, @@ -67,10 +69,13 @@ public enum LanguageString { // Command group 'messaging' MESSAGING_SERVER, MESSAGING_DIRECT, + MESSAGING_LASTCONTACT, MESSAGING_ERROR_SELF, + MESSAGING_ERROR_NOLASTCONTACT, // Command group 'links' LINKS_WEBSITE, + LINKS_FORUM, LINKS_DISCORD, LINKS_TEAMSPEAK, LINKS_MUMBLE, @@ -83,8 +88,9 @@ public enum LanguageString { CLEARCHAT_CRINGE, // Command /home + HOME, HOME_NORESPAWN, - HOME_TELEPORTED, + HOME_NORESPAWN_OTHER, // Command /toggledownfall TOGGLEDOWNFALL, @@ -99,6 +105,34 @@ public enum LanguageString { TROLL_HACK_COMPLETE, TROLL_HEAVENS, + // Command /language + LANGUAGE, + LANGUAGE_CURRENT, + LANGUAGE_INVALID, + + // Command /heal + HEAL, + HEAL_REMOTE, + + // Command /plugins + PLUGINS, + PLUGINS_FAKE, + PLUGINS_ENTRY_PREFIX, + PLUGINS_ENTRY_SUFFIX, + PLUGINS_ENTRY_NAME_PREFIX, + PLUGINS_ENTRY_NAME_SUFFIX, + + // Command /reload + RELOAD, + + // Command /gamemode + GAMEMODE, + GAMEMODE_REMOTE, + GAMEMODE_SURVIVAL, + GAMEMODE_CREATIVE, + GAMEMODE_ADVENTURE, + GAMEMODE_SPECTATOR, + // Event for chat commands CHATCOMMAND_ERROR_NAMESPACE, } diff --git a/extension/src/main/java/de/jeremystartm/pickshadow/extension/api/translation/TranslationManager.java b/extension/src/main/java/de/jeremystartm/pickshadow/extension/api/translation/TranslationManager.java index 2cfeff3..67dbc38 100644 --- a/extension/src/main/java/de/jeremystartm/pickshadow/extension/api/translation/TranslationManager.java +++ b/extension/src/main/java/de/jeremystartm/pickshadow/extension/api/translation/TranslationManager.java @@ -22,8 +22,10 @@ package de.jeremystartm.pickshadow.extension.api.translation; import com.google.gson.Gson; import com.google.gson.JsonSyntaxException; import com.google.gson.reflect.TypeToken; -import de.staropensource.sosengine.base.logging.LoggerInstance; +import de.jeremystartm.pickshadow.extension.api.entity.player.PlayerDataFactory; +import de.staropensource.engine.base.logging.Logger; import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -41,13 +43,6 @@ import java.util.Objects; * @since v1-release0 */ public final class TranslationManager { - /** - * Contains the logger instance for this class. - * - * @since v1-release0 - */ - private static final LoggerInstance LOGGER = new LoggerInstance.Builder().setClazz(TranslationManager.class).setOrigin("PSSE").build(); - /** * Contains all translations. * @@ -55,6 +50,16 @@ public final class TranslationManager { */ private static final @NotNull Map<@NotNull String, @NotNull Map<@NotNull LanguageString, @NotNull String>> translations = new HashMap<>(); + /** + * Returns an array of all loaded languages. + * + * @return array of language names + * @since v1-release0 + */ + public static @NotNull String @NotNull [] getLanguages() { + return translations.keySet().toArray(new String[0]); + } + /** * Returns the translation for the specified language * string for the specified {@link CommandSender}. @@ -65,18 +70,28 @@ public final class TranslationManager { * @param placeholders placeholders * @since v1-release0 */ + @SafeVarargs public static @NotNull String get(@NotNull LanguageString languageString, @NotNull CommandSender sender, boolean includePrefix, @NotNull Map.Entry<@NotNull String, @NotNull String> @Nullable ... placeholders) { String temp = "en"; + // Determine language to use + if (sender instanceof Player player) + try { + temp = PlayerDataFactory.getInstance().get(player).getLanguage(); + } catch (NullPointerException ignored) {} + + // Check if the prefix shall be included if (includePrefix) temp = get(LanguageString.PREFIX, "en") + get(languageString, temp).replace("\n", "\n" + get(LanguageString.PREFIX_NEWLINE, "en")); else temp = get(languageString, temp); + // Replace placeholders if (placeholders != null) for (Map.Entry<@NotNull String, @NotNull String> placeholder : placeholders) temp = temp.replace("%" + placeholder.getKey() + "%", placeholder.getValue()); + // Return return temp; } @@ -89,12 +104,19 @@ public final class TranslationManager { * @since v1-release0 */ public static @NotNull String get(@NotNull LanguageString languageString, @NotNull String language) { + // Used for displaying '!MISSING_TRANSLATION!' + // when a translation is missing in 'en', the + // default language if (language.equals("en")) return translations.get(language).getOrDefault(languageString, "!" + languageString.name() + "!"); + // If the translation key hasn't been translated + // in the specified language, request the + // translation using the standard language instead if (!(translations.containsKey(language) && translations.get(language).containsKey(languageString))) return get(languageString, "en"); + // Return translation string return translations.get(language).get(languageString); } @@ -104,21 +126,21 @@ public final class TranslationManager { * @since v1-release0 */ public static void loadTranslations() { - LOGGER.verb("Loading translations"); + Logger.verb("Loading translations"); Map<@NotNull String, @NotNull String> translationsLanguage; translations.clear(); for (String language : new String[]{ "en", "de" }) { - LOGGER.diag("Loading translations for language \"" + language + "\""); + Logger.diag("Loading translations for language \"" + language + "\""); translations.put(language, new HashMap<>()); // Load, parse and store translations try (InputStream stream = TranslationManager.class.getClassLoader().getResourceAsStream("translations/" + language.toLowerCase(Locale.ROOT) + ".json")) { translationsLanguage = new Gson().fromJson(new String(Objects.requireNonNull(stream).readAllBytes(), StandardCharsets.UTF_8), new TypeToken>(){}.getType()); } catch (IOException | NullPointerException exception) { - LOGGER.crash("Failed loading translations for language \"" + language + "\": IOException occurred", exception); + Logger.crash("Failed loading translations for language \"" + language + "\": IOException occurred", exception); return; } catch (JsonSyntaxException exception) { - LOGGER.crash("Failed loading translations for language \"" + language + "\": JSON Syntax does not match Map", exception); + Logger.crash("Failed loading translations for language \"" + language + "\": JSON Syntax does not match Map", exception); return; } @@ -134,7 +156,7 @@ public final class TranslationManager { * @since v1-release0 */ public static void processTranslations() { - LOGGER.verb("Processing translations"); + Logger.verb("Processing translations"); String tagBranding = get(LanguageString.TAG_BRANDING, "en"); String tagGenericOpen = get(LanguageString.TAG_GENERIC_OPEN, "en"); @@ -147,7 +169,7 @@ public final class TranslationManager { String tagLinkClose = get(LanguageString.TAG_LINK_CLOSE, "en"); for (String language : translations.keySet()) { - LOGGER.diag("Processing translations for language \"" + language + "\""); + Logger.diag("Processing translations for language \"" + language + "\""); for (LanguageString languageString : translations.get(language).keySet()) translations.get(language).put( diff --git a/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/TemplateCommand.java b/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/TemplateCommand.java new file mode 100644 index 0000000..b439367 --- /dev/null +++ b/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/TemplateCommand.java @@ -0,0 +1,74 @@ +/* + * PICKSHADOW SERVER KIT SOURCE FILE + * Copyright (c) 2024 The PickShadow Server Kit authors + * Licensed under the GNU Affero General Public License v3 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.jeremystartm.pickshadow.extension.command; + +import de.jeremystartm.pickshadow.extension.api.command.CommandBase; +import de.jeremystartm.pickshadow.extension.api.command.TabCompletion; +import de.jeremystartm.pickshadow.extension.api.command.completion.StaticTabCompletion; +import de.jeremystartm.pickshadow.extension.api.translation.LanguageString; +import de.jeremystartm.pickshadow.extension.api.translation.TranslationManager; +import lombok.Getter; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; + +/** + * Handles the {@code /cmd} command. + * + * @since v1-release0 + */ +@Getter +@SuppressWarnings({ "JavadocDeclaration" }) +public final class TemplateCommand extends CommandBase { + /** + * Contains the tab completion for this command. + * + * @since v1-release0 + * -- GETTER -- + * Returns the tab completion for this command. + * + * @return tab completion + * @since v1-release0 + */ + private final @NotNull TabCompletion completion = new StaticTabCompletion() + .add("cmd", -1, "--verbose", "pickshadow.command.cmd.verbose") + .add("cmd", 0, "arg1", "pickshadow.command.cmd") + .add("cmd", 0, "arg2", "pickshadow.command.cmd.arg2") + .add("cmd", 0, "arg3", "pickshadow.command.cmd.arg3") + .copy("cmd", "cmd2"); + + /** + * Creates and initializes an instance of + * this class and registers this command. + * + * @since v1-release0 + */ + public TemplateCommand() throws IllegalArgumentException { + super("mode", "cmd"); + } + + /** {@inheritDoc} */ + @Override + public void invoke(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] arguments) { + if (checkPermission(sender, "pickshadow.command.cmd")) return; + + sender.sendRichMessage(TranslationManager.get(LanguageString.ERROR_UNIMPLEMENTED, sender, true)); + } +} diff --git a/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/AnnounceCommand.java b/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/AnnounceCommand.java index 0c3d24d..c14d682 100644 --- a/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/AnnounceCommand.java +++ b/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/AnnounceCommand.java @@ -24,7 +24,7 @@ import de.jeremystartm.pickshadow.extension.api.command.completion.StaticTabComp import de.jeremystartm.pickshadow.extension.api.command.TabCompletion; import de.jeremystartm.pickshadow.extension.api.translation.LanguageString; import de.jeremystartm.pickshadow.extension.api.translation.TranslationManager; -import de.staropensource.sosengine.base.utility.PlaceholderEngine; +import de.staropensource.engine.base.utility.PlaceholderEngine; import lombok.Getter; import net.kyori.adventure.text.minimessage.MiniMessage; import org.bukkit.Bukkit; @@ -51,7 +51,8 @@ public final class AnnounceCommand extends CommandBase { * @since v1-release0 */ private final @NotNull TabCompletion completion = new StaticTabCompletion() - .add("", -1, "--network", "pickshadow.command.announce.network"); + .add("", -1, "--network", "pickshadow.command.announce.network") + .add("", -1, "--noprefix", "pickshadow.command.announce.network"); /** * Creates and initializes an instance of @@ -60,7 +61,7 @@ public final class AnnounceCommand extends CommandBase { * @since v1-release0 */ public AnnounceCommand() throws IllegalArgumentException { - super("announce"); + super("general", "announce"); } /** {@inheritDoc} */ @@ -73,26 +74,38 @@ public final class AnnounceCommand extends CommandBase { return; } - StringBuilder argumentsString = new StringBuilder(); + StringBuilder argumentsBuilder = new StringBuilder(); + String argumentsFinalized; boolean networkwide = false; + boolean noprefix = false; // Convert 'arguments' into a String for (String argument : arguments) { if (argument.equals("--network")) { networkwide = true; continue; + } else if (argument.equals("--noprefix")) { + noprefix = true; + continue; } - if (!argumentsString.isEmpty()) - argumentsString.append(" "); + if (!argumentsBuilder.isEmpty()) + argumentsBuilder.append(" "); - argumentsString.append(argument); + argumentsBuilder.append(argument); } + argumentsFinalized = argumentsBuilder.toString(); + + if (noprefix) + argumentsFinalized = argumentsFinalized.replace("\\n", "\n"); + else + argumentsFinalized = TranslationManager.get(LanguageString.PREFIX, sender, false) + argumentsFinalized.replace("\\n", "\n" + TranslationManager.get(LanguageString.PREFIX_NEWLINE, sender, false)); + if (networkwide) { if (!checkPermission(sender, "pickshadow.command.announce.network")) sender.sendRichMessage(TranslationManager.get(LanguageString.ERROR_UNIMPLEMENTED, sender, true)); } else - Bukkit.broadcast(MiniMessage.miniMessage().deserialize(PlaceholderEngine.getInstance().process(argumentsString.toString().replace("\\n", "\n")))); + Bukkit.broadcast(MiniMessage.miniMessage().deserialize(PlaceholderEngine.getInstance().process(argumentsFinalized))); } } diff --git a/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/ClearChatCommand.java b/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/ClearChatCommand.java index 2170830..77733ef 100644 --- a/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/ClearChatCommand.java +++ b/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/ClearChatCommand.java @@ -68,7 +68,7 @@ public final class ClearChatCommand extends CommandBase { * @since v1-release0 */ public ClearChatCommand() throws IllegalArgumentException { - super("clearchat"); + super("general", "clearchat"); } /** {@inheritDoc} */ diff --git a/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/ExtensionCommand.java b/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/ExtensionCommand.java index a06d0b4..cb0cfc6 100644 --- a/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/ExtensionCommand.java +++ b/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/ExtensionCommand.java @@ -19,7 +19,7 @@ package de.jeremystartm.pickshadow.extension.command.general; -import de.jeremystartm.pickshadow.extension.ExtensionInformation; +import de.jeremystartm.pickshadow.common.PSSKInformation; import de.jeremystartm.pickshadow.extension.api.command.CommandBase; import de.jeremystartm.pickshadow.extension.api.command.completion.StaticTabCompletion; import de.jeremystartm.pickshadow.extension.api.command.TabCompletion; @@ -67,30 +67,40 @@ public final class ExtensionCommand extends CommandBase { * @since v1-release0 */ public ExtensionCommand() throws IllegalArgumentException { - super("psse"); + super("general", "psse", "pssp"); } /** {@inheritDoc} */ @Override public void invoke(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] arguments) { - if (checkPermission(sender, "pickshadow.command.extension")) return; - switch (alias) { case "psse", "pickshadow", "server" -> { + if (checkPermission(sender, "pickshadow.command.extension")) return; + if (arguments.length == 0) - sender.sendRichMessage( - TranslationManager.get(LanguageString.EXTENSIONCMD_GREETER, - sender, - true, - entry("codename", ExtensionInformation.getVersioningCodename()), - entry("version", ExtensionInformation.getVersioningString()), - entry("commit", ExtensionInformation.getGitCommitIdentifierShort()), - entry("%dirty%", String.valueOf(ExtensionInformation.isGitDirty())) - )); + sender.sendRichMessage(TranslationManager.get(LanguageString.EXTENSIONCMD_GREETER, + sender, + true, + entry("codename", PSSKInformation.getVersioningCodename()), + entry("version", PSSKInformation.getVersioningString()), + entry("commit", PSSKInformation.getGitCommitIdentifierShort()), + entry("dirty", String.valueOf(PSSKInformation.isGitDirty())) + )); else { switch (arguments[0]) { - case "license" -> sender.sendRichMessage(TranslationManager.get(LanguageString.EXTENSIONCMD_LICENSE, sender, true)); - case "source" -> sender.sendRichMessage(TranslationManager.get(LanguageString.EXTENSIONCMD_SOURCE, sender, true, entry("source", "https://git.staropensource.de/JeremyStarTM/PSSE"))); + case "license" -> sender.sendRichMessage(TranslationManager.get( + LanguageString.EXTENSIONCMD_LICENSE, + sender, + true, + entry("license", "GNU Affero General Public License v3"), + entry("license_url", "https://gnu.org/licenses/agpl-3.0.en.html") + )); + case "source" -> sender.sendRichMessage(TranslationManager.get( + LanguageString.EXTENSIONCMD_SOURCE, + sender, + true, + entry("source", "https://git.staropensource.de/JeremyStarTM/PSSE") + )); case "killjvm" -> { if (checkPermission(sender, "pickshadow.command.extension.advanced")) return; @@ -111,7 +121,10 @@ public final class ExtensionCommand extends CommandBase { Runtime.getRuntime().halt(0); } } - case "pssp" -> sender.sendRichMessage(TranslationManager.get(LanguageString.EXTENSIONCMD_ERROR_OLDCMD, sender, true)); + case "pssp" -> { + if (!checkPermission(sender, "pickshadow.command.extension.legacy")) + sender.sendRichMessage(TranslationManager.get(LanguageString.EXTENSIONCMD_ERROR_OLDCMD, sender, true)); + } } } } diff --git a/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/LanguageCommand.java b/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/LanguageCommand.java new file mode 100644 index 0000000..3280be9 --- /dev/null +++ b/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/LanguageCommand.java @@ -0,0 +1,119 @@ +/* + * PICKSHADOW SERVER KIT SOURCE FILE + * Copyright (c) 2024 The PickShadow Server Kit authors + * Licensed under the GNU Affero General Public License v3 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.jeremystartm.pickshadow.extension.command.general; + +import de.jeremystartm.pickshadow.extension.api.command.CommandBase; +import de.jeremystartm.pickshadow.extension.api.command.TabCompletion; +import de.jeremystartm.pickshadow.extension.api.command.completion.StaticTabCompletion; +import de.jeremystartm.pickshadow.extension.api.entity.player.PlayerDataFactory; +import de.jeremystartm.pickshadow.extension.api.translation.LanguageString; +import de.jeremystartm.pickshadow.extension.api.translation.TranslationManager; +import lombok.Getter; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; + +import static java.util.Map.entry; + +/** + * Handles the {@code /language} command. + * + * @since v1-release0 + */ +@Getter +@SuppressWarnings({ "JavadocDeclaration" }) +public final class LanguageCommand extends CommandBase { + /** + * Contains the tab completion for this command. + * + * @since v1-release0 + * -- GETTER -- + * Returns the tab completion for this command. + * + * @return tab completion + * @since v1-release0 + */ + private final @NotNull TabCompletion completion; + + /** + * Creates and initializes an instance of + * this class and registers this command. + * + * @since v1-release0 + */ + public LanguageCommand() throws IllegalArgumentException { + super("general", "language"); + + // Initialize completion + StaticTabCompletion staticTabCompletion = new StaticTabCompletion(); + for (String language : TranslationManager.getLanguages()) + staticTabCompletion.add("language", 0, language, "pickshadow.command.language"); + + staticTabCompletion + .copy("language", "lang") + .copy("language", "setlanguage") + .copy("language", "setlang"); + + completion = staticTabCompletion; + } + + /** {@inheritDoc} */ + @Override + public void invoke(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] arguments) { + if (checkPermission(sender, "pickshadow.command.language")) return; + + if (sender instanceof Player player) { + // Check size of 'arguments' + if (arguments.length == 0) + sender.sendRichMessage(TranslationManager.get( + LanguageString.LANGUAGE_CURRENT, + sender, + true, + entry("language", PlayerDataFactory.getInstance().get(player).getLanguage()) + )); + else if (arguments.length == 1) {// Check if language is loaded + if (!Arrays.asList(TranslationManager.getLanguages()).contains(arguments[0])) { + sender.sendRichMessage(TranslationManager.get( + LanguageString.LANGUAGE_INVALID, + sender, + true, + entry("language", arguments[0]) + )); + } + + // Change language + PlayerDataFactory.getInstance().get(player).setLanguage(arguments[0]); + + // Send success message + sender.sendRichMessage(TranslationManager.get( + LanguageString.LANGUAGE, + sender, + true, + entry("language", arguments[0]) + )); + } else + sender.sendRichMessage(TranslationManager.get(LanguageString.ERROR_TOO_MANY_ARGUMENTS, sender, true)); + } else + sender.sendRichMessage(TranslationManager.get(LanguageString.ERROR_NOT_A_PLAYER, sender, true)); + } +} diff --git a/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/LinkCommand.java b/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/LinkCommand.java index 341ccc3..cd0a967 100644 --- a/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/LinkCommand.java +++ b/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/LinkCommand.java @@ -56,7 +56,7 @@ public final class LinkCommand extends CommandBase { * @since v1-release0 */ public LinkCommand() throws IllegalArgumentException { - super("discord", "teamspeak", "mumble", "website"); + super("general", "website", "forum", "discord", "teamspeak", "mumble"); } /** {@inheritDoc} */ @@ -66,6 +66,7 @@ public final class LinkCommand extends CommandBase { switch (alias) { case "website" -> sender.sendRichMessage(TranslationManager.get(LanguageString.LINKS_WEBSITE, sender, true)); + case "forum", "messageboard", "mb" -> sender.sendRichMessage(TranslationManager.get(LanguageString.LINKS_FORUM, sender, true)); case "discord", "dc" -> sender.sendRichMessage(TranslationManager.get(LanguageString.LINKS_DISCORD, sender, true)); case "teamspeak", "ts" -> sender.sendRichMessage(TranslationManager.get(LanguageString.LINKS_TEAMSPEAK, sender, true)); case "mumble" -> sender.sendRichMessage(TranslationManager.get(LanguageString.LINKS_MUMBLE, sender, true)); diff --git a/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/TrollCommand.java b/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/TrollCommand.java index 688bcd5..1aa2703 100644 --- a/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/TrollCommand.java +++ b/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/TrollCommand.java @@ -78,7 +78,7 @@ public final class TrollCommand extends CommandBase { * @since v1-release0 */ public TrollCommand() throws IllegalArgumentException { - super("troll"); + super("general", "troll"); } /** {@inheritDoc} */ diff --git a/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/replacement/GamemodeCommand.java b/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/replacement/GamemodeCommand.java new file mode 100644 index 0000000..22659f1 --- /dev/null +++ b/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/replacement/GamemodeCommand.java @@ -0,0 +1,183 @@ +/* + * PICKSHADOW SERVER KIT SOURCE FILE + * Copyright (c) 2024 The PickShadow Server Kit authors + * Licensed under the GNU Affero General Public License v3 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.jeremystartm.pickshadow.extension.command.general.replacement; + +import de.jeremystartm.pickshadow.extension.api.command.CommandBase; +import de.jeremystartm.pickshadow.extension.api.command.TabCompletion; +import de.jeremystartm.pickshadow.extension.api.command.completion.StaticTabCompletion; +import de.jeremystartm.pickshadow.extension.api.translation.LanguageString; +import de.jeremystartm.pickshadow.extension.api.translation.TranslationManager; +import lombok.Getter; +import org.bukkit.Bukkit; +import org.bukkit.GameMode; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +import static java.util.Map.entry; + +/** + * Handles the {@code /cmd} command. + * + * @since v1-release0 + */ +@Getter +@SuppressWarnings({ "JavadocDeclaration" }) +public final class GamemodeCommand extends CommandBase { + /** + * Contains the tab completion for this command. + * + * @since v1-release0 + * -- GETTER -- + * Returns the tab completion for this command. + * + * @return tab completion + * @since v1-release0 + */ + private final @NotNull TabCompletion completion = new StaticTabCompletion() + .add("gamemode", 0, "0", "pickshadow.command.gamemode.survival") + .add("gamemode", 0, "1", "pickshadow.command.gamemode.creative") + .add("gamemode", 0, "2", "pickshadow.command.gamemode.adventure") + .add("gamemode", 0, "3", "pickshadow.command.gamemode.spectator") + .add("gamemode", 0, "survival", "pickshadow.command.gamemode.survival") + .add("gamemode", 0, "creative", "pickshadow.command.gamemode.creative") + .add("gamemode", 0, "adventure", "pickshadow.command.gamemode.adventure") + .add("gamemode", 0, "spectator", "pickshadow.command.gamemode.spectator") + .players("gamemode", 1) + .copy("gamemode", "gm"); + + /** + * Creates and initializes an instance of + * this class and registers this command. + * + * @since v1-release0 + */ + public GamemodeCommand() throws IllegalArgumentException { + super("general", "gamemode"); + } + + /** {@inheritDoc} */ + @Override + public void invoke(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] arguments) { + if (checkPermission(sender, "pickshadow.command.gamemode")) return; + + // Check length of 'arguments' + if (arguments.length == 0) { + sender.sendRichMessage(TranslationManager.get(LanguageString.ERROR_TOO_FEW_ARGUMENTS, sender, true)); + return; + } else if (arguments.length > 2) { + sender.sendRichMessage(TranslationManager.get(LanguageString.ERROR_TOO_MANY_ARGUMENTS, sender, true)); + return; + } + + boolean onSelf; + GameMode gamemode; + LanguageString gamemodeTranslation; + Player target; + + // Determine gamemode + switch (arguments[0]) { + case "0", "survival" -> { + gamemode = GameMode.SURVIVAL; + gamemodeTranslation = LanguageString.GAMEMODE_SURVIVAL; + } + case "1", "creative" -> { + gamemode = GameMode.CREATIVE; + gamemodeTranslation = LanguageString.GAMEMODE_CREATIVE; + } + case "2", "adventure" -> { + gamemode = GameMode.ADVENTURE; + gamemodeTranslation = LanguageString.GAMEMODE_ADVENTURE; + } + case "3", "spectator" -> { + gamemode = GameMode.SPECTATOR; + gamemodeTranslation = LanguageString.GAMEMODE_SPECTATOR; + } + default -> { + sender.sendRichMessage(TranslationManager.get( + LanguageString.ERROR_INVALID_ARGUMENT, + sender, + true, + entry("argument", arguments[0]) + )); + return; + } + } + + // Get player + switch (arguments.length) { + case 1 -> { + onSelf = true; + + // Check if command sender is a player + if (sender instanceof Player senderPlayer) + target = senderPlayer; + else { + sender.sendRichMessage(TranslationManager.get(LanguageString.ERROR_NOT_A_PLAYER, sender, true)); + return; + } + } + case 2 -> { + target = Bukkit.getPlayer(arguments[1]); + try { + onSelf = Objects.requireNonNull(target).getName().equals(sender.getName()); + } catch (NullPointerException exception) { + onSelf = false; + } + + // Check if 'target' is null + if (target == null) { + sender.sendRichMessage(TranslationManager.get(LanguageString.ERROR_PLAYER_NOT_FOUND, + sender, + true, + entry("player", arguments[1]) + )); + return; + } + } + default -> { + sender.sendRichMessage(TranslationManager.get(LanguageString.ERROR_TOO_MANY_ARGUMENTS, sender, true)); + return; + } + } + + // Set game mode + target.setGameMode(gamemode); + + // Send message + target.sendRichMessage(TranslationManager.get( + LanguageString.GAMEMODE, + target, + true, + entry("gamemode", TranslationManager.get(gamemodeTranslation, target, false)) + )); + if (!onSelf) + sender.sendRichMessage(TranslationManager.get( + LanguageString.GAMEMODE_REMOTE, + sender, + true, + entry("player", target.getName()), + entry("gamemode", TranslationManager.get(gamemodeTranslation, target, false)) + )); + } +} diff --git a/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/replacement/HelpCommand.java b/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/replacement/HelpCommand.java new file mode 100644 index 0000000..4f24bde --- /dev/null +++ b/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/replacement/HelpCommand.java @@ -0,0 +1,69 @@ +/* + * PICKSHADOW SERVER KIT SOURCE FILE + * Copyright (c) 2024 The PickShadow Server Kit authors + * Licensed under the GNU Affero General Public License v3 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.jeremystartm.pickshadow.extension.command.general.replacement; + +import de.jeremystartm.pickshadow.extension.api.command.CommandBase; +import de.jeremystartm.pickshadow.extension.api.command.TabCompletion; +import de.jeremystartm.pickshadow.extension.api.command.completion.StubTabCompletion; +import de.jeremystartm.pickshadow.extension.api.translation.LanguageString; +import de.jeremystartm.pickshadow.extension.api.translation.TranslationManager; +import lombok.Getter; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; + +/** + * Handles the {@code /help} command. + * + * @since v1-release0 + */ +@Getter +@SuppressWarnings({ "JavadocDeclaration" }) +public final class HelpCommand extends CommandBase { + /** + * Contains the tab completion for this command. + * + * @since v1-release0 + * -- GETTER -- + * Returns the tab completion for this command. + * + * @return tab completion + * @since v1-release0 + */ + private final @NotNull TabCompletion completion = StubTabCompletion.completion(); + + /** + * Creates and initializes an instance of + * this class and registers this command. + * + * @since v1-release0 + */ + public HelpCommand() throws IllegalArgumentException { + super("general", "help"); + } + + /** {@inheritDoc} */ + @Override + public void invoke(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] arguments) { + if (checkPermission(sender, "pickshadow.command.help")) return; + + sender.sendRichMessage(TranslationManager.get(LanguageString.ERROR_UNIMPLEMENTED, sender, true)); + } +} diff --git a/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/MessageCommand.java b/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/replacement/MessageCommand.java similarity index 71% rename from extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/MessageCommand.java rename to extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/replacement/MessageCommand.java index ca24a2a..ce09652 100644 --- a/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/MessageCommand.java +++ b/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/replacement/MessageCommand.java @@ -17,11 +17,12 @@ * along with this program. If not, see . */ -package de.jeremystartm.pickshadow.extension.command.general; +package de.jeremystartm.pickshadow.extension.command.general.replacement; import de.jeremystartm.pickshadow.extension.api.command.CommandBase; import de.jeremystartm.pickshadow.extension.api.command.completion.StaticTabCompletion; import de.jeremystartm.pickshadow.extension.api.command.TabCompletion; +import de.jeremystartm.pickshadow.extension.api.entity.player.PlayerDataFactory; import de.jeremystartm.pickshadow.extension.api.translation.LanguageString; import de.jeremystartm.pickshadow.extension.api.translation.TranslationManager; import lombok.Getter; @@ -31,6 +32,8 @@ import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; +import java.util.UUID; + import static java.util.Map.entry; /** @@ -64,12 +67,14 @@ public final class MessageCommand extends CommandBase { * @since v1-release0 */ public MessageCommand() throws IllegalArgumentException { - super("message", "reply"); + super("general", "message", "reply"); } /** {@inheritDoc} */ @Override public void invoke(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] arguments) { + if (checkPermission(sender, "pickshadow.command.message")) return; + if (sender instanceof Player player) switch (alias) { case "message", "msg", "whisper", "w" -> { @@ -90,7 +95,7 @@ public final class MessageCommand extends CommandBase { } // Check if the sender is the receiver - if (player.getUniqueId() == ((Player) sender).getUniqueId()) { + if (player.getUniqueId().equals(receiver.getUniqueId())) { player.sendRichMessage(TranslationManager.get(LanguageString.MESSAGING_ERROR_SELF, sender, true)); return; } @@ -115,8 +120,33 @@ public final class MessageCommand extends CommandBase { // Send message player.sendRichMessage(output); receiver.sendRichMessage(output); + + // Mark receiver as last contact + PlayerDataFactory.getInstance().get(player).setLastMessaged(receiver.getUniqueId()); + } + case "reply", "r" -> { + UUID lastMessaged = PlayerDataFactory.getInstance().get(player).getLastMessaged(); + + if (lastMessaged == null) + player.sendRichMessage(TranslationManager.get(LanguageString.MESSAGING_ERROR_NOLASTCONTACT, sender, true)); + else { + String lastMessagedUsername = Bukkit.getOfflinePlayer(lastMessaged).getName(); + + if (lastMessagedUsername == null) + player.sendRichMessage(TranslationManager.get(LanguageString.MESSAGING_ERROR_NOLASTCONTACT, sender, true)); + else { + if (arguments.length == 0) + player.sendRichMessage( + TranslationManager.get(LanguageString.MESSAGING_LASTCONTACT, + sender, + true, + entry("contact", lastMessagedUsername) + )); + else + player.performCommand("message " + lastMessagedUsername + " " + String.join(" ", arguments)); + } + } } - case "reply", "r" -> sender.sendRichMessage(TranslationManager.get(LanguageString.ERROR_UNIMPLEMENTED, sender, true)); } else sender.sendRichMessage(TranslationManager.get(LanguageString.ERROR_NOT_A_PLAYER, sender, true)); diff --git a/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/replacement/PluginsCommand.java b/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/replacement/PluginsCommand.java new file mode 100644 index 0000000..8a6768e --- /dev/null +++ b/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/replacement/PluginsCommand.java @@ -0,0 +1,94 @@ +/* + * PICKSHADOW SERVER KIT SOURCE FILE + * Copyright (c) 2024 The PickShadow Server Kit authors + * Licensed under the GNU Affero General Public License v3 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.jeremystartm.pickshadow.extension.command.general.replacement; + +import de.jeremystartm.pickshadow.extension.api.command.CommandBaseWithNull; +import de.jeremystartm.pickshadow.extension.api.translation.LanguageString; +import de.jeremystartm.pickshadow.extension.api.translation.TranslationManager; +import lombok.Getter; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import static java.util.Map.entry; + +/** + * Handles the {@code /plugins} command. + * + * @since v1-release0 + */ +@Getter +public final class PluginsCommand extends CommandBaseWithNull { + /** + * Creates and initializes an instance of + * this class and registers this command. + * + * @since v1-release0 + */ + public PluginsCommand() {} + + /** {@inheritDoc} */ + @Override + public void invoke(@NotNull CommandSender sender, @Nullable Command command, @NotNull String alias, @NotNull String[] arguments) { + if (sender.hasPermission("pickshadow.command.plugins.real")) { + StringBuilder output = new StringBuilder(); + Plugin[] plugins = Bukkit.getPluginManager().getPlugins(); + String prefixNewline = TranslationManager.get(LanguageString.PREFIX_NEWLINE, sender, false); + String entryPrefix = TranslationManager.get(LanguageString.PLUGINS_ENTRY_PREFIX, sender, false); + String entrySuffix = TranslationManager.get(LanguageString.PLUGINS_ENTRY_SUFFIX, sender, false); + String entryNamePrefix = TranslationManager.get(LanguageString.PLUGINS_ENTRY_NAME_PREFIX, sender, false); + String entryNameSuffix = TranslationManager.get(LanguageString.PLUGINS_ENTRY_NAME_SUFFIX, sender, false); + + // Append header + output.append(TranslationManager.get( + LanguageString.PLUGINS, + sender, + true, + entry("count", String.valueOf(plugins.length)) + )); + + // Append plugin list + for (Plugin plugin : plugins) + output + .append("\n") + .append(prefixNewline) + .append(entryPrefix) + .append(entryNamePrefix) + .append(plugin.getName()) + .append(entryNameSuffix) + .append(" (") + .append(plugin.isEnabled() ? "enabled": "disabled") + .append(plugin.isNaggable() ? "": ", was nagged") + .append(")") + .append(entrySuffix) + ; + + sender.sendRichMessage(output.toString()); + } else if (!checkPermission(sender, "pickshadow.command.plugins")) + sender.sendRichMessage(TranslationManager.get( + LanguageString.PLUGINS, + sender, + true + )); + } +} diff --git a/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/replacement/ReloadCommand.java b/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/replacement/ReloadCommand.java new file mode 100644 index 0000000..89b7e76 --- /dev/null +++ b/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/replacement/ReloadCommand.java @@ -0,0 +1,53 @@ +/* + * PICKSHADOW SERVER KIT SOURCE FILE + * Copyright (c) 2024 The PickShadow Server Kit authors + * Licensed under the GNU Affero General Public License v3 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.jeremystartm.pickshadow.extension.command.general.replacement; + +import de.jeremystartm.pickshadow.extension.api.command.CommandBaseWithNull; +import de.jeremystartm.pickshadow.extension.api.translation.LanguageString; +import de.jeremystartm.pickshadow.extension.api.translation.TranslationManager; +import lombok.Getter; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Handles the {@code /reload} command. + * + * @since v1-release0 + */ +@Getter +public final class ReloadCommand extends CommandBaseWithNull { + /** + * Creates and initializes an instance of + * this class and registers this command. + * + * @since v1-release0 + */ + public ReloadCommand() {} + + /** {@inheritDoc} */ + @Override + public void invoke(@NotNull CommandSender sender, @Nullable Command command, @NotNull String alias, @NotNull String[] arguments) { + if (checkPermission(sender, "pickshadow.command.reload")) return; + + sender.sendRichMessage(TranslationManager.get(LanguageString.RELOAD, sender, true)); + } +} diff --git a/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/replacement/package-info.java b/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/replacement/package-info.java new file mode 100644 index 0000000..b1dbb97 --- /dev/null +++ b/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/replacement/package-info.java @@ -0,0 +1,27 @@ +/* + * PICKSHADOW SERVER KIT SOURCE FILE + * Copyright (c) 2024 The PickShadow Server Kit authors + * Licensed under the GNU Affero General Public License v3 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +/** + * Replacement commands. + *

+ * These replace various Minecraft or Bukkit commands. + * + * @since v1-release0 + */ +package de.jeremystartm.pickshadow.extension.command.general.replacement; diff --git a/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/survival/HomeCommand.java b/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/survival/HomeCommand.java index cb0abeb..4114086 100644 --- a/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/survival/HomeCommand.java +++ b/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/survival/HomeCommand.java @@ -25,11 +25,16 @@ import de.jeremystartm.pickshadow.extension.api.command.TabCompletion; import de.jeremystartm.pickshadow.extension.api.translation.LanguageString; import de.jeremystartm.pickshadow.extension.api.translation.TranslationManager; import lombok.Getter; +import org.bukkit.Bukkit; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; +import java.util.Objects; + +import static java.util.Map.entry; + /** * Handles the {@code /home} command. * @@ -57,7 +62,7 @@ public final class HomeCommand extends CommandBase { * @since v1-release0 */ public HomeCommand() throws IllegalArgumentException { - super("home"); + super("survival", "home"); } /** {@inheritDoc} */ @@ -65,14 +70,61 @@ public final class HomeCommand extends CommandBase { public void invoke(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] arguments) { if (checkPermission(sender, "pickshadow.command.home")) return; - if (sender instanceof Player player) { - if (player.getRespawnLocation() == null) - player.sendRichMessage(TranslationManager.get(LanguageString.HOME_NORESPAWN, player, true)); - else { - player.teleport(player.getRespawnLocation()); - player.setFallDistance(0f); - player.sendRichMessage(TranslationManager.get(LanguageString.HOME_TELEPORTED, player, true)); + boolean onSelf; + Player player; + + // Get player + switch (arguments.length) { + case 0 -> { + onSelf = true; + + // Check if command sender is a player + if (sender instanceof Player senderPlayer) + player = senderPlayer; + else { + sender.sendRichMessage(TranslationManager.get(LanguageString.ERROR_NOT_A_PLAYER, sender, true)); + return; + } } + case 1 -> { + player = Bukkit.getPlayer(arguments[0]); + try { + onSelf = !Objects.requireNonNull(player).getName().equals(sender.getName()); + } catch (NullPointerException exception) { + onSelf = false; + } + + // Check if 'player' is null + if (player == null) { + sender.sendRichMessage(TranslationManager.get(LanguageString.ERROR_PLAYER_NOT_FOUND, + sender, + true, + entry("player", arguments[0]) + )); + return; + } + } + default -> { + sender.sendRichMessage(TranslationManager.get(LanguageString.ERROR_TOO_MANY_ARGUMENTS, sender, true)); + return; + } + } + + // Teleport player + if (player.getRespawnLocation() == null) + if (onSelf) + sender.sendRichMessage(TranslationManager.get(LanguageString.HOME_NORESPAWN, sender, true)); + else + sender.sendRichMessage(TranslationManager.get( + LanguageString.HOME_NORESPAWN_OTHER, + sender, + true, + entry("player", player.getName()) + )); + else { + player.teleport(player.getRespawnLocation()); + player.setFallDistance(0f); + sender.sendRichMessage(TranslationManager.get(LanguageString.HOME, sender, true)); } } } diff --git a/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/survival/package-info.java b/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/survival/package-info.java index 9c67d8a..6867889 100644 --- a/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/survival/package-info.java +++ b/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/survival/package-info.java @@ -18,7 +18,8 @@ */ /** - * Commands used by survival servers. + * Commands most useful when + * playing in survival mode. * * @since v1-release0 */ diff --git a/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/survivalcheat/FeedCommand.java b/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/survivalcheat/FeedCommand.java new file mode 100644 index 0000000..60dc41b --- /dev/null +++ b/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/survivalcheat/FeedCommand.java @@ -0,0 +1,128 @@ +/* + * PICKSHADOW SERVER KIT SOURCE FILE + * Copyright (c) 2024 The PickShadow Server Kit authors + * Licensed under the GNU Affero General Public License v3 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.jeremystartm.pickshadow.extension.command.survivalcheat; + +import de.jeremystartm.pickshadow.extension.api.command.CommandBase; +import de.jeremystartm.pickshadow.extension.api.command.TabCompletion; +import de.jeremystartm.pickshadow.extension.api.command.completion.StaticTabCompletion; +import de.jeremystartm.pickshadow.extension.api.translation.LanguageString; +import de.jeremystartm.pickshadow.extension.api.translation.TranslationManager; +import lombok.Getter; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +import static java.util.Map.entry; + +/** + * Handles the {@code /feed} command. + * + * @since v1-release0 + */ +@Getter +@SuppressWarnings({ "JavadocDeclaration" }) +public final class FeedCommand extends CommandBase { + /** + * Contains the tab completion for this command. + * + * @since v1-release0 + * -- GETTER -- + * Returns the tab completion for this command. + * + * @return tab completion + * @since v1-release0 + */ + private final @NotNull TabCompletion completion = new StaticTabCompletion() + .players("feed", 0); + + /** + * Creates and initializes an instance of + * this class and registers this command. + * + * @since v1-release0 + */ + public FeedCommand() throws IllegalArgumentException { + super("survivalcheat", "feed"); + } + + /** {@inheritDoc} */ + @Override + public void invoke(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] arguments) { + if (checkPermission(sender, "pickshadow.command.feed")) return; + + Player player; + + boolean onSelf; + + // Get player + switch (arguments.length) { + case 0 -> { + onSelf = true; + + // Check if command sender is a player + if (sender instanceof Player senderPlayer) + player = senderPlayer; + else { + sender.sendRichMessage(TranslationManager.get(LanguageString.ERROR_NOT_A_PLAYER, sender, true)); + return; + } + } + case 1 -> { + player = Bukkit.getPlayer(arguments[0]); + try { + onSelf = Objects.requireNonNull(player).getName().equals(sender.getName()); + } catch (NullPointerException exception) { + onSelf = false; + } + + // Check if 'player' is null + if (player == null) { + sender.sendRichMessage(TranslationManager.get(LanguageString.ERROR_PLAYER_NOT_FOUND, + sender, + true, + entry("player", arguments[0]) + )); + return; + } + } + default -> { + sender.sendRichMessage(TranslationManager.get(LanguageString.ERROR_TOO_MANY_ARGUMENTS, sender, true)); + return; + } + } + + // Heal player + player.setFoodLevel(20); + + // Send success message + player.sendRichMessage(TranslationManager.get(LanguageString.HEAL, player, true)); + if (!onSelf) + sender.sendRichMessage(TranslationManager.get( + LanguageString.HEAL_REMOTE, + sender, + true, + entry("player", player.getName()) + )); + } +} diff --git a/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/survivalcheat/HealCommand.java b/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/survivalcheat/HealCommand.java new file mode 100644 index 0000000..235845d --- /dev/null +++ b/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/survivalcheat/HealCommand.java @@ -0,0 +1,153 @@ +/* + * PICKSHADOW SERVER KIT SOURCE FILE + * Copyright (c) 2024 The PickShadow Server Kit authors + * Licensed under the GNU Affero General Public License v3 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.jeremystartm.pickshadow.extension.command.survivalcheat; + +import de.jeremystartm.pickshadow.extension.BuildOptions; +import de.jeremystartm.pickshadow.extension.api.command.CommandBase; +import de.jeremystartm.pickshadow.extension.api.command.TabCompletion; +import de.jeremystartm.pickshadow.extension.api.command.completion.StaticTabCompletion; +import de.jeremystartm.pickshadow.extension.api.translation.LanguageString; +import de.jeremystartm.pickshadow.extension.api.translation.TranslationManager; +import lombok.Getter; +import org.bukkit.Bukkit; +import org.bukkit.attribute.Attribute; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.event.entity.EntityRegainHealthEvent; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +import static java.util.Map.entry; + +/** + * Handles the {@code /heal} command. + * + * @since v1-release0 + */ +@Getter +@SuppressWarnings({ "JavadocDeclaration" }) +public final class HealCommand extends CommandBase { + /** + * Contains the tab completion for this command. + * + * @since v1-release0 + * -- GETTER -- + * Returns the tab completion for this command. + * + * @return tab completion + * @since v1-release0 + */ + private final @NotNull TabCompletion completion = new StaticTabCompletion() + .players("heal", 0) + .add("heal", -1, "--keep-effects", "pickshadow.command.heal") + .add("heal", -1, "--all-effects", "pickshadow.command.heal"); + + /** + * Creates and initializes an instance of + * this class and registers this command. + * + * @since v1-release0 + */ + public HealCommand() throws IllegalArgumentException { + super("survivalcheat", "heal"); + } + + /** {@inheritDoc} */ + @Override + public void invoke(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] arguments) { + if (checkPermission(sender, "pickshadow.command.heal")) return; + + Player player; + List<@NotNull String> argumentsList = new ArrayList<>(Arrays.stream(arguments).toList()); + + boolean onSelf; + boolean keepEffects = argumentsList.remove("--keep-effects"); + boolean allEffects = argumentsList.remove("--all-effects"); + + // Get player + switch (argumentsList.size()) { + case 0 -> { + onSelf = true; + + // Check if command sender is a player + if (sender instanceof Player senderPlayer) + player = senderPlayer; + else { + sender.sendRichMessage(TranslationManager.get(LanguageString.ERROR_NOT_A_PLAYER, sender, true)); + return; + } + } + case 1 -> { + player = Bukkit.getPlayer(argumentsList.getFirst()); + try { + onSelf = Objects.requireNonNull(player).getName().equals(sender.getName()); + } catch (NullPointerException exception) { + onSelf = false; + } + + // Check if 'player' is null + if (player == null) { + sender.sendRichMessage(TranslationManager.get(LanguageString.ERROR_PLAYER_NOT_FOUND, + sender, + true, + entry("player", argumentsList.getFirst()) + )); + return; + } + } + default -> { + sender.sendRichMessage(TranslationManager.get(LanguageString.ERROR_TOO_MANY_ARGUMENTS, sender, true)); + return; + } + } + + // Heal player + player.heal(Objects.requireNonNull(player.getAttribute(Attribute.MAX_HEALTH)).getValue() - player.getHealth(), EntityRegainHealthEvent.RegainReason.MAGIC); + player.setFoodLevel(20); + player.setSaturation(20); + player.setRemainingAir(300); + + // Reset bad or all effects + if (!keepEffects) + if (allEffects) + for (PotionEffect effect : player.getActivePotionEffects()) + player.removePotionEffect(effect.getType()); + else + for (PotionEffectType effectType : BuildOptions.SETTINGS_EFFECTS_DAMAGING) + player.removePotionEffect(effectType); + + // Send success message + player.sendRichMessage(TranslationManager.get(LanguageString.HEAL, player, true)); + if (!onSelf) + sender.sendRichMessage(TranslationManager.get( + LanguageString.HEAL_REMOTE, + sender, + true, + entry("player", player.getName()) + )); + } +} diff --git a/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/ToggleDownfallCommand.java b/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/survivalcheat/ToggleDownfallCommand.java similarity index 96% rename from extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/ToggleDownfallCommand.java rename to extension/src/main/java/de/jeremystartm/pickshadow/extension/command/survivalcheat/ToggleDownfallCommand.java index fe074e5..5ba83af 100644 --- a/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/general/ToggleDownfallCommand.java +++ b/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/survivalcheat/ToggleDownfallCommand.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package de.jeremystartm.pickshadow.extension.command.general; +package de.jeremystartm.pickshadow.extension.command.survivalcheat; import de.jeremystartm.pickshadow.extension.api.command.CommandBase; import de.jeremystartm.pickshadow.extension.api.command.completion.StubTabCompletion; @@ -61,7 +61,7 @@ public final class ToggleDownfallCommand extends CommandBase { * @since v1-release0 */ public ToggleDownfallCommand() throws IllegalArgumentException { - super("toggledownfall"); + super("survivalcheat", "toggledownfall"); } /** {@inheritDoc} */ diff --git a/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/survivalcheat/package-info.java b/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/survivalcheat/package-info.java new file mode 100644 index 0000000..3cea88c --- /dev/null +++ b/extension/src/main/java/de/jeremystartm/pickshadow/extension/command/survivalcheat/package-info.java @@ -0,0 +1,27 @@ +/* + * PICKSHADOW SERVER KIT SOURCE FILE + * Copyright (c) 2024 The PickShadow Server Kit authors + * Licensed under the GNU Affero General Public License v3 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + + +/** + * Commands which are generally used + * for cheating in survival mode. + * + * @since v1-release0 + */ +package de.jeremystartm.pickshadow.extension.command.survivalcheat; diff --git a/extension/src/main/java/de/jeremystartm/pickshadow/extension/listener/ChatListener.java b/extension/src/main/java/de/jeremystartm/pickshadow/extension/listener/ChatListener.java index 5182aee..36aa359 100644 --- a/extension/src/main/java/de/jeremystartm/pickshadow/extension/listener/ChatListener.java +++ b/extension/src/main/java/de/jeremystartm/pickshadow/extension/listener/ChatListener.java @@ -19,8 +19,13 @@ package de.jeremystartm.pickshadow.extension.listener; +import de.jeremystartm.pickshadow.extension.BuildOptions; +import de.jeremystartm.pickshadow.extension.api.command.CommandBaseWithNull; import de.jeremystartm.pickshadow.extension.api.translation.LanguageString; import de.jeremystartm.pickshadow.extension.api.translation.TranslationManager; +import de.jeremystartm.pickshadow.extension.command.general.replacement.PluginsCommand; +import de.jeremystartm.pickshadow.extension.command.general.replacement.ReloadCommand; +import de.staropensource.engine.base.logging.Logger; import io.papermc.paper.event.player.AsyncChatEvent; import net.kyori.adventure.text.minimessage.MiniMessage; import org.bukkit.Bukkit; @@ -30,12 +35,29 @@ import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerCommandPreprocessEvent; import org.jetbrains.annotations.NotNull; +import java.util.Arrays; + /** * Listens on chat events. * * @since v1-release0 */ public final class ChatListener implements Listener { + /** + * Contains a static instance of + * the {@link PluginsCommand} class. + * + * @since v1-release0 + */ + private static final @NotNull PluginsCommand pluginsCommand = new PluginsCommand(); + /** + * Contains a static instance of + * the {@link ReloadCommand} class. + * + * @since v1-release0 + */ + private static final @NotNull ReloadCommand reloadCommand = new ReloadCommand(); + /** * Handles chat messages. * @@ -45,12 +67,14 @@ public final class ChatListener implements Listener { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void handleChatMessage(@NotNull AsyncChatEvent event) { event.setCancelled(true); - Bukkit.broadcast( - MiniMessage.miniMessage().deserialize( - TranslationManager.get(LanguageString.MESSAGING_SERVER, event.getPlayer(), false) - .replace("%sender%", event.getPlayer().getName()) - ).append(MiniMessage.miniMessage().deserialize(MiniMessage.miniMessage().serialize(event.originalMessage()).replace("\\<", "<"))) - ); + + if (!BuildOptions.SMALLSTUFF_CHAT_COMMENTS || !MiniMessage.miniMessage().serialize(event.originalMessage()).startsWith("#")) + Bukkit.broadcast( + MiniMessage.miniMessage().deserialize( + TranslationManager.get(LanguageString.MESSAGING_SERVER, event.getPlayer(), false) + .replace("%sender%", event.getPlayer().getName()) + ).append(MiniMessage.miniMessage().deserialize(MiniMessage.miniMessage().serialize(event.originalMessage()).replace("\\<", "<"))) + ); } /** @@ -61,9 +85,35 @@ public final class ChatListener implements Listener { */ @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) public void handleChatCommand(@NotNull PlayerCommandPreprocessEvent event) { - if (event.getMessage().split(" ")[0].contains(":")) { - event.setCancelled(true); - event.getPlayer().sendRichMessage(TranslationManager.get(LanguageString.CHATCOMMAND_ERROR_NAMESPACE, event.getPlayer(), true)); + try { + String[] arguments = event.getMessage().split(" "); + String command = arguments[0]; + + if (command.contains(":")) { + event.setCancelled(true); + event.getPlayer().sendRichMessage(TranslationManager.get(LanguageString.CHATCOMMAND_ERROR_NAMESPACE, event.getPlayer(), true)); + } + + // Override commands + forceOverrideCommand(event, arguments, pluginsCommand, "plugins", "pl"); + forceOverrideCommand(event, arguments, reloadCommand, "reload", "rl"); + } catch (Exception exception) { + event.getPlayer().sendRichMessage(TranslationManager.get(LanguageString.ERROR_UNKNOWN, event.getPlayer(), true)); + Logger.crash("Failed to handle command:\n" + event.getMessage(), exception, false); } } + + /** + * Forcefully overrides a command. + * + * @param event event information + * @since v1-release0 + */ + private static void forceOverrideCommand(@NotNull PlayerCommandPreprocessEvent event, @NotNull String @NotNull [] arguments, @NotNull CommandBaseWithNull commandBase, @NotNull String... commands) { + for (String command : commands) + if (arguments[0].equals("/" + command)) { + event.setCancelled(true); + commandBase.invoke(event.getPlayer(), null, command.substring(1), Arrays.copyOf(arguments, 1)); + } + } } diff --git a/extension/src/main/java/de/jeremystartm/pickshadow/extension/listener/ConnectionListener.java b/extension/src/main/java/de/jeremystartm/pickshadow/extension/listener/ConnectionListener.java index 2456c8a..b9bf97d 100644 --- a/extension/src/main/java/de/jeremystartm/pickshadow/extension/listener/ConnectionListener.java +++ b/extension/src/main/java/de/jeremystartm/pickshadow/extension/listener/ConnectionListener.java @@ -19,11 +19,14 @@ package de.jeremystartm.pickshadow.extension.listener; +import de.jeremystartm.pickshadow.extension.BuildOptions; import de.jeremystartm.pickshadow.extension.Extension; import de.jeremystartm.pickshadow.extension.api.entity.player.PlayerDataFactory; import de.jeremystartm.pickshadow.extension.api.translation.LanguageString; import de.jeremystartm.pickshadow.extension.api.translation.TranslationManager; import de.jeremystartm.pickshadow.extension.command.general.ClearChatCommand; +import de.jeremystartm.pickshadow.extension.misc.Scheduler; +import net.kyori.adventure.text.Component; import net.kyori.adventure.text.minimessage.MiniMessage; import org.bukkit.Bukkit; import org.bukkit.entity.Player; @@ -55,13 +58,26 @@ public final class ConnectionListener implements Listener { // Register player PlayerDataFactory.getInstance().registerPlayer(event.getPlayer()); - // Set join message - event.joinMessage(MiniMessage.miniMessage().deserialize(TranslationManager.get( - LanguageString.CONNECTION_JOIN, - event.getPlayer(), - false, - entry("player", event.getPlayer().getName()) - ))); + // Reset fall distance + // This effectively reintroduces MC-212, + // which was fixed in 24w45a. + if (BuildOptions.UNFIX_FALLDAMAGE_CANCELLING) + event.getPlayer().setFallDistance(0f); + + // Launch scheduler + Scheduler.player(event.getPlayer()); + + // Empty join message + event.joinMessage(Component.empty()); + + // Broadcast our own join message + for (Player player : Bukkit.getOnlinePlayers()) + player.sendRichMessage(TranslationManager.get( + LanguageString.CONNECTION_JOIN, + player, + false, + entry("player", event.getPlayer().getName()) + )); // Schedule late join event event.getPlayer().getScheduler().execute(Extension.getInstance(), () -> handlePlayerJoinLate(event.getPlayer()), null, 1); @@ -92,16 +108,21 @@ public final class ConnectionListener implements Listener { * @since v1-release0 */ @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - private void handlePlayerJoin(@NotNull PlayerQuitEvent event) { + private void handlePlayerLeave(@NotNull PlayerQuitEvent event) { // Unregister player PlayerDataFactory.getInstance().unregisterPlayer(PlayerDataFactory.getInstance().get(event.getPlayer())); - // Set leave message - event.quitMessage(MiniMessage.miniMessage().deserialize(TranslationManager.get( - LanguageString.CONNECTION_DISCONNECT, - event.getPlayer(), - false, - entry("player", event.getPlayer().getName()) - ))); + // Empty quit message + event.quitMessage(Component.empty()); + + // Broadcast our own quit message + for (Player player : Bukkit.getOnlinePlayers()) + if (!player.getUniqueId().equals(event.getPlayer().getUniqueId())) + player.sendRichMessage(TranslationManager.get( + LanguageString.CONNECTION_DISCONNECT, + player, + false, + entry("player", event.getPlayer().getName()) + )); } } diff --git a/extension/src/main/java/de/jeremystartm/pickshadow/extension/misc/BukkitLoggingAdapter.java b/extension/src/main/java/de/jeremystartm/pickshadow/extension/misc/BukkitLoggingAdapter.java new file mode 100644 index 0000000..2ac0baf --- /dev/null +++ b/extension/src/main/java/de/jeremystartm/pickshadow/extension/misc/BukkitLoggingAdapter.java @@ -0,0 +1,54 @@ +/* + * STAROPENSOURCE ENGINEMC SOURCE FILE + * Copyright (c) 2024 The StarOpenSource EngineMC Authors + * Licensed under the GNU Affero General Public License v3 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.jeremystartm.pickshadow.extension.misc; + +import de.jeremystartm.pickshadow.extension.Extension; +import de.staropensource.engine.base.EngineConfiguration; +import de.staropensource.engine.base.implementable.LoggingAdapter; +import de.staropensource.engine.base.implementation.shortcode.EmptyShortcodeParser; +import de.staropensource.engine.base.type.logging.LogLevel; +import org.jetbrains.annotations.NotNull; + +/** + * EngineMC's {@code BukkitLoggingAdapter} class. + * + * @since v1-release0 + */ +public final class BukkitLoggingAdapter implements LoggingAdapter { + /** + * Constructs this class. + * + * @since v1-alpha0 + */ + public BukkitLoggingAdapter() {} + + /** {@inheritDoc} */ + @Override + public void print(@NotNull LogLevel level, @NotNull StackTraceElement issuer, @NotNull String message, @NotNull String format) { + // Remove any tags + if (EngineConfiguration.getInstance() != null && EngineConfiguration.getInstance().getLogFeatures().contains("formatting")) + format = new EmptyShortcodeParser(format, true).getClean(); + + switch (level) { + case DIAGNOSTIC, VERBOSE, SILENT_WARNING, INFORMATIONAL, WARNING -> Extension.getInstance().getLogger().info(format.replace("\n", "\n ")); + case ERROR, CRASH -> Extension.getInstance().getLogger().severe(format.replace("\n", "\n ")); + } + } +} diff --git a/extension/src/main/java/de/jeremystartm/pickshadow/extension/misc/Scheduler.java b/extension/src/main/java/de/jeremystartm/pickshadow/extension/misc/Scheduler.java new file mode 100644 index 0000000..a40ac4b --- /dev/null +++ b/extension/src/main/java/de/jeremystartm/pickshadow/extension/misc/Scheduler.java @@ -0,0 +1,109 @@ +/* + * PICKSHADOW SERVER KIT SOURCE FILE + * Copyright (c) 2024 The PickShadow Server Kit authors + * Licensed under the GNU Affero General Public License v3 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.jeremystartm.pickshadow.extension.misc; + +import de.jeremystartm.pickshadow.extension.BuildOptions; +import de.jeremystartm.pickshadow.extension.Extension; +import de.jeremystartm.pickshadow.extension.api.entity.player.PlayerData; +import de.jeremystartm.pickshadow.extension.api.entity.player.PlayerDataFactory; +import de.jeremystartm.pickshadow.extension.api.translation.LanguageString; +import de.jeremystartm.pickshadow.extension.api.translation.TranslationManager; +import de.staropensource.engine.base.logging.Logger; +import io.papermc.paper.threadedregions.scheduler.ScheduledTask; +import net.kyori.adventure.text.minimessage.MiniMessage; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +/** + * Responsible for periodically executing tasks. + * + * @since v1-release0 + */ +public final class Scheduler { + /** + * Creates and initializes an + * instance of this class. + * + * @since v1-release0 + */ + private Scheduler() {} + + /** + * Invokes scheduler actions for the entire server. + * + * @param scheduledTask information about the scheduled task + * @since v1-release0 + */ + public static void server(@NotNull ScheduledTask scheduledTask) { + for (World world : Bukkit.getServer().getWorlds()) + world(world); + } + + /** + * Invokes scheduler actions for a world. + * + * @since v1-release0 + */ + public static void world(@NotNull World world) { + + } + + /** + * Invokes scheduler actions for a specified player. + * + * @param player player to invoke scheduler actions on + * @since v1-release0 + */ + public static void player(@NotNull Player player) { + try { + PlayerData data = PlayerDataFactory.getInstance().get(player); + + // Apply stonecutter damage + if (BuildOptions.SMALLSTUFF_STONECUTTER_DAMAGE) + switch (data.getStonecutterDamageState()) { + case -1 -> { + Location location = player.getLocation().clone(); + location.setX((long) location.getX() - 1); + location.setY(((long) location.getY())); + location.setZ((long) location.getZ()); + + if (player.getWorld().getBlockAt(location).getType() == Material.STONECUTTER) { + data.setStonecutterDamageState(0); + player.damage(1); + } + } + case 11 -> data.setStonecutterDamageState(-1); + default -> data.setStonecutterDamageState(data.getStonecutterDamageState() + 1); + } + + // Reschedule + player.getScheduler().execute(Extension.getInstance(), () -> player(player), null, 1L); + } catch (Exception exception) { + if (player.isOnline()) + player.kick(MiniMessage.miniMessage().deserialize(TranslationManager.get(LanguageString.CONNECTION_ERROR_SCHEDULER, player, false))); + + Logger.crash("Player scheduler failed for " + player.getName() + " [" + player.getUniqueId() + "]", exception, false); + } + } +} diff --git a/extension/src/main/java/de/jeremystartm/pickshadow/extension/misc/TabListHandler.java b/extension/src/main/java/de/jeremystartm/pickshadow/extension/misc/TabListHandler.java index 1797f4a..b78aa47 100644 --- a/extension/src/main/java/de/jeremystartm/pickshadow/extension/misc/TabListHandler.java +++ b/extension/src/main/java/de/jeremystartm/pickshadow/extension/misc/TabListHandler.java @@ -21,7 +21,7 @@ package de.jeremystartm.pickshadow.extension.misc; import de.jeremystartm.pickshadow.extension.Extension; import de.jeremystartm.pickshadow.extension.api.entity.player.PlayerData; -import de.staropensource.sosengine.base.logging.LoggerInstance; +import de.staropensource.engine.base.logging.Logger; import fr.mrmicky.fastboard.adventure.FastBoard; import net.kyori.adventure.text.minimessage.MiniMessage; import lombok.Getter; @@ -55,13 +55,6 @@ public final class TabListHandler { @Getter private static TabListHandler instance = null; - /** - * Contains the logger instance for this class. - * - * @since v1-release0 - */ - private final LoggerInstance logger = new LoggerInstance.Builder().setClazz(getClass()).setOrigin("PSSE").build(); - /** * Contains the content of the tab list. * Each entry represents one line. @@ -101,7 +94,7 @@ public final class TabListHandler { * @since v1-release0 */ public void initializeTabList(@NotNull PlayerData playerData) { - logger.verb("Initializing player list for player " + playerData.getPlayer().getName() + " (" + playerData.getPlayer().getUniqueId() + ")"); + Logger.verb("Initializing player list for player " + playerData.getPlayer().getName() + " (" + playerData.getPlayer().getUniqueId() + ")"); // Initialize FastBoard FastBoard board = new FastBoard(playerData.getPlayer()); @@ -127,10 +120,10 @@ public final class TabListHandler { if (justSchedule) return; - logger.verb("Updating player list for player " + playerData.getPlayer().getName() + " (" + playerData.getPlayer().getUniqueId() + ")"); + Logger.verb("Updating player list for player " + playerData.getPlayer().getName() + " (" + playerData.getPlayer().getUniqueId() + ")"); for (int line = 0; line < content.size(); line++) { - logger.verb("Processing line " + line + " for player " + playerData.getPlayer().getName() + " (" + playerData.getPlayer().getUniqueId() + ")"); + Logger.verb("Processing line " + line + " for player " + playerData.getPlayer().getName() + " (" + playerData.getPlayer().getUniqueId() + ")"); playerData.getPlayerListScoreboard().updateLine(line, MiniMessage.miniMessage().deserialize(content.get(line))); } } diff --git a/extension/src/main/resources/plugin.yml b/extension/src/main/resources/plugin.yml index c41b238..d9303ac 100644 --- a/extension/src/main/resources/plugin.yml +++ b/extension/src/main/resources/plugin.yml @@ -12,6 +12,8 @@ depend: - "LuckPerms" commands: + # General + # -> AnnounceCommand announce: description: "Announces a message and broadcasts it to all players on the subserver or the network." usage: "/announce [--network] " @@ -20,12 +22,14 @@ commands: - broadcast - bcast - bc + # -> ClearChatCommand clearchat: description: "Clears the chat for all players on the subserver or the entire network." usage: "/clearchat [--network]" aliases: - chatclear - cc + # -> ExtensionCommand psse: description: "Interface for players and administrators to PSSE and the PickShadow network." usage: "/psse " @@ -35,31 +39,27 @@ commands: pssp: description: "Notifies you that PSSP is no more." usage: "/pssp" - home: - description: "Teleports you to your respawn point." - usage: "/home" + # -> LanguageCommand + language: + description: "Allows you to change the language of PSSE" + usage: "/language " aliases: - - bed - - tphome - message: - description: "Messages another player." - usage: "/message " - aliases: - - msg - - whisper - - w - reply: - description: "Messages the last messaged player." - usage: "/reply " - aliases: - - reply - - r + - lang + - setlanguage + - setlang + # -> LinkCommand website: description: "Displays the link to PickShadow's website." usage: "/website" aliases: - web - www + forum: + description: "Displays the link to PickShadow's forum." + usage: "/forum" + aliases: + - messageboard + - mb discord: description: "Displays the link to PickShadow's Discord server." usage: "/discord" @@ -74,11 +74,176 @@ commands: description: "Displays the IP address to PickShadow's Mumble server." usage: "/mumble" aliases: [] - toggledownfall: - description: "A PSSE provided command." - usage: "/toggledownfall" - aliases: [] + # -> TrollCommand troll: description: "Various torturing methods for misbehaving players ;)" usage: "/troll " aliases: [] + + # General (replacements) + # -> GamemodeCommand + gamemode: + description: "Changes the gamemode of the specified player." + usage: "/gamemode [player]" + aliases: + - gm + # -> HelpCommand + help: + description: "Provides information about the server." + usage: "/help" + aliases: + - h + # -> MessageCommand + message: + description: "Messages another player." + usage: "/message " + aliases: + - msg + - whisper + - w + reply: + description: "Messages the last messaged player." + usage: "/reply " + aliases: + - reply + - r + # -> PluginsCommand + plugins: + description: "Lists all installed plugins." + usage: "/plugins" + aliases: + - pl + # -> ReloadCommand + reload: + description: "Warns about server reloads." + usage: "/reload" + aliases: + - rl + + + # Survival + # -> HomeCommand + home: + description: "Teleports you to your respawn point." + usage: "/home" + aliases: + - bed + - tphome + - tph + + + # Survival (Cheating) + # -> FeedCommand + feed: + description: "Feeds the specified player." + usage: "/feed [player]" + aliases: [] + # -> HealCommand + heal: + description: "Heals the specified player." + usage: "/heal [player]" + aliases: [] + # -> ToggleDownfallCommand + toggledownfall: + description: "A PSSE provided command." + usage: "/toggledownfall" + aliases: [] + +permissions: + # Commands + # -> General + # --> AnnounceCommand + pickshadow.command.announce: + description: "Provides access to the '/announce' command." + default: false + pickshadow.command.announce.network: + description: "Provides access to '/announce''s '--network' option." + default: false + # --> ClearChatCommand + pickshadow.command.clearchat: + description: "Provides access to the '/clearchat' command." + default: false + pickshadow.command.clearchat.network: + description: "Provides access to '/clearchat''s '--network' option." + default: false + # --> ExtensionCommand + pickshadow.command.extension: + description: "Provides access to the '/psse' command" + default: false + pickshadow.command.extension.advanced: + description: "Provides access to administrative operations." + default: false + pickshadow.command.extension.legacy: + description: "Provides access to the '/psse' command only." + default: false + # --> LanguageCommand + pickshadow.command.language: + description: "Provides access to the '/language' command." + default: false + # --> LinkCommand + pickshadow.command.link: + description: "Provides access to multiple commands returning hyperlinks." + default: false + # --> TrollCommand + pickshadow.command.troll: + description: "Provides access to the '/troll' command." + default: false + + # -> General (replacements) + # --> GamemodeCommand + pickshadow.command.gamemode: + description: "Provides access to the '/gamemode' command." + default: false + pickshadow.command.gamemode.survival: + description: "Provides access to '/gamemode''s 'survival' option." + default: false + pickshadow.command.gamemode.creative: + description: "Provides access to '/gamemode''s 'creative' option." + default: false + pickshadow.command.gamemode.adventure: + description: "Provides access to '/gamemode''s 'adventure' option." + default: false + pickshadow.command.gamemode.spectator: + description: "Provides access to '/gamemode''s 'spectator' option." + default: false + # --> HelpCommand + pickshadow.command.help: + description: "Provides access to the '/help' command." + default: false + # --> MessageCommand + pickshadow.command.message: + description: "Provides access to the '/message' command." + default: false + # --> PluginsCommand + pickshadow.command.plugins: + description: "Provides access to the fake '/plugins' command." + default: false + pickshadow.command.plugins.real: + description: "Provides access to the real '/plugins' command." + default: false + # --> ReloadCommand + pickshadow.command.reload: + description: "Provides access to the '/reload' command." + default: false + + + # -> Survival + # --> HomeCommand + pickshadow.command.home: + description: "Provides access to the '/home' command." + default: false + + + # -> Survival (Cheating) + # --> FeedCommand + pickshadow.command.feed: + description: "Provides access to the '/feed' command." + default: false + # --> HealCommand + pickshadow.command.heal: + description: "Provides access to the '/heal' command." + default: false + # --> ToggleDownfallCommand + pickshadow.command.toggledownfall: + description: "Provides access to the '/toggledownfall' command :3" + default: false diff --git a/extension/src/main/resources/translations/de.json b/extension/src/main/resources/translations/de.json index 1cd59eb..5873479 100644 --- a/extension/src/main/resources/translations/de.json +++ b/extension/src/main/resources/translations/de.json @@ -1,6 +1,7 @@ { "ERROR_UNKNOWN": "Ein unbekannter Fehler ist aufgetreten. Bitte kontaktiere das Serverteam.", "ERROR_UNIMPLEMENTED": "Diese Aktion wurde noch nicht implementiert.", + "ERROR_INVALID_MODE": "Diese Aktion ist nicht verfügbar, da sie deaktiviert wurde.", "ERROR_NOT_A_PLAYER": "Ein Spieler ist für diese Aktion benötigt.", "ERROR_MISSING_PERM": "Dir ist es nicht erlaubt, diese Aktion durchzuführen (%permission% fehlt).", "ERROR_TOO_FEW_ARGUMENTS": "Zu wenige Argumente.", @@ -11,27 +12,34 @@ "CONNECTION_JOIN": "%player% ist dem Spiel beigetreten", "CONNECTION_JOIN_WELCOME": "Willkommen zu , %player%!", "CONNECTION_DISCONNECT": "%player% hat das Spiel verlassen", + "CONNECTION_ERROR_REGISTRATION": "Anmeldung konnte nicht verarbeitet werden", + "CONNECTION_ERROR_TABLISTHANDLER": "Der TabListHandler schlug unerwartet fehl", + "CONNECTION_ERROR_SCHEDULER": "Der Player Scheduler schlug unerwartet fehl", - "EXTENSIONCMD_GREETER": "Dieser Subserver läuft <#d60532>PSSE \"%codename%\" @ %version% (%commit%, dirty %dirty%)\nFühre ein paar Subbefehle aus um mehr Informationen einzusehen.", - "EXTENSIONCMD_KILLJVM": "Bye bye!", - "EXTENSIONCMD_LICENSE": "PSSE ist lizensiert unter der GNU Affero General Public-Lizenz v3.", - "EXTENSIONCMD_SOURCE": "Du kannst PickShadow's serverseitigen Code auf sos!git finden.", + "EXTENSIONCMD_GREETER": "Dieser Subserver läuft <#d60532>PSSE \"%codename%\" @ %version% (%commit%, dirty %dirty%)\nFühre ein paar Subbefehle aus um mehr Informationen einzusehen.", + "EXTENSIONCMD_KILLJVM": "Bye bye!", + "EXTENSIONCMD_LICENSE": "PSSE ist lizensiert unter der %license%.", + "EXTENSIONCMD_SOURCE": "Du kannst PickShadow's serverseitigen Code auf sos!git finden.", "EXTENSIONCMD_ERROR_OLDCMD": "Was ist PSSP...?\nDas PickShadow Server Plugin (PSSP) wurde in PickShadow Server Extension (PSSE) umbenannt.\nBitte verwende diesen Namen stattdessen, vielen Dank.", + "MESSAGING_LASTCONTACT": "Die letzte Person, die du angeschrieben hast ist %contact%.", "MESSAGING_ERROR_SELF": "Du kannst dich nicht selber anschreiben.", + "MESSAGING_ERROR_NOLASTCONTACT": "Du hast in der letzten Zeit keinen angeschrieben.", "LINKS_WEBSITE": "Das PickShadow Netzwerk hat aktuell keine Webseite.", + "LINKS_FORUM": "Das PickShadow Netzwerk hat aktuell kein Forum.", "LINKS_DISCORD": "Das PickShadow Netzwerk hat aktuell keine Discord Guilde.", "LINKS_TEAMSPEAK": "Das PickShadow Netzwerk hat aktuell keinen TeamSpeak Server.", "LINKS_MUMBLE": "Das PickShadow Netzwerk hat aktuell keinen Mumble Server.", - "CLEARCHAT_GENERIC": "Der Chat wurde von %sender% geleert.", - "CLEARCHAT_VOID": "Der Chat wurde von %sender% ins Void geschubst.", - "CLEARCHAT_SKILLISSUES": "Der Chat wurde von %sender% aufgrund von Skill Issues der Chatteilnehmer gelöscht.", - "CLEARCHAT_BRAINDAMAGE": "Um weitverbreitenden Gehirnschaden zu verhinden, hat %sender% den Chat geleert.", - "CLEARCHAT_CRINGE": "Aufgrund von Massen an Cringe musste %sender% den Chat leeren.", + "CLEARCHAT_GENERIC": "Der Chat wurde von %sender% geleert.", + "CLEARCHAT_VOID": "Der Chat wurde von %sender% ins Void geschubst.", + "CLEARCHAT_SKILLISSUES": "Der Chat wurde von %sender% aufgrund von Skill Issues der Chatteilnehmer gelöscht.", + "CLEARCHAT_BRAINDAMAGE": "Um weitverbreitenden Gehirnschaden zu verhinden, hat %sender% den Chat geleert.", + "CLEARCHAT_CRINGE": "Aufgrund von Massen an Cringe musste %sender% den Chat leeren.", "HOME_NORESPAWN": "Du hast keinen gültigen Respawnpunkt.\nStelle sicher, dass dein Bett oder Respawn Anker nicht abgebaut oder behindert ist.", + "HOME_NORESPAWN_OTHER": "%player% hat keinen gültigen Respawnpunkt.\n%player%'s Bett oder Respawn Anker wurde entweder abgebaut oder ist behindert.", "TOGGLEDOWNFALL": "Niederschlag umgestellt", @@ -44,5 +52,24 @@ "TROLL_HACK_COMPLETE": "Zugang gewährt.", "TROLL_HEAVENS": "%target% glaubt nun fliegen zu können.", + "LANGUAGE": "Deine bevorzugte Sprache wurde auf %language% umgestellt.", + "LANGUAGE_CURRENT": "Deine bevorzugte Sprache ist %language%.", + "LANGUAGE_INVALID": "Die Sprache %language% wird leider nicht von PickShadow unterstützt.", + + "HEAL": "Du wurdest geheilt.", + "HEAL_REMOTE": "%player% wurde geheilt.", + + "PLUGINS": "%count% Erweiterungen sind auf diesem Subserver installiert, welche sind:", + "PLUGINS_FAKE": "Denkst du wirklich, dass du interne Informationen so einfach bekommen kannst?\nGlücklicherweise sind wir keine Arschlöcher und geben an was wir verwenden.\nDas PickShadow Netzwerk verwendet PSSE, LuckPerms und FreedomChat um Subserver zu verwalten.", + + "RELOAD": "Server reloads gelten als unsicher und werden daher von PSSE blockiert.\nBitte starte den Subserver stattdessen neu.\nFür mehr Informationen, lese bitte den folgenden Blogartikel (Englisch):\nhttps://madelinemiller.dev/blog/problem-with-reload/", + + "GAMEMODE": "Dein Spielmodus wurde in den %gamemode% geändert.", + "GAMEMODE_REMOTE": "Der Spielmodus von %player% wurde in den %gamemode% geändert.", + "GAMEMODE_SURVIVAL": "Überlebensmodus", + "GAMEMODE_CREATIVE": "Kreativmodus", + "GAMEMODE_ADVENTURE": "Abenteuermodus", + "GAMEMODE_SPECTATOR": "Zuschauermodus", + "CHATCOMMAND_ERROR_NAMESPACE": "Namespaces zu verwenden ist nicht erlaubt, da es verwendet werden kann um Sicherheitsmaßnahmen zu umgehen." } diff --git a/extension/src/main/resources/translations/en.json b/extension/src/main/resources/translations/en.json index 28bdd4f..7bc934a 100644 --- a/extension/src/main/resources/translations/en.json +++ b/extension/src/main/resources/translations/en.json @@ -13,6 +13,7 @@ "ERROR_UNKNOWN": "An unknown error occurred. Please contact the server team.", "ERROR_UNIMPLEMENTED": "This action is not yet implemented.", + "ERROR_INVALID_MODE": "This action is unavailable due to it being disabled.", "ERROR_NOT_A_PLAYER": "You must be a player to perform this action.", "ERROR_MISSING_PERM": "You aren't allowed to perform this action (lacking %permission%).", "ERROR_TOO_FEW_ARGUMENTS": "Too few arguments.", @@ -20,23 +21,27 @@ "ERROR_INVALID_ARGUMENT": "Invalid argument(s) %argument%.", "ERROR_PLAYER_NOT_FOUND": "The player %player% could not be found.", - "CONNECTION_JOIN": "%player% joined the game", + "CONNECTION_JOIN": "%player% joined the game", "CONNECTION_JOIN_WELCOME": "Welcome to , %player%!", - "CONNECTION_DISCONNECT": "%player% left the game", - "CONNECTION_ERROR_REGISTRATION": "Unable to process log in", - "CONNECTION_ERROR_TABLISTHANDLER": "TabListHandler failed unexpectedly", + "CONNECTION_DISCONNECT": "%player% left the game", + "CONNECTION_ERROR_REGISTRATION": "Unable to process log in", + "CONNECTION_ERROR_TABLISTHANDLER": "The TabListHandler failed unexpectedly", + "CONNECTION_ERROR_SCHEDULER": "The player scheduler failed unexpectedly", - "EXTENSIONCMD_GREETER": "This subserver is running <#d60532>PSSE \"%codename%\" @ %version% (%commit%, dirty %dirty%)\nTo view additional information, invoke some subcommands.", - "EXTENSIONCMD_KILLJVM": "Bye bye!", - "EXTENSIONCMD_LICENSE": "PSSE is licensed under the GNU Affero General Public License v3.", - "EXTENSIONCMD_SOURCE": "You can find the source code of PickShadow's server-side code on sos!git.", - "EXTENSIONCMD_ERROR_OLDCMD": "What's PSSP...?\nThe PickShadow Server Plugin (PSSP) has been renamed into PickShadow Server Extension (PSSE).\nPlease use this name instead, thank you.", + "EXTENSIONCMD_GREETER": "This subserver is running <#d60532>PSSE \"%codename%\" @ %version% (%commit%, dirty %dirty%)\nTo view additional information, invoke some subcommands.", + "EXTENSIONCMD_KILLJVM": "Bye bye!", + "EXTENSIONCMD_LICENSE": "PSSE is licensed under the %license%.", + "EXTENSIONCMD_SOURCE": "You can find the source code of PickShadow's server-side code on sos!git.", + "EXTENSIONCMD_ERROR_OLDCMD": "What's PSSP...?\nThe PickShadow Server Plugin (PSSP) has been renamed into PickShadow Server Extension (PSSE).\nPlease use the new name from now on. Thank you.", "MESSAGING_SERVER": "%sender% <#d60532>» ", - "MESSAGING_DIRECT": "%from% » %to% » %message%", + "MESSAGING_DIRECT": "%from% » %to% » %message%", + "MESSAGING_LASTCONTACT": "The last person you've messaged is %contact%.", "MESSAGING_ERROR_SELF": "You can't message yourself.", + "MESSAGING_ERROR_NOLASTCONTACT": "You haven't messaged anyone recently.", "LINKS_WEBSITE": "The PickShadow Network doesn't currently have a website.", + "LINKS_FORUM": "The PickShadow Network doesn't currently have a forum.", "LINKS_DISCORD": "The PickShadow Network doesn't currently have a Discord guild.", "LINKS_TEAMSPEAK": "The PickShadow Network doesn't currently have a TeamSpeak server.", "LINKS_MUMBLE": "The PickShadow Network doesn't currently have a Mumble server.", @@ -47,8 +52,9 @@ "CLEARCHAT_BRAINDAMAGE": "To prevent widespread brain damage, the chat has been cleared by %sender%.", "CLEARCHAT_CRINGE": "Due to masses of cringe, %sender% had to clear the chat.", + "HOME": "... woosh ...", "HOME_NORESPAWN": "You do not have a valid respawn point.\nPlease make sure your bed or respawn anchor isn't missing or obstructed.", - "HOME_TELEPORTED": "... woosh ...", + "HOME_NORESPAWN_OTHER": "%player% does not have a valid respawn point.\n%player%'s bed or respawn anchor is either missing or obstructed.", "TOGGLEDOWNFALL": "Toggled downfall", @@ -61,5 +67,28 @@ "TROLL_HACK_COMPLETE": "Access granted.", "TROLL_HEAVENS": "%target% believes that they can fly.", + "LANGUAGE": "Your preferred language was changed to %language%.", + "LANGUAGE_CURRENT": "Your preferred language is set to %language%.", + "LANGUAGE_INVALID": "The language %language% is not supported by PickShadow, sorry.", + + "HEAL": "You have been healed.", + "HEAL_REMOTE": "%player% has been healed.", + + "PLUGINS": "%count% extensions are installed on this subserver, these being:", + "PLUGINS_FAKE": "Do you really think you can get internal information so easily?\nLuckily, we aren't dicks and credit what we use.\nThe PickShadow network uses PSSE, LuckPerms and FreedomChat to manage subservers.", + "PLUGINS_ENTRY_PREFIX": "- ", + "PLUGINS_ENTRY_SUFFIX": "", + "PLUGINS_ENTRY_NAME_PREFIX": "", + "PLUGINS_ENTRY_NAME_SUFFIX": "", + + "RELOAD": "Server reloads are considered unsafe and are consequently blocked by PSSE.\nPlease restart the subserver instead.\nFor more information, please read the following blog article:\nhttps://madelinemiller.dev/blog/problem-with-reload/", + + "GAMEMODE": "Your gamemode was changed to %gamemode%.", + "GAMEMODE_REMOTE": "The gamemode of %player% was changed to %gamemode%.", + "GAMEMODE_SURVIVAL": "Survival mode", + "GAMEMODE_CREATIVE": "Creative mode", + "GAMEMODE_ADVENTURE": "Adventure mode", + "GAMEMODE_SPECTATOR": "Spectator mode", + "CHATCOMMAND_ERROR_NAMESPACE": "Using namespaces is not allowed, as it may be used to circumvent security measures." } diff --git a/gradle.properties b/gradle.properties index c2530d3..fc72068 100644 --- a/gradle.properties +++ b/gradle.properties @@ -25,35 +25,37 @@ versioningTyperelease=0 versioningFork= # Java -javaSource=22 -javaTarget=22 +javaSource=21 +javaTarget=21 + +# Plugins +pluginLombok=8.10.2 +pluginShadow=8.1.8 +pluginPaperweight=1.7.4 +pluginGitProperties=2.4.2 +pluginRunTask=2.3.1 # Dependencies -dependencyLombok=1.18.32 -dependencyJetbrainsAnnotations=24.1.0 -dependencyStarOpenSourceEngine=1-alpha5 -dependencyHikari=4.0.3 +dependencyLombok=1.18.34 +dependencyJetbrainsAnnotations=26.0.1 +dependencyStarOpenSourceEngine=1-alpha8 +dependencyStarOpenSourceEngineMC=1-release2 +dependencyHikari=6.1.0 dependencyGson=2.11.0 dependencyPaper=R0.1 dependencyAdventure=4.17.0 dependencyLuckPerms=5.4 dependencyFastboard=2.1.3 -# Plugins -pluginLombok=8.6 -pluginShadow=8.1.7 -pluginPaperweight=1.7.3 -pluginGitProperties=2.4.2 -pluginRunTask=2.3.0 - # Minecraft -minecraftVersion=1.21 +minecraftVersion=1.21.3 minecraftApi=1.21 # Plugin download metadata -downloadFreedomChat=x6xcBZtb -downloadEngineMC=https://git.staropensource.de/StarOpenSource/EngineMC/releases/download/v1-alpha1/bukkit.jar -downloadLuckPerms=https://download.luckperms.net/1556/bukkit/loader/LuckPerms-Bukkit-5.4.141.jar +downloadEngineMC=https://git.staropensource.de/StarOpenSource/EngineMC/releases/download/v1-release3/bukkit.jar +downloadLuckPerms=https://download.luckperms.net/1561/bukkit/loader/LuckPerms-Bukkit-5.4.146.jar +downloadSpark=https://ci.lucko.me/job/spark/464/artifact/spark-bukkit/build/libs/spark-1.10.118-bukkit.jar +downloadFreedomChat=NdbpBqOZ # etc group = de.staropensource.pickshadow diff --git a/servermanager/build.gradle b/servermanager/build.gradle new file mode 100644 index 0000000..fd52cab --- /dev/null +++ b/servermanager/build.gradle @@ -0,0 +1,96 @@ +/* + * PICKSHADOW SERVER KIT SOURCE FILE + * Copyright (c) 2024 The PickShadow Server Kit authors + * Licensed under the GNU Affero General Public License v3 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +// Plugins +plugins { + id("java") + id("application") + id("io.freefair.lombok") version("${pluginLombok}") + id("io.github.goooler.shadow") version("${pluginShadow}") +} + +// Dependencies +dependencies { + // Lombok + compileOnly("org.projectlombok:lombok:${dependencyLombok}") + annotationProcessor("org.projectlombok:lombok:${dependencyLombok}") + + // JetBrains Annotations + compileOnly("org.jetbrains:annotations:${dependencyJetbrainsAnnotations}") + + // Libraries + implementation("de.staropensource.engine:base:${dependencyStarOpenSourceEngine}") + runtimeOnly("de.staropensource.engine:ansi:${dependencyStarOpenSourceEngine}") + implementation("com.google.code.gson:gson:${dependencyGson}") + + // Project + implementation(project(":common")) +} + +// Set Java version +java { + toolchain.languageVersion.set(JavaLanguageVersion.of("${javaTarget}")) +} + +// Fix delombok task +delombok.doFirst { + File target = file("${project.projectDir}/src/main/module-info.java") + File source = file("${project.projectDir}/src/main/java/module-info.java") + + target.delete() + source.renameTo(target) +} +delombok.doLast { + File target = file("${project.projectDir}/src/main/java/module-info.java") + File source = file("${project.projectDir}/src/main/module-info.java") + + target.delete() + source.renameTo(target) +} + +// Configure output jar +jar { + manifest { + attributes( + "Main-Class": "de.jeremystartm.pickshadow.servermanager.Main" + ) + } +} + +// Configure application run task +application { + mainClass.set("de.jeremystartm.pickshadow.servermanager.Main") + applicationDefaultJvmArgs = [ + // Display GC log + "-Xlog:gc", + + // Set log level to DIAGNOSTIC + "-Dsosengine.base.loggerLevel=diagnostic", + + // Force writing to standard output + "-Dsosengine.base.loggerForceStandardOutput=true", + + // Pass classes which should be included if + // reflective sclasspath scanning is disabled. + "-Dsosengine.base.initialIncludeSubsystemClasses=de.staropensource.engine.ansi.AnsiSubsystem", + + // Force Jansi to write escape sequences + "-Djansi.mode=force", + ] +} diff --git a/servermanager/gradle b/servermanager/gradle new file mode 120000 index 0000000..3337596 --- /dev/null +++ b/servermanager/gradle @@ -0,0 +1 @@ +../gradle \ No newline at end of file diff --git a/servermanager/gradlew b/servermanager/gradlew new file mode 120000 index 0000000..502f5a2 --- /dev/null +++ b/servermanager/gradlew @@ -0,0 +1 @@ +../gradlew \ No newline at end of file diff --git a/servermanager/gradlew.bat b/servermanager/gradlew.bat new file mode 120000 index 0000000..2840132 --- /dev/null +++ b/servermanager/gradlew.bat @@ -0,0 +1 @@ +../gradlew.bat \ No newline at end of file diff --git a/servermanager/src/main/javadoc/theme.css b/servermanager/src/main/javadoc/theme.css new file mode 120000 index 0000000..bccac6b --- /dev/null +++ b/servermanager/src/main/javadoc/theme.css @@ -0,0 +1 @@ +../../../../../src/main/javadoc/theme.css \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 807c0ca..9adb200 100644 --- a/settings.gradle +++ b/settings.gradle @@ -17,6 +17,8 @@ * along with this program. If not, see . */ -rootProject.name = "PSSE" +rootProject.name = "PickShadow" +include("common") include("extension") +include("servermanager")