diff --git a/build.gradle b/build.gradle
index 73a7a47..ffe8a36 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,19 +1,19 @@
plugins {
id 'java-library'
id 'maven-publish'
- id("io.papermc.paperweight.userdev") version "1.7.1"
- id("xyz.jpenilla.run-paper") version "2.3.0"
+ id("io.papermc.paperweight.userdev") version "2.0.0-beta.17"
+ id("xyz.jpenilla.run-paper") version "2.3.1"
}
// Suppiled by Jenkins
ext.majorVersion = 2
ext.minorVersion = 12
-ext.minecraftVersion = "1.20.4"
+ext.minecraftVersion = "1.21.7"
-ext.buildNumber = System.env.BUILD_NUMBER == null ? "dev" : "build" + "$System.env.BUILD_NUMBER"
+ext.buildNumber = System.env.BUILD_NUMBER == null ? "dev" : "build" + "$System.env.BUILD_NUMBER"
ext.mavenDirectory = System.env.MAVEN_DIR == null ? "$projectDir/repo" : "$System.env.MAVEN_DIR"
ext.jdDirectory = System.env.JAVADOCS_DIR == null ? null : "$System.env.JAVADOCS_DIR"
-// differet version convension from Nyaa plugins
+// different version convention than Nyaa plugins
group = "de.greensurvivors"
archivesBaseName = "Padlock"
version = "$majorVersion.$minorVersion.$buildNumber".toString()
@@ -25,8 +25,8 @@ runServer {
*/
java {
- // Configure the java toolchain. This allows gradle to auto-provision JDK 17 on systems that only have JDK 8 installed for example.
- toolchain.languageVersion.set(JavaLanguageVersion.of(17))
+ // Configure the java toolchain. This allows gradle to auto-provision JDK 21 on systems that only have JDK 8 installed for example.
+ toolchain.languageVersion.set(JavaLanguageVersion.of(21))
}
// extra compile warnings
@@ -36,7 +36,7 @@ compileJava {
// Set the release flag. This configures what version bytecode the compiler will emit, as well as what JDK APIs are usable.
// See https://openjdk.java.net/jeps/247 for more information.
- options.release.set(17)
+ options.release.set(21)
}
repositories {
@@ -47,16 +47,20 @@ repositories {
}
dependencies {
- implementation 'org.apache.commons:commons-collections4:4.4'
- implementation 'org.jetbrains:annotations:24.1.0'
- implementation("com.github.ben-manes.caffeine:caffeine:3.1.8") // caches
- implementation("de.mkammerer:argon2-jvm:2.11") // native password hashing with argon2
+ implementation 'org.apache.commons:commons-collections4:4.5.0'
+ implementation 'org.jetbrains:annotations:26.0.2'
+ implementation("com.github.ben-manes.caffeine:caffeine:3.2.1") // caches
+ implementation("de.mkammerer:argon2-jvm:2.12") // native password hashing with argon2
paperweight.paperDevBundle("$minecraftVersion-R0.1-SNAPSHOT")
- compileOnly ('com.sk89q.worldguard:worldguard-bukkit:7.1.0-SNAPSHOT') { transitive = false }
- compileOnly('com.github.MilkBowl:VaultAPI:1.7') { transitive = false }
+ compileOnly('com.sk89q.worldguard:worldguard-bukkit:7.0.14-SNAPSHOT') { transitive = false }
+ compileOnly('com.github.MilkBowl:VaultAPI:1.7.1') { transitive = false }
}
+// we only work with paper and downstream!
+// todo
+//paperweight.reobfArtifactConfiguration = ReobfArtifactConfiguration.MOJANG_PRODUCTION
+
processResources {
expand version: project.version,
mcVersion: "$minecraftVersion"
@@ -68,13 +72,6 @@ tasks.register('sourcesJar', Jar) {
from sourceSets.main.java.srcDirs
}
-tasks {
- // Configure reobfJar to run when invoking the build task
- assemble {
- dependsOn(reobfJar)
- }
-}
-
publishing {
publications {
mavenJava(MavenPublication) {
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 7f93135..a4b76b9 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 9355b41..ff23a68 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
diff --git a/gradlew b/gradlew
index 1aa94a4..f5feea6 100755
--- a/gradlew
+++ b/gradlew
@@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+# SPDX-License-Identifier: Apache-2.0
+#
##############################################################################
#
@@ -55,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
-# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@@ -84,7 +86,8 @@ done
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
-APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
+APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
+' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
diff --git a/gradlew.bat b/gradlew.bat
index 6689b85..9b42019 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -13,6 +13,8 @@
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
+@rem SPDX-License-Identifier: Apache-2.0
+@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@@ -43,11 +45,11 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
+echo. 1>&2
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
goto fail
@@ -57,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
+echo. 1>&2
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
goto fail
diff --git a/src/main/java/de/greensurvivors/padlock/Padlock.java b/src/main/java/de/greensurvivors/padlock/Padlock.java
index 83a1355..968d084 100644
--- a/src/main/java/de/greensurvivors/padlock/Padlock.java
+++ b/src/main/java/de/greensurvivors/padlock/Padlock.java
@@ -1,13 +1,15 @@
package de.greensurvivors.padlock;
-import de.greensurvivors.padlock.command.ApplyPassword;
import de.greensurvivors.padlock.command.MainCommand;
import de.greensurvivors.padlock.config.ConfigManager;
import de.greensurvivors.padlock.config.MessageManager;
import de.greensurvivors.padlock.impl.DependencyManager;
import de.greensurvivors.padlock.impl.LockCacheManager;
import de.greensurvivors.padlock.impl.openabledata.OpenableToggleManager;
-import de.greensurvivors.padlock.listener.*;
+import de.greensurvivors.padlock.listener.BlockDebugListener;
+import de.greensurvivors.padlock.listener.BlockEnvironmentListener;
+import de.greensurvivors.padlock.listener.BlockInventoryMoveListener;
+import de.greensurvivors.padlock.listener.BlockPlayerListener;
import org.bukkit.Bukkit;
import org.bukkit.command.PluginCommand;
import org.bukkit.plugin.Plugin;
@@ -67,9 +69,9 @@ public void onEnable() {
pluginManager.registerEvents(new BlockPlayerListener(this), this);
pluginManager.registerEvents(new BlockEnvironmentListener(this), this);
pluginManager.registerEvents(new BlockInventoryMoveListener(this), this);
- pluginManager.registerEvents(new ChatPlayerListener(this), this);
+ //setPassword and applyPassword commands are also listener, but they will register themself.
- //register commands
+ //register main command; applyPassword will - again - register itself
MainCommand lockCmd = new MainCommand(this);
PluginCommand mainCommand = getCommand("padlock");
@@ -81,17 +83,6 @@ public void onEnable() {
getLogger().log(Level.SEVERE, "Couldn't register command 'padlock'!");
}
- ApplyPassword pwCmd = new ApplyPassword(plugin);
- PluginCommand pwCommand = getCommand("password");
- if (pwCommand != null) {
-
- pwCommand.setExecutor(pwCmd);
- pwCommand.setTabCompleter(pwCmd);
- } else {
- getLogger().log(Level.SEVERE, "Couldn't register command 'password'!");
- }
-
-
// Dependencys
dependencyManager = new DependencyManager(this);
}
diff --git a/src/main/java/de/greensurvivors/padlock/command/ApplyPassword.java b/src/main/java/de/greensurvivors/padlock/command/ApplyPassword.java
index c50d860..38ff411 100644
--- a/src/main/java/de/greensurvivors/padlock/command/ApplyPassword.java
+++ b/src/main/java/de/greensurvivors/padlock/command/ApplyPassword.java
@@ -5,81 +5,135 @@
import de.greensurvivors.padlock.config.MessageManager;
import de.greensurvivors.padlock.config.PermissionManager;
import de.greensurvivors.padlock.impl.SignSelection;
+import de.greensurvivors.padlock.impl.internal.InputAnvilMenu;
+import de.greensurvivors.padlock.impl.internal.VersionManager;
import de.greensurvivors.padlock.impl.signdata.SignPasswords;
import net.kyori.adventure.text.Component;
+import org.bukkit.Bukkit;
+import org.bukkit.Material;
import org.bukkit.block.Sign;
import org.bukkit.command.Command;
-import org.bukkit.command.CommandExecutor;
-import org.bukkit.command.CommandSender;
-import org.bukkit.command.TabCompleter;
+import org.bukkit.command.*;
import org.bukkit.entity.Player;
-import org.bukkit.event.player.PlayerCommandPreprocessEvent;
+import org.bukkit.event.Event;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.inventory.InventoryClickEvent;
+import org.bukkit.event.inventory.InventoryCloseEvent;
+import org.bukkit.event.inventory.InventoryDragEvent;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.AnvilInventory;
+import org.bukkit.inventory.InventoryView;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.permissions.Permissible;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import java.util.Arrays;
import java.util.List;
import java.util.Set;
+import java.util.WeakHashMap;
+import java.util.logging.Level;
/**
* Please note: This is a main command as well as a subcommand.
* But since we are handling passwords, we want to leak them as less as possible,
- * the {@link #onCommand(CommandSender, String[])} doesn't do anything and this class doesn't even
- * implement {@link CommandExecutor}. Instead, we catch them even before the server checks
- * for the right command in a {@link PlayerCommandPreprocessEvent} in {@link de.greensurvivors.padlock.listener.ChatPlayerListener}.
+ * the input will get handled by an AnvilGUI.
*
Also, {@link MainCommand} as well as {@link Padlock} itself has a separate instance of this,
*/
-public class ApplyPassword extends SubCommand implements TabCompleter, CommandExecutor {
+public class ApplyPassword extends SubCommand implements TabCompleter, CommandExecutor, Listener {
+ private final WeakHashMap openInventories = new WeakHashMap<>();
+
public ApplyPassword(@NotNull Padlock plugin) {
super(plugin);
+
+ Bukkit.getPluginManager().registerEvents(this, plugin);
+
+ // register this also as a normal command
+ PluginCommand pwCommand = plugin.getCommand("password");
+ if (pwCommand != null) {
+
+ pwCommand.setExecutor(this);
+ pwCommand.setTabCompleter(this);
+ } else {
+ plugin.getLogger().log(Level.SEVERE, "Couldn't register command 'password'!");
+ }
}
- /**
- * Because fuck Java not allowing an abstract function to also be static.
- * I really wish I could implement this cleaner, but nether can an abstract methode static,
- * nor is there a way to have a static Set in the mother class without all subclasses share the same... -.-
- */
- public static @NotNull Set getAliasesStatic() {
- return Set.of("password", "pw", "applypassword", "usepassword");
+ @EventHandler(ignoreCancelled = false)
+ private void onInventoryClose(@NotNull InventoryCloseEvent event) {
+ if (openInventories.containsKey(event.getView())) {
+ event.getInventory().clear();
+ openInventories.remove(event.getView());
+ }
}
- /**
- * Passwords need extra protection. Therefore, we catch them before the command gets
- * processed via {@link PlayerCommandPreprocessEvent} in {@link de.greensurvivors.padlock.listener.ChatPlayerListener}
- *
- * @param password
- * @param player
- */
- public static void onExternalCommand(char @NotNull [] password, @NotNull Player player) {
- // check permission for passwords
- if (player.hasPermission(PermissionManager.CMD_APPLY_PASSWORD.getPerm())) {
-
- //get and check selected sign
- Sign sign = SignSelection.getSelectedSign(player);
-
- if (sign != null) {
- //check for old Lockett(Pro) signs and try to update them
- sign = MainCommand.checkAndUpdateLegacySign(sign, player);
- if (sign == null) {
- Padlock.getPlugin().getMessageManager().sendLang(player, MessageManager.LangPath.SIGN_NEED_RESELECT);
- return;
- }
+ @EventHandler(ignoreCancelled = false)
+ private void onInventoryClick(@NotNull InventoryClickEvent event) {
+ final InventoryView view = event.getView();
- if (PadlockAPI.isLockSign(sign)) {
- if (!SignPasswords.isOnCooldown(player.getUniqueId(), sign.getLocation())) {
- // this will communicate if access was granted or not
- SignPasswords.checkPasswordAndGrandAccess(sign, player, password);
- } else {
- Padlock.getPlugin().getMessageManager().sendLang(player, MessageManager.LangPath.PASSWORD_ON_COOLDOWN);
+ if (openInventories.containsKey(view)) {
+ if (event.getView().getTopInventory() instanceof InputAnvilMenu anvilInventory) {
+ switch (event.getAction()) {
+ case NOTHING, DROP_ALL_CURSOR, DROP_ONE_CURSOR -> {
+ } // nothing
+ case PLACE_ALL, PLACE_SOME, PLACE_ONE, SWAP_WITH_CURSOR, HOTBAR_SWAP, DROP_ALL_SLOT, DROP_ONE_SLOT,
+ COLLECT_TO_CURSOR, UNKNOWN -> {
+ if (event.getClickedInventory() == anvilInventory) {
+ event.setResult(Event.Result.DENY);
+ }
+ }
+ case PICKUP_ALL, PICKUP_SOME, PICKUP_HALF, PICKUP_ONE -> {
+ if (event.getClickedInventory() == anvilInventory) {
+ if (event.getSlotType() == InventoryType.SlotType.RESULT) {
+ char[] password = anvilInventory.getRenameChars();
+
+ if (password != null) {
+ //this will communicate if access was granted or not
+ SignPasswords.checkPasswordAndGrandAccess(openInventories.get(view), event.getWhoClicked(), password);
+ Arrays.fill(password, '*');
+ Bukkit.getScheduler().runTaskLater(plugin, view::close, 1L); // delay closing one tick, so the event can correctly run
+ } else {
+ plugin.getMessageManager().sendLang(event.getWhoClicked(), MessageManager.LangPath.PASSWORD_ERROR_EMPTY);
+ }
+ }
+
+ event.setResult(Event.Result.DENY);
+ }
+ }
+ case MOVE_TO_OTHER_INVENTORY -> {
+ if (event.getClickedInventory() == anvilInventory && event.getSlotType() == InventoryType.SlotType.RESULT) {
+ char[] password = anvilInventory.getRenameChars();
+
+ if (password != null) {
+ //this will communicate if access was granted or not
+ SignPasswords.checkPasswordAndGrandAccess(openInventories.get(view), event.getWhoClicked(), password);
+ Bukkit.getScheduler().runTaskLater(plugin, view::close, 1L); // delay closing one tick, so the event can correctly run
+ } else {
+ plugin.getMessageManager().sendLang(event.getWhoClicked(), MessageManager.LangPath.PASSWORD_ERROR_EMPTY);
+ }
+ }
+
+ event.setResult(Event.Result.DENY);
}
- } else {
- Padlock.getPlugin().getMessageManager().sendLang(player, MessageManager.LangPath.SIGN_NEED_RESELECT);
}
- } else {
- Padlock.getPlugin().getMessageManager().sendLang(player, MessageManager.LangPath.SIGN_NOT_SELECTED);
+ } else { // error;
+ plugin.getLogger().warning("Got wrong Inventory type while setting password. How did this happen?");
+ event.setResult(Event.Result.DENY);
+ view.close();
+ }
+ }
+ }
+
+ @EventHandler(ignoreCancelled = true)
+ private void onInventoryDrag(@NotNull InventoryDragEvent event) {
+ if (openInventories.containsKey(event.getView())) {
+ for (int rawSlotId : event.getRawSlots()) {
+ if (rawSlotId < event.getView().getTopInventory().getSize()) {
+ event.setResult(Event.Result.DENY);
+ }
}
- } else {
- Padlock.getPlugin().getMessageManager().sendLang(player, MessageManager.LangPath.NO_PERMISSION);
}
}
@@ -90,7 +144,7 @@ protected boolean checkPermission(@NotNull Permissible permissible) {
@Override
public @NotNull Set getAliases() {
- return getAliasesStatic();
+ return Set.of("password", "pw", "applypassword", "usepassword");
}
@Override
@@ -101,7 +155,52 @@ protected boolean checkPermission(@NotNull Permissible permissible) {
@Override
protected boolean onCommand(@NotNull CommandSender sender, @NotNull String[] args) {
if (sender instanceof Player player) {
- plugin.getMessageManager().sendLang(player, MessageManager.LangPath.PASSWORD_START_PROCESSING);
+ if (Bukkit.isPrimaryThread()) {
+ // check permission for passwords
+ if (player.hasPermission(PermissionManager.CMD_APPLY_PASSWORD.getPerm())) {
+
+ //get and check selected sign
+ Sign sign = SignSelection.getSelectedSign(player);
+
+ if (sign != null) {
+ //check for old Lockett(Pro) signs and try to update them
+ sign = MainCommand.checkAndUpdateLegacySign(sign, player);
+ if (sign == null) {
+ Padlock.getPlugin().getMessageManager().sendLang(player, MessageManager.LangPath.SIGN_NEED_RESELECT);
+ return true;
+ }
+
+ if (PadlockAPI.isLockSign(sign)) {
+ if (!SignPasswords.isOnCooldown(player.getUniqueId(), sign.getLocation())) {
+ plugin.getMessageManager().sendLang(player, MessageManager.LangPath.PASSWORD_START_PROCESSING);
+
+ InventoryView view = VersionManager.openInputAnvil(player, plugin.getMessageManager().getLang(MessageManager.LangPath.PASSWORD_INPUT_APPLY_TITLE), Component.empty());
+
+ AnvilInventory inventory = (AnvilInventory) view.getTopInventory();
+
+ ItemStack stack = new ItemStack(Material.NAME_TAG);
+ ItemMeta meta = stack.getItemMeta();
+ meta.displayName(plugin.getMessageManager().getLang(MessageManager.LangPath.PASSWORD_DEFAULT_PASSWORD));
+ stack.setItemMeta(meta);
+
+ inventory.setFirstItem(stack);
+
+ openInventories.put(view, sign);
+ } else {
+ Padlock.getPlugin().getMessageManager().sendLang(player, MessageManager.LangPath.PASSWORD_ON_COOLDOWN);
+ }
+ } else {
+ Padlock.getPlugin().getMessageManager().sendLang(player, MessageManager.LangPath.SIGN_NEED_RESELECT);
+ }
+ } else {
+ Padlock.getPlugin().getMessageManager().sendLang(player, MessageManager.LangPath.SIGN_NOT_SELECTED);
+ }
+ } else {
+ Padlock.getPlugin().getMessageManager().sendLang(player, MessageManager.LangPath.NO_PERMISSION);
+ }
+ } else {
+ plugin.getLogger().warning("Command apply password was run async and failed!");
+ }
return true;
} else {
plugin.getMessageManager().sendLang(sender, MessageManager.LangPath.NOT_A_PLAYER);
@@ -111,7 +210,7 @@ protected boolean onCommand(@NotNull CommandSender sender, @NotNull String[] arg
@Override
protected @Nullable List onTabComplete(@NotNull CommandSender sender, @NotNull String[] args) {
- return null;
+ return List.of();
}
@Override
@@ -121,12 +220,6 @@ protected boolean onCommand(@NotNull CommandSender sender, @NotNull String[] arg
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
- if (sender instanceof Player player) {
- plugin.getMessageManager().sendLang(player, MessageManager.LangPath.PASSWORD_START_PROCESSING);
- return true;
- } else {
- plugin.getMessageManager().sendLang(sender, MessageManager.LangPath.NOT_A_PLAYER);
- return false;
- }
+ return onCommand(sender, args);
}
}
diff --git a/src/main/java/de/greensurvivors/padlock/command/SetPassword.java b/src/main/java/de/greensurvivors/padlock/command/SetPassword.java
index e15b645..2b267e9 100644
--- a/src/main/java/de/greensurvivors/padlock/command/SetPassword.java
+++ b/src/main/java/de/greensurvivors/padlock/command/SetPassword.java
@@ -5,13 +5,27 @@
import de.greensurvivors.padlock.config.MessageManager;
import de.greensurvivors.padlock.config.PermissionManager;
import de.greensurvivors.padlock.impl.SignSelection;
+import de.greensurvivors.padlock.impl.internal.InputAnvilMenu;
+import de.greensurvivors.padlock.impl.internal.VersionManager;
import de.greensurvivors.padlock.impl.signdata.SignLock;
import de.greensurvivors.padlock.impl.signdata.SignPasswords;
import net.kyori.adventure.text.Component;
+import org.bukkit.Bukkit;
+import org.bukkit.Material;
import org.bukkit.block.Sign;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
-import org.bukkit.event.player.PlayerCommandPreprocessEvent;
+import org.bukkit.event.Event;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.inventory.InventoryClickEvent;
+import org.bukkit.event.inventory.InventoryCloseEvent;
+import org.bukkit.event.inventory.InventoryDragEvent;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.AnvilInventory;
+import org.bukkit.inventory.InventoryView;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.permissions.Permissible;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -19,63 +33,90 @@
import java.util.Arrays;
import java.util.List;
import java.util.Set;
+import java.util.WeakHashMap;
-/**
- * But since we are handling passwords, we want to leak them as less as possible,
- * and catch them even before the server checks for the right command in a {@link PlayerCommandPreprocessEvent} in {@link de.greensurvivors.padlock.listener.ChatPlayerListener}
- */
-public final class SetPassword extends SubCommand {
+public final class SetPassword extends SubCommand implements Listener {
+ private final WeakHashMap openInventories = new WeakHashMap<>();
SetPassword(@NotNull Padlock plugin) {
super(plugin);
+
+ Bukkit.getPluginManager().registerEvents(this, plugin);
}
- /**
- * Because fuck Java not allowing an abstract function to also be static.
- * I really wish I could implement this cleaner, but nether can an abstract methode static,
- * nor is there a way to have a static Set in the mother class without all subclasses share the same... -.-
- */
- public static @NotNull Set getAliasesStatic() {
- return Set.of("setpassword", "setpw");
+ @EventHandler(ignoreCancelled = false)
+ private void onInventoryClose(@NotNull InventoryCloseEvent event) {
+ if (openInventories.containsKey(event.getView())) {
+ event.getInventory().clear();
+
+ openInventories.remove(event.getView());
+ }
}
- public static void onExternalCommand(char @Nullable [] newPassword, @NotNull Player player) {
- // check permission to edit
- if (player.hasPermission(PermissionManager.CMD_SET_PASSWORD.getPerm())) {
- //get and check selected sign
- Sign sign = SignSelection.getSelectedSign(player);
- if (sign != null) {
- //check for old Lockett(Pro) signs and try to update them
- sign = MainCommand.checkAndUpdateLegacySign(sign, player);
- if (sign == null) {
- Padlock.getPlugin().getMessageManager().sendLang(player, MessageManager.LangPath.SIGN_NEED_RESELECT);
- return;
- }
+ @EventHandler(ignoreCancelled = false)
+ private void onInventoryClick(@NotNull InventoryClickEvent event) {
+ final InventoryView view = event.getView();
- if (PadlockAPI.isLockSign(sign)) {
- // check sign owner, even admins can't change a password of something they don't own.
- if (SignLock.isOwner(sign, player.getUniqueId()) || player.hasPermission(PermissionManager.ADMIN_PASSWORD.getPerm())) {
- // this will communicate if password was set or removed
- SignPasswords.setPassword(sign, player, newPassword);
- } else {
- Padlock.getPlugin().getMessageManager().sendLang(player, MessageManager.LangPath.NO_PERMISSION);
+ if (openInventories.containsKey(view)) {
+ if (event.getView().getTopInventory() instanceof InputAnvilMenu anvilInventory) {
+ switch (event.getAction()) {
+ case NOTHING, DROP_ALL_CURSOR, DROP_ONE_CURSOR -> {
+ } // nothing
+ case PLACE_ALL, PLACE_SOME, PLACE_ONE, SWAP_WITH_CURSOR, HOTBAR_SWAP, DROP_ALL_SLOT, DROP_ONE_SLOT,
+ COLLECT_TO_CURSOR, UNKNOWN -> {
+ if (event.getClickedInventory() == anvilInventory) {
+ event.setResult(Event.Result.DENY);
+ }
+ }
+ case PICKUP_ALL, PICKUP_SOME, PICKUP_HALF, PICKUP_ONE -> {
+ if (event.getClickedInventory() == anvilInventory) {
+ if (event.getSlotType() == InventoryType.SlotType.RESULT) {
+ char[] password = anvilInventory.getRenameChars();
+
+ // this will communicate if password was set or removed
+ SignPasswords.setPassword(openInventories.get(view), event.getWhoClicked(), password);
+ // yes I know I invalidate the arrays at multiple places, but in terms of password safety it's better to be double and tripple safe then sorry.
+ Arrays.fill(password, '*');
+ Bukkit.getScheduler().runTaskLater(plugin, view::close, 1L); // delay closing one tick, so the event can correctly run
+ }
+
+ event.setResult(Event.Result.DENY);
+ }
+ }
+ case MOVE_TO_OTHER_INVENTORY -> {
+ if (event.getClickedInventory() == anvilInventory && event.getSlotType() == InventoryType.SlotType.RESULT) {
+ char[] password = anvilInventory.getRenameChars();
+
+ // this will communicate if password was set or removed
+ SignPasswords.setPassword(openInventories.get(view), event.getWhoClicked(), password);
+ // yes I know I invalidate the arrays at multiple places, but in terms of password safety it's better to be double and tripple safe then sorry.
+ Arrays.fill(password, '*');
+ Bukkit.getScheduler().runTaskLater(plugin, view::close, 1L); // delay closing one tick, so the event can correctly run
+ }
+
+ event.setResult(Event.Result.DENY);
}
- } else {
- Padlock.getPlugin().getMessageManager().sendLang(player, MessageManager.LangPath.SIGN_NEED_RESELECT);
}
- } else {
- Padlock.getPlugin().getMessageManager().sendLang(player, MessageManager.LangPath.SIGN_NOT_SELECTED);
+ } else { // error;
+ plugin.getLogger().warning("Got wrong Inventory type while setting password. How did this happen?");
+ event.setResult(Event.Result.DENY);
+ view.close();
}
- } else {
- Padlock.getPlugin().getMessageManager().sendLang(player, MessageManager.LangPath.NO_PERMISSION);
}
+ }
- // yes I know I invalidate the arrays at multiple places, but in terms of password safety it's better to be double and tripple safe then sorry.
- if (newPassword != null) {
- Arrays.fill(newPassword, '*');
+ @EventHandler(ignoreCancelled = true)
+ private void onInventoryDrag(@NotNull InventoryDragEvent event) {
+ if (openInventories.containsKey(event.getView())) {
+ for (int rawSlotId : event.getRawSlots()) {
+ if (rawSlotId < event.getView().getTopInventory().getSize()) {
+ event.setResult(Event.Result.DENY);
+ }
+ }
}
}
+
@Override
protected boolean checkPermission(@NotNull Permissible permissible) {
return permissible.hasPermission(PermissionManager.CMD_SET_PASSWORD.getPerm());
@@ -83,7 +124,7 @@ protected boolean checkPermission(@NotNull Permissible permissible) {
@Override
protected @NotNull Set getAliases() {
- return getAliasesStatic();
+ return Set.of("setpassword", "setpw");
}
@Override
@@ -93,9 +134,53 @@ protected boolean checkPermission(@NotNull Permissible permissible) {
@Override
protected boolean onCommand(@NotNull CommandSender sender, @NotNull String[] args) {
- if (sender instanceof Player player) {
- plugin.getMessageManager().sendLang(player, MessageManager.LangPath.PASSWORD_SAFETY_WARNING);
- plugin.getMessageManager().sendLang(player, MessageManager.LangPath.PASSWORD_START_PROCESSING);
+ if (sender instanceof Player player) { // check permission to edit
+ if (Bukkit.isPrimaryThread()) {
+ if (player.hasPermission(PermissionManager.CMD_SET_PASSWORD.getPerm())) {
+ //get and check selected sign
+ Sign sign = SignSelection.getSelectedSign(player);
+ if (sign != null) {
+ //check for old Lockett(Pro) signs and try to update them
+ sign = MainCommand.checkAndUpdateLegacySign(sign, player);
+ if (sign == null) {
+ Padlock.getPlugin().getMessageManager().sendLang(player, MessageManager.LangPath.SIGN_NEED_RESELECT);
+ return true;
+ }
+
+ if (PadlockAPI.isLockSign(sign)) {
+ // check sign owner, even admins can't change a password of something they don't own.
+ if (SignLock.isOwner(sign, player.getUniqueId()) || player.hasPermission(PermissionManager.ADMIN_PASSWORD.getPerm())) {
+ plugin.getMessageManager().sendLang(player, MessageManager.LangPath.PASSWORD_SAFETY_WARNING);
+ plugin.getMessageManager().sendLang(player, MessageManager.LangPath.PASSWORD_START_PROCESSING);
+
+ InventoryView view = VersionManager.openInputAnvil(player, plugin.getMessageManager().getLang(MessageManager.LangPath.PASSWORD_INPUT_SET_TITLE), plugin.getMessageManager().getLang(MessageManager.LangPath.PASSWORD_INPUT_REMOVE));
+
+ AnvilInventory inventory = (AnvilInventory) view.getTopInventory();
+
+ ItemStack stack = new ItemStack(Material.NAME_TAG);
+ ItemMeta meta = stack.getItemMeta();
+ meta.displayName(plugin.getMessageManager().getLang(MessageManager.LangPath.PASSWORD_DEFAULT_PASSWORD));
+ stack.setItemMeta(meta);
+
+ inventory.setFirstItem(stack);
+
+ openInventories.put(view, sign);
+
+ } else {
+ Padlock.getPlugin().getMessageManager().sendLang(player, MessageManager.LangPath.NO_PERMISSION);
+ }
+ } else {
+ Padlock.getPlugin().getMessageManager().sendLang(player, MessageManager.LangPath.SIGN_NEED_RESELECT);
+ }
+ } else {
+ Padlock.getPlugin().getMessageManager().sendLang(player, MessageManager.LangPath.SIGN_NOT_SELECTED);
+ }
+ } else {
+ Padlock.getPlugin().getMessageManager().sendLang(player, MessageManager.LangPath.NO_PERMISSION);
+ }
+ } else {
+ plugin.getLogger().warning("Command apply password was run async and failed!");
+ }
return true;
} else {
plugin.getMessageManager().sendLang(sender, MessageManager.LangPath.NOT_A_PLAYER);
@@ -105,6 +190,6 @@ protected boolean onCommand(@NotNull CommandSender sender, @NotNull String[] arg
@Override
protected @Nullable List onTabComplete(@NotNull CommandSender sender, @NotNull String[] args) {
- return null;
+ return List.of();
}
}
diff --git a/src/main/java/de/greensurvivors/padlock/config/ConfigManager.java b/src/main/java/de/greensurvivors/padlock/config/ConfigManager.java
index 10ee22a..77b276b 100644
--- a/src/main/java/de/greensurvivors/padlock/config/ConfigManager.java
+++ b/src/main/java/de/greensurvivors/padlock/config/ConfigManager.java
@@ -366,7 +366,7 @@ public boolean doLocksExpire() {
return LOCK_EXPIRE_DAYS.getValueOrFallback();
}
- public boolean isLockable(Material material) {
+ public boolean isLockable(@NotNull Material material) {
return LOCKABLES.getValueOrFallback().contains(material);
}
diff --git a/src/main/java/de/greensurvivors/padlock/config/MessageManager.java b/src/main/java/de/greensurvivors/padlock/config/MessageManager.java
index 7077e41..9897704 100644
--- a/src/main/java/de/greensurvivors/padlock/config/MessageManager.java
+++ b/src/main/java/de/greensurvivors/padlock/config/MessageManager.java
@@ -421,6 +421,11 @@ public enum LangPath {
PASSWORD_SAFETY_WARNING("cmd.password.safety-warning", "Warning: never use a password, you are using anywhere else! While I did everything I could for your safety, there ARE ways your password could get leaked!"),
PASSWORD_START_PROCESSING("cmd.password.start-processing"),
PASSWORD_WRONG_PASSWORD("cmd.password.wrong-password"),
+ PASSWORD_DEFAULT_PASSWORD("cmd.password.default"),
+ PASSWORD_INPUT_SET_TITLE("cmd.password.gui-title.set"),
+ PASSWORD_INPUT_REMOVE("cmd.password.gui.remove"),
+ PASSWORD_INPUT_APPLY_TITLE("cmd.password.gui-title.apply"),
+ PASSWORD_ERROR_EMPTY("cmd.password.error.empty"),
//plugin prefix
PLUGIN_PREFIX("prefix", "[Padlock] "),
//
diff --git a/src/main/java/de/greensurvivors/padlock/impl/internal/InputAnvilMenu.java b/src/main/java/de/greensurvivors/padlock/impl/internal/InputAnvilMenu.java
new file mode 100644
index 0000000..810ef74
--- /dev/null
+++ b/src/main/java/de/greensurvivors/padlock/impl/internal/InputAnvilMenu.java
@@ -0,0 +1,13 @@
+package de.greensurvivors.padlock.impl.internal;
+
+import org.jetbrains.annotations.Nullable;
+
+public interface InputAnvilMenu {
+ /**
+ * use {@link #getRenameChars()} instead!
+ */
+ @Deprecated
+ String getRenameText();
+
+ char @Nullable [] getRenameChars();
+}
diff --git a/src/main/java/de/greensurvivors/padlock/impl/internal/NMSAnvilMenu.java b/src/main/java/de/greensurvivors/padlock/impl/internal/NMSAnvilMenu.java
new file mode 100644
index 0000000..bd4e485
--- /dev/null
+++ b/src/main/java/de/greensurvivors/padlock/impl/internal/NMSAnvilMenu.java
@@ -0,0 +1,13 @@
+package de.greensurvivors.padlock.impl.internal;
+
+import org.bukkit.craftbukkit.inventory.view.CraftAnvilView;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public interface NMSAnvilMenu {
+
+ @NotNull
+ CraftAnvilView getBukkitView();
+
+ char @Nullable [] getLastInput();
+}
diff --git a/src/main/java/de/greensurvivors/padlock/impl/internal/VersionManager.java b/src/main/java/de/greensurvivors/padlock/impl/internal/VersionManager.java
new file mode 100644
index 0000000..78e1f54
--- /dev/null
+++ b/src/main/java/de/greensurvivors/padlock/impl/internal/VersionManager.java
@@ -0,0 +1,14 @@
+package de.greensurvivors.padlock.impl.internal;
+
+import de.greensurvivors.padlock.impl.internal.v21_7.NMSInputAnvilMenu;
+import net.kyori.adventure.text.Component;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.InventoryView;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class VersionManager {
+ public static InventoryView openInputAnvil(final @NotNull Player player, @NotNull Component title, @Nullable Component emptyText) {
+ return NMSInputAnvilMenu.openInputAnvil(player, title, emptyText);
+ }
+}
diff --git a/src/main/java/de/greensurvivors/padlock/impl/internal/v21_7/CraftAnvilInventory.java b/src/main/java/de/greensurvivors/padlock/impl/internal/v21_7/CraftAnvilInventory.java
new file mode 100644
index 0000000..8c30e5f
--- /dev/null
+++ b/src/main/java/de/greensurvivors/padlock/impl/internal/v21_7/CraftAnvilInventory.java
@@ -0,0 +1,42 @@
+package de.greensurvivors.padlock.impl.internal.v21_7;
+
+import de.greensurvivors.padlock.impl.internal.InputAnvilMenu;
+import net.minecraft.world.Container;
+import org.bukkit.Location;
+import org.bukkit.craftbukkit.inventory.CraftInventoryAnvil;
+import org.bukkit.craftbukkit.inventory.view.CraftAnvilView;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.inventory.InventoryView;
+import org.jetbrains.annotations.Nullable;
+
+public final class CraftAnvilInventory extends CraftInventoryAnvil implements InputAnvilMenu {
+
+ public CraftAnvilInventory(Location location, Container inventory, Container resultInventory) {
+ super(location, inventory, resultInventory);
+ }
+
+ /**
+ * use {@link #getRenameChars()} instead!
+ *
+ * @return
+ */
+ @Override
+ @Deprecated
+ public String getRenameText() {
+ return super.getRenameText();
+ }
+
+ public char @Nullable [] getRenameChars() {
+ if (!this.getViewers().isEmpty()) { // pretty much syncWithArbitraryViewValue
+ HumanEntity entity = this.getViewers().getFirst();
+ if (entity != null) {
+ InventoryView var4 = entity.getOpenInventory();
+ if (var4 instanceof CraftAnvilView cav && cav.getHandle() instanceof NMSInputAnvilMenu nmsInputAnvilMenu) {
+ return nmsInputAnvilMenu.getLastInput();
+ }
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/src/main/java/de/greensurvivors/padlock/impl/internal/v21_7/NMSInputAnvilMenu.java b/src/main/java/de/greensurvivors/padlock/impl/internal/v21_7/NMSInputAnvilMenu.java
new file mode 100644
index 0000000..15471b5
--- /dev/null
+++ b/src/main/java/de/greensurvivors/padlock/impl/internal/v21_7/NMSInputAnvilMenu.java
@@ -0,0 +1,152 @@
+package de.greensurvivors.padlock.impl.internal.v21_7;
+
+import de.greensurvivors.padlock.impl.internal.NMSAnvilMenu;
+import io.papermc.paper.adventure.PaperAdventure;
+import net.minecraft.core.component.DataComponents;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.MenuProvider;
+import net.minecraft.world.SimpleMenuProvider;
+import net.minecraft.world.entity.player.Inventory;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.inventory.AnvilMenu;
+import net.minecraft.world.inventory.ContainerLevelAccess;
+import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.level.block.state.BlockState;
+import org.bukkit.craftbukkit.entity.CraftHumanEntity;
+import org.bukkit.craftbukkit.inventory.view.CraftAnvilView;
+import org.bukkit.inventory.InventoryView;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Warning: The code in this class is derived from Paper and therefore a legal grey zone.
+ * Even though I changed it wildly, this will remain legally derived from Mojangs code
+ * as well as the legal hell that bukkit/Spigot/paper is.
+ * I don't claim to own anything of this class. Copyright by mojang (Microsoft) and everybody contributed.
+ *
+ * This is a purely virtual inventory ("menu" in mojang terms)
+ * There is no way to link this to a block somewhere in the world.
+ * This is an overwrite of Server behavior to just act as an Input of text.
+ * This will NOT work as a regular anvil! It will NOT call all Events you would expect of a normal anvil (like PrepareResultEvent)
+ * This is purely intentional as we want to keep the user input as much a secret as possible!
+ */
+// I could use more official Server API,
+// but this would still build onto not API code, never could become 100% clean of mojangs code, complicate much code and slow it down.
+// so until this becomes a hassle to update I believe this should be a better option.
+public final class NMSInputAnvilMenu extends AnvilMenu implements NMSAnvilMenu {
+ private static final String BLURRED = "*****";
+ private final @Nullable Component emptyText;
+
+ private CraftAnvilView bukkitAnvilView;
+ private char @Nullable [] lastInput = null;
+
+ public static InventoryView openInputAnvil(final @NotNull org.bukkit.entity.Player player, @NotNull net.kyori.adventure.text.Component title, @Nullable net.kyori.adventure.text.Component emptyText) {
+ net.minecraft.world.entity.player.Player nmsPlayer = ((CraftHumanEntity) player).getHandle();
+ MenuProvider provider = new SimpleMenuProvider((syncId, inventory, player2) -> new NMSInputAnvilMenu(syncId, inventory, ContainerLevelAccess.create(nmsPlayer.level(), nmsPlayer.blockPosition()), emptyText),
+ PaperAdventure.asVanilla(title));
+
+ nmsPlayer.openMenu(provider);
+ nmsPlayer.containerMenu.checkReachable = false;
+ nmsPlayer.containerMenu.getBukkitView();
+
+ return nmsPlayer.containerMenu.getBukkitView();
+ }
+
+ public NMSInputAnvilMenu(int syncId, @NotNull Inventory inventory, @NotNull ContainerLevelAccess context, @Nullable net.kyori.adventure.text.Component emptyText) {
+ super(syncId, inventory, context);
+
+ if (emptyText == null) {
+ this.emptyText = null;
+ } else {
+ this.emptyText = PaperAdventure.asVanilla(emptyText);
+ }
+
+ itemName = BLURRED;
+ }
+
+ @Contract(pure = true)
+ public char @Nullable [] getLastInput() {
+ return lastInput;
+ }
+
+ @Override
+ protected boolean isValidBlock(@NotNull BlockState state) {
+ return true;
+ }
+
+ @Override
+ protected boolean mayPickup(@NotNull Player player, boolean present) {
+ return present;
+ }
+
+ @Override
+ protected void onTake(@NotNull Player player, @NotNull ItemStack stack) {
+ this.inputSlots.setItem(INPUT_SLOT, ItemStack.EMPTY);
+ this.inputSlots.setItem(ADDITIONAL_SLOT, ItemStack.EMPTY);
+ this.resultSlots.setItem(0, ItemStack.EMPTY);
+ }
+
+ @Override
+ public void createResult() {
+ final ItemStack input = this.inputSlots.getItem(INPUT_SLOT);
+
+ if (!input.isEmpty()) { // sanity check
+ ItemStack result;
+
+ if (lastInput != null && lastInput.length > 0) {
+ result = input.copy();
+ result.set(DataComponents.CUSTOM_NAME, Component.literal(BLURRED));
+ } else if (input.has(DataComponents.CUSTOM_NAME)) {
+ result = input.copy();
+ result.remove(DataComponents.CUSTOM_NAME);
+ } else { // todo this should never happen
+ result = ItemStack.EMPTY;
+ }
+
+ this.resultSlots.setItem(INPUT_SLOT, result);
+ this.sendAllDataToRemote(); // CraftBukkit - SPIGOT-6686: Always send completed inventory to stay in sync with client
+ this.broadcastChanges(); // I don't know what's the differenz between these two methods is
+ }
+ }
+
+ @Override
+ public boolean setItemName(final @Nullable String newInput) {
+ if (newInput != null && newInput.length() <= MAX_NAME_LENGTH) {
+ this.lastInput = newInput.toCharArray();
+ // somehow we have to set the cost everytime either createResult() or setItemName() is called, or else the client will see a 0 and will assume a 1.
+ // (derps out)
+ // so in this case we just set it to number of chars to display something usefully
+ this.cost.set(newInput.length());
+
+ ItemStack resultItem = this.resultSlots.getItem(0);
+ if (!resultItem.isEmpty()) {
+ if (newInput.isBlank()) {
+ if (emptyText == null) {
+ resultItem.remove(DataComponents.CUSTOM_NAME);
+ } else {
+ resultItem.set(DataComponents.CUSTOM_NAME, emptyText);
+ }
+ } else {
+ resultItem.set(DataComponents.CUSTOM_NAME, Component.literal(BLURRED));
+ }
+ }
+
+ this.createResult();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public @NotNull CraftAnvilView getBukkitView() {
+ if (this.bukkitAnvilView == null) {
+ final CraftAnvilInventory bukkitInv = new CraftAnvilInventory(this.access.getLocation(), this.inputSlots, this.resultSlots);
+ this.bukkitAnvilView = new CraftAnvilView(this.player.getBukkitEntity(), bukkitInv, this);
+ this.bukkitAnvilView.updateFromLegacy(bukkitInv);
+ }
+
+ return this.bukkitAnvilView;
+ }
+}
diff --git a/src/main/java/de/greensurvivors/padlock/impl/signdata/SignPasswords.java b/src/main/java/de/greensurvivors/padlock/impl/signdata/SignPasswords.java
index 73ed189..515eb77 100644
--- a/src/main/java/de/greensurvivors/padlock/impl/signdata/SignPasswords.java
+++ b/src/main/java/de/greensurvivors/padlock/impl/signdata/SignPasswords.java
@@ -12,7 +12,7 @@
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
import org.bukkit.block.Sign;
-import org.bukkit.entity.Player;
+import org.bukkit.entity.HumanEntity;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;
@@ -208,7 +208,7 @@ public static boolean needsPasswordAccess(@NotNull Sign sign) {
return (hash != null && !hash.isEmpty());
}
- public static void checkPasswordAndGrandAccess(@NotNull Sign sign, @NotNull Player player, char @NotNull [] password) {
+ public static void checkPasswordAndGrandAccess(@NotNull Sign sign, @NotNull HumanEntity player, char @NotNull [] password) {
final String hash = sign.getPersistentDataContainer().get(passwordHashKey, PersistentDataType.STRING);
if (hash != null) {
@@ -244,7 +244,7 @@ public static void removeAccessOfLoc(@NotNull Location location) {
}
}
- public static void setPassword(final @NotNull Sign sign, final @NotNull Player player, final char @Nullable [] newPassword) {
+ public static void setPassword(final @NotNull Sign sign, final @NotNull HumanEntity player, final char @Nullable [] newPassword) {
PersistentDataContainer dataContainer = sign.getPersistentDataContainer();
if (newPassword == null) {
diff --git a/src/main/java/de/greensurvivors/padlock/listener/ChatPlayerListener.java b/src/main/java/de/greensurvivors/padlock/listener/ChatPlayerListener.java
deleted file mode 100644
index e49ec18..0000000
--- a/src/main/java/de/greensurvivors/padlock/listener/ChatPlayerListener.java
+++ /dev/null
@@ -1,201 +0,0 @@
-package de.greensurvivors.padlock.listener;
-
-import de.greensurvivors.padlock.Padlock;
-import de.greensurvivors.padlock.command.ApplyPassword;
-import de.greensurvivors.padlock.command.SetPassword;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.logging.log4j.Level;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Marker;
-import org.apache.logging.log4j.core.LogEvent;
-import org.apache.logging.log4j.core.Logger;
-import org.apache.logging.log4j.core.filter.AbstractFilter;
-import org.apache.logging.log4j.message.Message;
-import org.bukkit.Bukkit;
-import org.bukkit.event.EventHandler;
-import org.bukkit.event.EventPriority;
-import org.bukkit.event.Listener;
-import org.bukkit.event.player.PlayerCommandPreprocessEvent;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.*;
-
-public final class ChatPlayerListener implements Listener {
- private static final Set setPwCmdStrings = new HashSet<>();
- private static final Set removePwCmdStrings = new HashSet<>(); // the client doesn't set tailing whitespace
- private static final Set applyPwCmdStrings = new HashSet<>();
- private final Padlock plugin;
-
- public ChatPlayerListener(@NotNull Padlock plugin) {
- this.plugin = plugin;
-
- // get all possible combinations of all command aliases with all subcommand aliases
- final Set cmdAliases = new HashSet<>();
- for (Map.Entry> cmdEntry : plugin.getDescription().getCommands().entrySet()) {
- cmdAliases.add(cmdEntry.getKey().toLowerCase(Locale.ENGLISH));
- Object objectAliases = cmdEntry.getValue().get("aliases");
-
- if (Objects.requireNonNull(objectAliases) instanceof String string) {
- cmdAliases.add(string);
- } else if (objectAliases instanceof List> list) {
- for (Object listEntry : list) {
- if (listEntry instanceof String string) {
- cmdAliases.add(string);
- } else {
- plugin.getLogger().warning("Couldn't get alias " + listEntry + " as String. Ignoring. Might risk posting passwords to chat tho");
- }
- }
- } else {
- plugin.getLogger().warning("Couldn't get aliases " + objectAliases + ". Ignoring. Might risk posting passwords to chat tho");
- }
- }
- for (String cmdStr : cmdAliases) {
- for (String subCmdStr : SetPassword.getAliasesStatic()) {
- setPwCmdStrings.add(cmdStr + " " + subCmdStr + " ");
- }
- }
- for (String cmdStr : cmdAliases) {
- for (String subCmdStr : SetPassword.getAliasesStatic()) {
- removePwCmdStrings.add(cmdStr + " " + subCmdStr);
- }
- }
- for (String cmdStr : cmdAliases) {
- for (String subCmdStr : ApplyPassword.getAliasesStatic()) {
- applyPwCmdStrings.add(cmdStr + " " + subCmdStr + " ");
- }
- }
- for (String cmdStr : ApplyPassword.getAliasesStatic()) {
- applyPwCmdStrings.add(cmdStr + " ");
- }
-
- // set our custom filter
- ((org.apache.logging.log4j.core.Logger) LogManager.getRootLogger()).addFilter(new CmdFilter());
- }
-
- /**
- * We want to be there if possible as the first, so we can cancel the event as fast as possible,
- * if it could contain a password
- */
- @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = false)
- private void onChat(@NotNull PlayerCommandPreprocessEvent event) {
- final String text = event.getMessage();
-
- for (String cmdToCheck : setPwCmdStrings) {
- if (text.regionMatches(true, 1, cmdToCheck, 0, cmdToCheck.length())) {
- // make room so chat event can roll through, we can wait until next circle
- Bukkit.getScheduler().runTask(plugin, () -> {
- final char @Nullable [] newPassword;
-
- if (text.length() > cmdToCheck.length() + 1) {
- newPassword = text.substring(cmdToCheck.length() + 1).toCharArray();
- } else { // should never happen, since the client doesn't send tailing whitespace
- newPassword = null;
- }
-
- SetPassword.onExternalCommand(newPassword, event.getPlayer());
-
- //invalidate char array
- // yes I know I invalidate the arrays at multiple places, but in terms of password safety it's better to be double and tripple safe then sorry.
- if (newPassword != null) {
- Arrays.fill(newPassword, '*');
- }
- });
-
- event.setMessage(text.substring(0, cmdToCheck.length() + 1) + "**********");
- plugin.getLogger().info(event.getPlayer().getName() + " issued sub command: setpassword.");
- return;
- }
- }
-
- for (String cmdToCheck : removePwCmdStrings) {
- if (text.regionMatches(true, 1, cmdToCheck, 0, cmdToCheck.length())) {
- // make room so chat event can roll through, we can wait until next circle
- Bukkit.getScheduler().runTask(plugin, () -> {
- SetPassword.onExternalCommand(null, event.getPlayer());
- });
-
- plugin.getLogger().info(event.getPlayer().getName() + " issued sub command: setpassword to remove a password.");
- return;
- }
- }
-
- for (String cmdToCheck : applyPwCmdStrings) {
- if (text.regionMatches(true, 1, cmdToCheck, 0, cmdToCheck.length())) {
- // make room so chat event can roll through, we can wait until next circle
- Bukkit.getScheduler().runTask(plugin, () -> {
- final char[] password = text.substring(cmdToCheck.length() + 1).toCharArray();
- ApplyPassword.onExternalCommand(password, event.getPlayer());
-
- //invalidate char array
- // yes I know I invalidate the arrays at multiple places, but in terms of password safety it's better to be double and tripple safe then sorry.
- Arrays.fill(password, '*');
- });
-
- event.setMessage(text.substring(0, cmdToCheck.length() + 1) + "**********");
- plugin.getLogger().info(event.getPlayer().getName() + " issued sub command: password.");
- return;
- }
- }
- }
-
- /**
- * This filters the log of the Server to not write any passwords and accidentally keep them in the logs.
- */
- private static class CmdFilter extends AbstractFilter {
- private CmdFilter() {
- }
-
- @Override
- public Result filter(final LogEvent event) {
- return event == null ? Result.NEUTRAL : logResult(event.getMessage().getFormattedMessage());
- }
-
- @Override
- public Result filter(final Logger logger, final Level level, final Marker marker, final Message msg,
- final Throwable t) {
- Result result = t != null ? logResult(t.getMessage()) : Result.NEUTRAL;
- if (msg != null) {
- if (result == Result.DENY) return result;
- return logResult(msg.getFormattedMessage());
- }
- return Result.NEUTRAL;
- }
-
- public Result filter(final Logger logger, final Level level, final Marker marker, final Object msg,
- final Throwable t) {
- Result result = t != null ? logResult(t.getMessage()) : Result.NEUTRAL;
- if (msg != null) {
- if (result == Result.DENY) return result;
- return logResult(msg.toString());
- }
- return Result.NEUTRAL;
- }
-
- public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
- final Object... params) {
- return logResult(msg);
- }
-
-
- public Result logResult(String text) {
- for (String cmdToCheck : setPwCmdStrings) {
- int index = StringUtils.indexOfIgnoreCase(text, cmdToCheck);
-
- if (index > 0) {
- return Result.DENY;
- }
- }
-
- for (String cmdToCheck : applyPwCmdStrings) {
- int index = StringUtils.indexOfIgnoreCase(text, cmdToCheck);
-
- if (index > 0) {
- return Result.DENY;
- }
- }
-
- return Result.NEUTRAL;
- }
- }
-}
diff --git a/src/main/resources/lang.properties b/src/main/resources/lang.properties
index 865c623..8dc54d4 100644
--- a/src/main/resources/lang.properties
+++ b/src/main/resources/lang.properties
@@ -41,6 +41,11 @@ cmd.info.owners=owners:
cmd.info.timer=timer:
cmd.not-a-subcommand=The argument "" you provided is not a subcommand.
cmd.password.access-granted=Access granted.
+cmd.password.default=Ch4nge this!
+cmd.password.error.empty=Passwords can't be empty! If no password was set you don't need to use /applypassword
+cmd.password.gui-title.apply=Input password
+cmd.password.gui-title.set=Set new password (empty = remove)
+cmd.password.gui.remove=Remove password!
cmd.password.on-cooldown=You are still on Cooldown. Are you waiting for a cmd or did you mistype your password often?
cmd.password.safety-warning=Warning: never use a password, you are using anywhere else! While I did everything I could for your safety, there ARE ways your password could get leaked!
cmd.password.start-processing=Processing a password may take a while, please stand by!
diff --git a/src/main/resources/lang_de.properties b/src/main/resources/lang_de.properties
index 151596b..e2eaf33 100644
--- a/src/main/resources/lang_de.properties
+++ b/src/main/resources/lang_de.properties
@@ -41,6 +41,10 @@ cmd.info.owners=Besitzer:
cmd.info.timer=Timer:
cmd.not-a-subcommand=Das Argument "" ist kein Subcommand.
cmd.password.access-granted=Zugriff gewährt.
+cmd.password.error.empty=Passwörter können nicht leer sein! Wenn kein Passwort gesetzt ist, ist /applypassword nicht nötig.
+cmd.password.gui-title.apply=Passwort eingeben!
+cmd.password.gui-title.set=Neues Passwort eingeben! (leer = kein Pw)
+cmd.password.gui.remove=Password entfernen!
cmd.password.safety-warning=Warnung: Nutze NIEMALS ein Passwort, was du auch woanders verwendest! Während ich alles in meinen Wissen daran setze deine Sicherheit sicher zu stellen, KANN es vorkommen, dass dieses Password geleakt wird!
cmd.password.start-processing=Bitte warte einen Moment, bis das Passwort verarbeitet wurde.
cmd.password.wrong-password=Falsches Passwort. Bitte versuche es erneut!
diff --git a/src/main/resources/lang_en.properties b/src/main/resources/lang_en.properties
index 81bbf71..ed3db70 100644
--- a/src/main/resources/lang_en.properties
+++ b/src/main/resources/lang_en.properties
@@ -40,6 +40,10 @@ cmd.info.owners=owners:
cmd.info.timer=timer:
cmd.not-a-subcommand=The argument "" you provided is not a subcommand.
cmd.password.access-granted=Access granted.
+cmd.password.error.empty=Passwords can't be empty! If no password was set you don't need to use /applypassword
+cmd.password.gui-title.apply=Input password
+cmd.password.gui-title.set=Set new password (empty = remove)
+cmd.password.gui.remove=Remove password!
cmd.password.safety-warning=Warning: never use a password, you are using anywhere else! While I did everything I could for your safety, there ARE ways your password could get leaked!
cmd.password.start-processing=Processing a password may take a while, please stand by!
cmd.password.wrong-password=You submitted the wrong password. Please try again!