diff --git a/.gitignore b/.gitignore index 7c81ce8..6aaf662 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ # Gradle .gradle/ +.gradle-user-home/ build/ !gradle-wrapper.jar !**/src/main/**/build/ diff --git a/build.gradle b/build.gradle index 352faff..c769a0e 100644 --- a/build.gradle +++ b/build.gradle @@ -1,3 +1,8 @@ +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import org.gradle.language.jvm.tasks.ProcessResources + +import java.util.Collections + plugins { id 'java' id 'io.papermc.paperweight.userdev' version '2.0.0-beta.21' @@ -23,207 +28,180 @@ java { toolchain.languageVersion.set(JavaLanguageVersion.of(21)) } -def excludedModules = project.hasProperty('excludeModules') ? - project.property('excludeModules').split(',')*.trim() : [] +def moduleExcludes = [ + 'blocks': [ + '**/commands/WorkbenchCommand.java', + '**/commands/AnvilCommand.java', + '**/commands/CartographyTableCommand.java', + '**/commands/GrindstoneCommand.java', + '**/commands/LoomCommand.java', + '**/commands/SmithingTableCommand.java', + '**/commands/StonecutterCommand.java', + '**/commands/EnderChestCommand.java', + '**/commands/BlocksMenuCommand.java', + '**/listeners/ShulkerBoxListener.java' + ], + 'player': [ + '**/commands/FlyCommand.java', + '**/commands/HealCommand.java', + '**/commands/FeedCommand.java', + '**/commands/VanishCommand.java', + '**/commands/SeenCommand.java', + '**/commands/HatCommand.java', + '**/commands/SuicideCommand.java', + '**/commands/RepairCommand.java' + ], + 'jei-fix': [ + '**/listeners/JeiRecipeSyncListener.java' + ], + 'mob-drops': [ + '**/listeners/MobDropListener.java', + '**/listeners/MobDropMenuListener.java', + '**/commands/MobDropCommand.java' + ] +] -def includeBlocks = !excludedModules.contains('blocks') -def includePlayer = !excludedModules.contains('player') -def includeJeiFix = !excludedModules.contains('jei-fix') -def includeMobDrops = !excludedModules.contains('mob-drops') +def variantDefinitions = [ + standard: [ + archiveFileName: "EssentialsC-${project.version}.jar", + excludedModules: ['mob-drops'] + ], + all: [ + archiveFileName: "EssentialsC-all-${project.version}.jar", + excludedModules: [] + ], + lite: [ + archiveFileName: "EssentialsC-lite-${project.version}.jar", + excludedModules: ['blocks'] + ] +] -println "\n📦 EssentialsC 模块配置:" -println " ✅ Core (核心)" -println " ${includeBlocks ? '✅' : '❌'} Blocks (便捷方块)" -println " ${includePlayer ? '✅' : '❌'} Player (玩家管理)" -println " ${includeJeiFix ? '✅' : '❌'} JEI Fix (JEI 修复)" -println " ${includeMobDrops ? '✅' : '❌'} Mob Drops (生物掉落物)" -println "" +if (project.hasProperty('excludeModules')) { + def customExcludedModules = project.property('excludeModules') + .split(',') + .collect { it.trim() } + .findAll { !it.isEmpty() } -sourceSets { - main { - java { - if (!includeBlocks) { - exclude '**/commands/WorkbenchCommand.java' - exclude '**/commands/AnvilCommand.java' - exclude '**/commands/CartographyTableCommand.java' - exclude '**/commands/GrindstoneCommand.java' - exclude '**/commands/LoomCommand.java' - exclude '**/commands/SmithingTableCommand.java' - exclude '**/commands/StonecutterCommand.java' - exclude '**/commands/EnderChestCommand.java' - exclude '**/commands/BlocksMenuCommand.java' - exclude '**/listeners/ShulkerBoxListener.java' - } - - if (!includePlayer) { - exclude '**/commands/FlyCommand.java' - exclude '**/commands/HealCommand.java' - exclude '**/commands/FeedCommand.java' - exclude '**/commands/VanishCommand.java' - exclude '**/commands/SeenCommand.java' - exclude '**/commands/HatCommand.java' - exclude '**/commands/SuicideCommand.java' - exclude '**/commands/RepairCommand.java' - } - - if (!includeJeiFix) { - exclude '**/listeners/JeiRecipeSyncListener.java' - } - - if (!includeMobDrops) { - exclude '**/listeners/MobDropListener.java' - exclude '**/listeners/MobDropMenuListener.java' - exclude '**/commands/MobDropCommand.java' - } - } + variantDefinitions.custom = [ + archiveFileName: "EssentialsC-custom-${project.version}.jar", + excludedModules: customExcludedModules + ] +} + +def resolveExcludePatterns = { Collection modules -> + modules.collectMany { module -> moduleExcludes.get(module, Collections.emptyList()) }.unique() +} + +variantDefinitions.each { variantName, variantConfig -> + def unknownModules = variantConfig.excludedModules.findAll { !moduleExcludes.containsKey(it) } + if (!unknownModules.isEmpty()) { + throw new GradleException("Unknown modules for variant '${variantName}': ${unknownModules.join(', ')}") } } +def variantSourceSets = [:] + +variantDefinitions.each { variantName, variantConfig -> + def sourceSet = sourceSets.create(variantName) + sourceSet.java.srcDirs = sourceSets.main.java.srcDirs + sourceSet.resources.srcDirs = sourceSets.main.resources.srcDirs + resolveExcludePatterns(variantConfig.excludedModules).each { pattern -> + sourceSet.java.exclude(pattern) + } + sourceSet.compileClasspath += sourceSets.main.compileClasspath + sourceSet.runtimeClasspath += sourceSet.output + sourceSet.compileClasspath + variantSourceSets[variantName] = sourceSet +} + tasks.withType(JavaCompile).configureEach { options.encoding = 'UTF-8' } -processResources { +tasks.withType(ProcessResources).configureEach { filteringCharset = 'UTF-8' - inputs.property('version', project.version) - filesMatching('paper-plugin.yml') { - expand('version': project.version) +} + +variantDefinitions.keySet().each { variantName -> + def sourceSet = variantSourceSets[variantName] + def processTaskName = sourceSet.processResourcesTaskName + tasks.named(processTaskName, ProcessResources).configure { + inputs.property('version', project.version) + filesMatching('paper-plugin.yml') { + expand('version': project.version) + } } } -shadowJar { +tasks.named('jar').configure { enabled = false } -// ========== 多版本构建任务 ========== - -task shadowJarStandard(type: com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - from sourceSets.main.output - configurations = [project.configurations.runtimeClasspath] - - exclude '**/listeners/MobDropListener.class' - exclude '**/listeners/MobDropMenuListener.class' - exclude '**/commands/MobDropCommand.class' - - archiveFileName.set("EssentialsC-${project.version}.jar") +tasks.named('shadowJar').configure { + enabled = false } -task shadowJarAll(type: com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - from sourceSets.main.output - configurations = [project.configurations.runtimeClasspath] - - archiveFileName.set("EssentialsC-all-${project.version}.jar") -} +def variantJarTasks = variantDefinitions.collect { variantName, variantConfig -> + def taskName = "shadowJar${variantName.capitalize()}" + def sourceSet = variantSourceSets[variantName] -task shadowJarLite(type: com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - from sourceSets.main.output - configurations = [project.configurations.runtimeClasspath] - - exclude '**/commands/WorkbenchCommand.class' - exclude '**/commands/AnvilCommand.class' - exclude '**/commands/CartographyTableCommand.class' - exclude '**/commands/GrindstoneCommand.class' - exclude '**/commands/LoomCommand.class' - exclude '**/commands/SmithingTableCommand.class' - exclude '**/commands/StonecutterCommand.class' - exclude '**/commands/EnderChestCommand.class' - exclude '**/commands/BlocksMenuCommand.class' - exclude '**/listeners/ShulkerBoxListener.class' - - archiveFileName.set("EssentialsC-lite-${project.version}.jar") -} - -build { - dependsOn shadowJarAll -} - -// ========== 部署任务 ========== - -task deployToPaper12111(type: Copy) { - group = 'deployment' - description = '部署到 Paper 1.21.11 测试服务器(仅完整版)' - dependsOn shadowJarAll - from shadowJarAll - into "${projectDir}/test-server/paper-1.21.11/plugins" - doLast { - println "✅ 插件已部署到 Paper 1.21.11: ${projectDir}/test-server/paper-1.21.11/plugins" + tasks.register(taskName, ShadowJar) { + group = 'build' + description = "Builds the ${variantName} plugin jar." + archiveFileName.set(variantConfig.archiveFileName as String) + from(sourceSet.output) + configurations = [project.configurations.runtimeClasspath] + dependsOn(tasks.named(sourceSet.classesTaskName)) } } -task deployToPaper26 { +tasks.named('assemble').configure { + dependsOn(variantJarTasks) +} + +tasks.register('buildAllVersions') { + group = 'build' + description = 'Builds standard, all, and lite plugin jars.' + dependsOn(variantJarTasks) +} + +tasks.register('deployToPaper12111', Copy) { group = 'deployment' - description = '部署到 Paper 26.1.2 测试服务器(仅完整版)' - dependsOn shadowJarAll - + description = 'Deploys the all variant to the local Paper 1.21.11 test server.' + def artifact = tasks.named('shadowJarAll').flatMap { it.archiveFile } + dependsOn(tasks.named('shadowJarAll')) + from(artifact) + into(layout.projectDirectory.dir('test-server/paper-1.21.11/plugins')) +} + +tasks.register('deployToPaper26') { + group = 'deployment' + description = 'Deploys the all variant to the local Paper 26.1.2 test server.' + dependsOn(tasks.named('shadowJarAll')) + doFirst { def pluginsDir = file("${projectDir}/test-server/paper-26.1.2/plugins") - if (pluginsDir.exists()) { - // 删除所有 EssentialsC 相关的 JAR 文件 - fileTree(pluginsDir).include('EssentialsC*.jar').each { file -> - println "🗑️ 删除旧插件: ${file.name}" - file.delete() - } - // 删除配置文件夹 - def configDir = file("${pluginsDir}/EssentialsC") - if (configDir.exists()) { - println "🗑️ 删除旧配置文件夹" - configDir.deleteDir() - } - println "✅ 清理完成" - } else { - println "⚠️ plugins 目录不存在,跳过清理" + if (!pluginsDir.exists()) { + return + } + + fileTree(pluginsDir).matching { + include 'EssentialsC*.jar' + }.each { pluginJar -> + pluginJar.delete() } } - + doLast { + def artifact = tasks.named('shadowJarAll').flatMap { it.archiveFile } copy { - from shadowJarAll.archiveFile - into "${projectDir}/test-server/paper-26.1.2/plugins" + from(artifact) + into("${projectDir}/test-server/paper-26.1.2/plugins") } - println "✅ 插件已部署到 Paper 26.1.2: ${projectDir}/test-server/paper-26.1.2/plugins" } } -// Paper 26.1.2 一键构建和部署任务 -task buildAndDeployToPaper26 { +tasks.register('buildAndDeployToPaper26') { group = 'deployment' - description = '一键构建并部署到 Paper 26.1.2' - dependsOn clean, deployToPaper26 -} - -// 默认部署到 Paper 1.21.11 -task deployToTestServer { - dependsOn deployToPaper12111 -} - -build.finalizedBy deployToTestServer - -task buildAllVersions { - group = 'build' - description = '构建所有版本的插件' - dependsOn shadowJarStandard, shadowJarAll, shadowJarLite - - doLast { - println "\n🎉 所有版本构建完成!" - println "📦 标准版: EssentialsC-${project.version}.jar" - println "📦 完整版: EssentialsC-all-${project.version}.jar" - println "📦 轻量版: EssentialsC-lite-${project.version}.jar" - println "📁 输出目录: ${buildDir}/libs/" - - def standardFile = file("${buildDir}/libs/EssentialsC-${project.version}.jar") - def allFile = file("${buildDir}/libs/EssentialsC-all-${project.version}.jar") - def liteFile = file("${buildDir}/libs/EssentialsC-lite-${project.version}.jar") - - if (!standardFile.exists()) { - throw new GradleException("❌ 标准版文件不存在!") - } - if (!allFile.exists()) { - throw new GradleException("❌ 完整版文件不存在!") - } - if (!liteFile.exists()) { - throw new GradleException("❌ 轻量版文件不存在!") - } - - println "✅ 所有文件验证通过!" - } + description = 'Builds and deploys the all variant to the local Paper 26.1.2 test server.' + dependsOn(tasks.named('clean'), tasks.named('deployToPaper26')) } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..8bdaf60 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2e11132..1844133 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-9.1.0-bin.zip +distributionUrl=https\://mirrors.aliyun.com/gradle/distributions/v9.1.0/gradle-9.1.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..adff685 --- /dev/null +++ b/gradlew @@ -0,0 +1,248 @@ +#!/bin/sh + +# +# Copyright © 2015 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# 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/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# 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 -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..c4bdd3a --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,93 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@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 ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +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 + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +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 + +:execute +@rem Setup the command line + + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/src/main/java/cn/infstar/essentialsC/EssentialsC.java b/src/main/java/cn/infstar/essentialsC/EssentialsC.java index eabadc0..48c1c86 100644 --- a/src/main/java/cn/infstar/essentialsC/EssentialsC.java +++ b/src/main/java/cn/infstar/essentialsC/EssentialsC.java @@ -1,6 +1,8 @@ package cn.infstar.essentialsC; -import cn.infstar.essentialsC.commands.*; +import cn.infstar.essentialsC.commands.BaseCommand; +import cn.infstar.essentialsC.commands.CommandRegistry; +import cn.infstar.essentialsC.commands.HelpCommand; import org.bukkit.Bukkit; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; @@ -18,200 +20,112 @@ public final class EssentialsC extends JavaPlugin { registerPluginChannels(); registerListeners(); registerCommands(); - - getLogger().info("插件已启用!版本: " + getDescription().getVersion()); + + getLogger().info("EssentialsC enabled. Version: " + getDescription().getVersion()); } @Override public void onDisable() { - getLogger().info("EssentialsC 插件已禁用!"); + getLogger().info("EssentialsC disabled."); } - + public static LangManager getLangManager() { return langManager; } - - /** - * 注册 JEI 配方同步所需的插件频道 - */ + private void registerPluginChannels() { org.bukkit.plugin.messaging.Messenger messenger = getServer().getMessenger(); - // 注册 Fabric 和 NeoForge 的配方同步频道 messenger.registerOutgoingPluginChannel(this, "fabric:recipe_sync"); messenger.registerOutgoingPluginChannel(this, "neoforge:recipe_content"); } - + private void registerListeners() { if (registerListener("cn.infstar.essentialsC.listeners.ShulkerBoxListener")) { - getLogger().info("- 潜影盒模块"); + getLogger().info("- Shulker box module"); } - + if (registerListener("cn.infstar.essentialsC.listeners.JeiRecipeSyncListener")) { - getLogger().info("- JEI 配方同步"); + getLogger().info("- JEI recipe sync"); } - + if (registerListener("cn.infstar.essentialsC.listeners.MobDropListener")) { - try { - Class.forName("cn.infstar.essentialsC.listeners.MobDropMenuListener"); - new cn.infstar.essentialsC.listeners.MobDropMenuListener(this); - } catch (ClassNotFoundException e) { - } - getLogger().info("- 生物掉落控制"); + createOptionalInstance("cn.infstar.essentialsC.listeners.MobDropMenuListener"); + getLogger().info("- Mob drop control"); } } - + private boolean registerListener(String className) { try { Class listenerClass = Class.forName(className); Object listenerInstance = listenerClass.getConstructor(EssentialsC.class).newInstance(this); getServer().getPluginManager().registerEvents((org.bukkit.event.Listener) listenerInstance, this); return true; - } catch (Exception e) { + } catch (Exception ignored) { return false; } } - + + private void createOptionalInstance(String className) { + try { + Class targetClass = Class.forName(className); + targetClass.getConstructor(EssentialsC.class).newInstance(this); + } catch (Exception ignored) { + } + } + private void registerCommands() { try { Field bukkitCommandMap = Bukkit.getServer().getClass().getDeclaredField("commandMap"); bukkitCommandMap.setAccessible(true); org.bukkit.command.CommandMap commandMap = (org.bukkit.command.CommandMap) bukkitCommandMap.get(Bukkit.getServer()); - - int commandCount = 0; - - if (classExists("cn.infstar.essentialsC.commands.WorkbenchCommand")) { - registerCommandWithAliases(commandMap, "workbench", new WorkbenchCommand(), "wb"); - commandCount++; + + for (CommandRegistry.CommandSpec spec : CommandRegistry.getCommandSpecs()) { + BaseCommand executor = CommandRegistry.getCommand(spec.name()); + if (executor == null) { + continue; + } + registerCommandWithAliases(commandMap, spec.name(), executor, spec.aliases().toArray(String[]::new)); } - if (classExists("cn.infstar.essentialsC.commands.AnvilCommand")) { - registerCommandWithAliases(commandMap, "anvil", new AnvilCommand()); - commandCount++; - } - if (classExists("cn.infstar.essentialsC.commands.CartographyTableCommand")) { - registerCommandWithAliases(commandMap, "cartographytable", new CartographyTableCommand(), "ct", "cartography"); - commandCount++; - } - if (classExists("cn.infstar.essentialsC.commands.GrindstoneCommand")) { - registerCommandWithAliases(commandMap, "grindstone", new GrindstoneCommand(), "gs"); - commandCount++; - } - if (classExists("cn.infstar.essentialsC.commands.LoomCommand")) { - registerCommandWithAliases(commandMap, "loom", new LoomCommand()); - commandCount++; - } - if (classExists("cn.infstar.essentialsC.commands.SmithingTableCommand")) { - registerCommandWithAliases(commandMap, "smithingtable", new SmithingTableCommand(), "st", "smithing"); - commandCount++; - } - if (classExists("cn.infstar.essentialsC.commands.StonecutterCommand")) { - registerCommandWithAliases(commandMap, "stonecutter", new StonecutterCommand(), "sc"); - commandCount++; - } - if (classExists("cn.infstar.essentialsC.commands.EnderChestCommand")) { - registerCommandWithAliases(commandMap, "enderchest", new EnderChestCommand(), "ec"); - commandCount++; - } - if (classExists("cn.infstar.essentialsC.commands.BlocksMenuCommand")) { - registerCommandWithAliases(commandMap, "blocks", new BlocksMenuCommand()); - commandCount++; - } - - if (classExists("cn.infstar.essentialsC.commands.FlyCommand")) { - registerCommandWithAliases(commandMap, "fly", new FlyCommand()); - commandCount++; - } - if (classExists("cn.infstar.essentialsC.commands.HealCommand")) { - registerCommandWithAliases(commandMap, "heal", new HealCommand()); - commandCount++; - } - if (classExists("cn.infstar.essentialsC.commands.FeedCommand")) { - registerCommandWithAliases(commandMap, "feed", new FeedCommand()); - commandCount++; - } - if (classExists("cn.infstar.essentialsC.commands.VanishCommand")) { - registerCommandWithAliases(commandMap, "vanish", new VanishCommand(), "v"); - commandCount++; - } - if (classExists("cn.infstar.essentialsC.commands.SeenCommand")) { - registerCommandWithAliases(commandMap, "seen", new SeenCommand(), "info"); - commandCount++; - } - if (classExists("cn.infstar.essentialsC.commands.HatCommand")) { - registerCommandWithAliases(commandMap, "hat", new HatCommand()); - commandCount++; - } - if (classExists("cn.infstar.essentialsC.commands.SuicideCommand")) { - registerCommandWithAliases(commandMap, "suicide", new SuicideCommand(), "die"); - commandCount++; - } - if (classExists("cn.infstar.essentialsC.commands.RepairCommand")) { - registerCommandWithAliases(commandMap, "repair", new RepairCommand(), "rep"); - commandCount++; - } - - if (classExists("cn.infstar.essentialsC.commands.MobDropCommand")) { - registerCommandWithAliases(commandMap, "mobdrops", new MobDropCommand()); - commandCount++; - } - + registerCommandWithAliases(commandMap, "essentialsc", new HelpCommand(), "essc"); - commandCount++; } catch (Exception e) { - getLogger().severe("无法注册命令: " + e.getMessage()); + getLogger().severe("Failed to register commands: " + e.getMessage()); e.printStackTrace(); } } - - private boolean classExists(String className) { - try { - Class.forName(className); - return true; - } catch (ClassNotFoundException e) { - return false; - } - } - - /** - * 注册命令并支持别名 - * @param commandMap Bukkit CommandMap - * @param name 主命令名 - * @param executor 命令执行器 - * @param aliases 别名列表(可选) - */ - private void registerCommandWithAliases(org.bukkit.command.CommandMap commandMap, String name, cn.infstar.essentialsC.commands.BaseCommand executor, String... aliases) { + + private void registerCommandWithAliases(org.bukkit.command.CommandMap commandMap, String name, BaseCommand executor, String... aliases) { Command command = new Command(name) { @Override public boolean execute(CommandSender sender, String commandLabel, String[] args) { return executor.onCommand(sender, this, commandLabel, args); } - + @Override public java.util.List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { - if (executor instanceof org.bukkit.command.TabCompleter) { - return ((org.bukkit.command.TabCompleter) executor).onTabComplete(sender, this, alias, args); + if (executor instanceof org.bukkit.command.TabCompleter completer) { + return completer.onTabComplete(sender, this, alias, args); } return super.tabComplete(sender, alias, args); } }; - + command.setPermission(executor.getPermission()); - // 注册到默认命名空间,使玩家可以直接使用 /workbench 而不是 /essentialsc:workbench commandMap.register("", command); - - // 注册别名 + for (String alias : aliases) { Command aliasCmd = new Command(alias) { @Override public boolean execute(CommandSender sender, String commandLabel, String[] args) { return executor.onCommand(sender, this, commandLabel, args); } - + @Override - public java.util.List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { - if (executor instanceof org.bukkit.command.TabCompleter) { - return ((org.bukkit.command.TabCompleter) executor).onTabComplete(sender, this, alias, args); + public java.util.List tabComplete(CommandSender sender, String label, String[] args) throws IllegalArgumentException { + if (executor instanceof org.bukkit.command.TabCompleter completer) { + return completer.onTabComplete(sender, this, label, args); } - return super.tabComplete(sender, alias, args); + return super.tabComplete(sender, label, args); } }; aliasCmd.setPermission(executor.getPermission()); diff --git a/src/main/java/cn/infstar/essentialsC/commands/BlocksMenuCommand.java b/src/main/java/cn/infstar/essentialsC/commands/BlocksMenuCommand.java index 780683d..9feec41 100644 --- a/src/main/java/cn/infstar/essentialsC/commands/BlocksMenuCommand.java +++ b/src/main/java/cn/infstar/essentialsC/commands/BlocksMenuCommand.java @@ -14,6 +14,8 @@ import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.persistence.PersistentDataType; +import java.util.List; + public class BlocksMenuCommand extends BaseCommand implements Listener { private static final int MENU_SIZE = 36; @@ -50,7 +52,7 @@ public class BlocksMenuCommand extends BaseCommand implements Listener { } private void openMenu(Player player) { - String title = plugin.getConfig().getString("blocks-menu.title", "&6&lEssentialsC &8- &e&l鍔熻兘鏂瑰潡鑿滃崟"); + String title = plugin.getConfig().getString("blocks-menu.title", "&6&lEssentialsC &8- &e&lBlocks Menu"); Inventory menu = new BlocksMenuHolder(translateColor(title)).getInventory(); var itemsConfig = plugin.getConfig().getConfigurationSection("blocks-menu.items"); @@ -76,7 +78,7 @@ public class BlocksMenuCommand extends BaseCommand implements Listener { } String name = translateColor(section.getString("name", "&fItem")); - java.util.List lore = section.getStringList("lore").stream() + List lore = section.getStringList("lore").stream() .map(this::translateColor) .toList(); @@ -86,7 +88,7 @@ public class BlocksMenuCommand extends BaseCommand implements Listener { player.openInventory(menu); } - private void addItem(Inventory inv, int slot, Material material, String name, java.util.List lore, String key) { + private void addItem(Inventory inv, int slot, Material material, String name, List lore, String key) { ItemStack item = new ItemStack(material); ItemMeta meta = item.getItemMeta(); if (meta != null) { @@ -115,9 +117,10 @@ public class BlocksMenuCommand extends BaseCommand implements Listener { ItemMeta meta = clicked.getItemMeta(); String key = meta.getPersistentDataContainer().get(this.blockKey, PersistentDataType.STRING); - if (key != null && HelpCommand.COMMAND_CACHE.containsKey(key)) { + BaseCommand blockCommand = CommandRegistry.getCommand(key); + if (blockCommand != null) { playBlockOpenSound(player, key); - HelpCommand.COMMAND_CACHE.get(key).execute(player, new String[0]); + blockCommand.execute(player, new String[0]); } } diff --git a/src/main/java/cn/infstar/essentialsC/commands/CommandRegistry.java b/src/main/java/cn/infstar/essentialsC/commands/CommandRegistry.java new file mode 100644 index 0000000..2eeca77 --- /dev/null +++ b/src/main/java/cn/infstar/essentialsC/commands/CommandRegistry.java @@ -0,0 +1,111 @@ +package cn.infstar.essentialsC.commands; + +import java.lang.reflect.Constructor; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public final class CommandRegistry { + + private static final Map COMMANDS = new LinkedHashMap<>(); + private static final Map ALIAS_TO_COMMAND = new HashMap<>(); + private static final Map COMMAND_CACHE = new HashMap<>(); + private static final Set UNAVAILABLE_COMMANDS = new java.util.HashSet<>(); + + static { + register("workbench", "essentialsc.command.workbench", "cn.infstar.essentialsC.commands.WorkbenchCommand", "wb"); + register("anvil", "essentialsc.command.anvil", "cn.infstar.essentialsC.commands.AnvilCommand"); + register("cartographytable", "essentialsc.command.cartographytable", "cn.infstar.essentialsC.commands.CartographyTableCommand", "ct", "cartography"); + register("grindstone", "essentialsc.command.grindstone", "cn.infstar.essentialsC.commands.GrindstoneCommand", "gs"); + register("loom", "essentialsc.command.loom", "cn.infstar.essentialsC.commands.LoomCommand"); + register("smithingtable", "essentialsc.command.smithingtable", "cn.infstar.essentialsC.commands.SmithingTableCommand", "st", "smithing"); + register("stonecutter", "essentialsc.command.stonecutter", "cn.infstar.essentialsC.commands.StonecutterCommand", "sc"); + register("enderchest", "essentialsc.command.enderchest", "cn.infstar.essentialsC.commands.EnderChestCommand", "ec"); + register("blocks", "essentialsc.command.blocks", "cn.infstar.essentialsC.commands.BlocksMenuCommand"); + register("hat", "essentialsc.command.hat", "cn.infstar.essentialsC.commands.HatCommand"); + register("suicide", "essentialsc.command.suicide", "cn.infstar.essentialsC.commands.SuicideCommand", "die"); + register("fly", "essentialsc.command.fly", "cn.infstar.essentialsC.commands.FlyCommand"); + register("heal", "essentialsc.command.heal", "cn.infstar.essentialsC.commands.HealCommand"); + register("vanish", "essentialsc.command.vanish", "cn.infstar.essentialsC.commands.VanishCommand", "v"); + register("seen", "essentialsc.command.seen", "cn.infstar.essentialsC.commands.SeenCommand", "info"); + register("feed", "essentialsc.command.feed", "cn.infstar.essentialsC.commands.FeedCommand"); + register("repair", "essentialsc.command.repair", "cn.infstar.essentialsC.commands.RepairCommand", "rep"); + register("mobdrops", "essentialsc.mobdrops.enderman", "cn.infstar.essentialsC.commands.MobDropCommand"); + } + + private CommandRegistry() { + } + + private static void register(String name, String permission, String className, String... aliases) { + List aliasList = List.of(aliases); + CommandSpec spec = new CommandSpec(name, permission, className, aliasList); + COMMANDS.put(name, spec); + ALIAS_TO_COMMAND.put(name, name); + for (String alias : aliasList) { + ALIAS_TO_COMMAND.put(alias, name); + } + } + + public static Collection getCommandSpecs() { + return Collections.unmodifiableCollection(COMMANDS.values()); + } + + public static String resolveCommandName(String input) { + if (input == null) { + return null; + } + return ALIAS_TO_COMMAND.get(input.toLowerCase()); + } + + public static boolean isAvailable(String name) { + return getCommand(name) != null; + } + + public static String getPermission(String name) { + CommandSpec spec = COMMANDS.get(name); + return spec == null ? null : spec.permission(); + } + + public static BaseCommand getCommand(String name) { + String resolvedName = resolveCommandName(name); + if (resolvedName == null) { + return null; + } + + BaseCommand cached = COMMAND_CACHE.get(resolvedName); + if (cached != null) { + return cached; + } + if (UNAVAILABLE_COMMANDS.contains(resolvedName)) { + return null; + } + + CommandSpec spec = COMMANDS.get(resolvedName); + if (spec == null) { + return null; + } + + try { + Class rawClass = Class.forName(spec.className()); + if (!BaseCommand.class.isAssignableFrom(rawClass)) { + UNAVAILABLE_COMMANDS.add(resolvedName); + return null; + } + + Constructor constructor = rawClass.asSubclass(BaseCommand.class).getDeclaredConstructor(); + BaseCommand command = constructor.newInstance(); + COMMAND_CACHE.put(resolvedName, command); + return command; + } catch (ReflectiveOperationException | LinkageError ignored) { + UNAVAILABLE_COMMANDS.add(resolvedName); + return null; + } + } + + public record CommandSpec(String name, String permission, String className, List aliases) { + } +} diff --git a/src/main/java/cn/infstar/essentialsC/commands/HelpCommand.java b/src/main/java/cn/infstar/essentialsC/commands/HelpCommand.java index a25ad9f..eea4b79 100644 --- a/src/main/java/cn/infstar/essentialsC/commands/HelpCommand.java +++ b/src/main/java/cn/infstar/essentialsC/commands/HelpCommand.java @@ -10,44 +10,21 @@ import org.bukkit.entity.Player; import java.util.ArrayList; import java.util.List; +import java.util.Map; public class HelpCommand extends BaseCommand implements TabCompleter { - - // 缓存命令实例,避免重复创建 - static final java.util.Map COMMAND_CACHE = new java.util.HashMap<>(); - - static { - COMMAND_CACHE.put("workbench", new WorkbenchCommand()); - COMMAND_CACHE.put("anvil", new AnvilCommand()); - COMMAND_CACHE.put("cartographytable", new CartographyTableCommand()); - COMMAND_CACHE.put("grindstone", new GrindstoneCommand()); - COMMAND_CACHE.put("loom", new LoomCommand()); - COMMAND_CACHE.put("smithingtable", new SmithingTableCommand()); - COMMAND_CACHE.put("stonecutter", new StonecutterCommand()); - COMMAND_CACHE.put("enderchest", new EnderChestCommand()); - COMMAND_CACHE.put("hat", new HatCommand()); - COMMAND_CACHE.put("suicide", new SuicideCommand()); - COMMAND_CACHE.put("fly", new FlyCommand()); - COMMAND_CACHE.put("heal", new HealCommand()); - COMMAND_CACHE.put("vanish", new VanishCommand()); - COMMAND_CACHE.put("seen", new SeenCommand()); - COMMAND_CACHE.put("feed", new FeedCommand()); - COMMAND_CACHE.put("repair", new RepairCommand()); - COMMAND_CACHE.put("blocks", new BlocksMenuCommand()); - COMMAND_CACHE.put("mobdrops", new MobDropCommand()); - } - + public HelpCommand() { super("essentialsc.command.help"); } - + @Override protected boolean execute(Player player, String[] args) { return handleCommand(player, player, args); } - + @Override - protected boolean executeConsole(org.bukkit.command.CommandSender sender, String[] args) { + protected boolean executeConsole(CommandSender sender, String[] args) { if (args.length > 0 && args[0].equalsIgnoreCase("reload")) { if (!sender.hasPermission("essentialsc.command.reload")) { sender.sendMessage(getLang().getString("messages.no-permission")); @@ -55,17 +32,17 @@ public class HelpCommand extends BaseCommand implements TabCompleter { } plugin.reloadConfig(); EssentialsC.getLangManager().reload(); - sender.sendMessage(getLang().getString("prefix") + "§a配置已重载!"); + sender.sendMessage(getLang().getString("prefix") + "Configuration reloaded."); return true; } sender.sendMessage(getLang().getString("messages.player-only")); return true; } - + private boolean handleCommand(CommandSender sender, Player player, String[] args) { if (args.length > 0) { String subCommand = args[0].toLowerCase(); - + if (subCommand.equals("reload")) { if (!sender.hasPermission("essentialsc.command.reload")) { sender.sendMessage(getLang().getString("messages.no-permission")); @@ -73,167 +50,140 @@ public class HelpCommand extends BaseCommand implements TabCompleter { } plugin.reloadConfig(); EssentialsC.getLangManager().reload(); - sender.sendMessage(getLang().getString("prefix") + "§a配置已重载!"); + sender.sendMessage(getLang().getString("prefix") + "Configuration reloaded."); return true; } - // 功能方块和其他命令 - 使用别名映射 + String actualCommand = getActualCommand(subCommand); - if (actualCommand != null && COMMAND_CACHE.containsKey(actualCommand)) { - String permission = getPermissionForCommand(actualCommand); - if (!player.hasPermission(permission)) { + BaseCommand targetCommand = CommandRegistry.getCommand(actualCommand); + if (actualCommand != null && targetCommand != null) { + String permission = CommandRegistry.getPermission(actualCommand); + if (permission != null && !player.hasPermission(permission)) { player.sendMessage(getLang().getString("messages.no-permission")); return true; } - - // seen 需要特殊处理参数 + if (actualCommand.equals("seen")) { if (args.length < 2) { player.sendMessage(getLang().getString("prefix") + getLang().getString("messages.seen-usage-console")); return true; } - COMMAND_CACHE.get("seen").execute(player, new String[]{args[1]}); + targetCommand.execute(player, new String[]{args[1]}); } else { - COMMAND_CACHE.get(actualCommand).execute(player, new String[]{}); + targetCommand.execute(player, new String[0]); } return true; - } else if (subCommand.equals("version") || subCommand.equals("v")) { - player.sendMessage(getLang().getString("prefix") + "§6EssentialsC §fv" + plugin.getDescription().getVersion()); - player.sendMessage(getLang().getString("prefix") + "§7运行在 Paper " + Bukkit.getVersion()); - return true; - } else { - player.sendMessage(getLang().getString("prefix") + getLang().getString("messages.unknown-subcommand", - java.util.Map.of("command", subCommand))); - player.sendMessage(getLang().getString("prefix") + getLang().getString("messages.help-usage")); + } + + if (subCommand.equals("version") || subCommand.equals("v")) { + player.sendMessage(getLang().getString("prefix") + "EssentialsC v" + plugin.getDescription().getVersion()); + player.sendMessage(getLang().getString("prefix") + "Running on Paper " + Bukkit.getVersion()); return true; } + + player.sendMessage(getLang().getString("prefix") + getLang().getString("messages.unknown-subcommand", + Map.of("command", subCommand))); + player.sendMessage(getLang().getString("prefix") + getLang().getString("messages.help-usage")); + return true; } - - // 显示帮助 + LangManager lang = getLang(); String version = plugin.getDescription().getVersion(); - + player.sendMessage(lang.getString("help.title")); - player.sendMessage(lang.getString("help.version", - java.util.Map.of("version", version))); + player.sendMessage(lang.getString("help.version", Map.of("version", version))); player.sendMessage(""); - - // 功能方块命令(检查权限后显示) + boolean hasBlockCommands = false; StringBuilder blockCommands = new StringBuilder(); - - if (player.hasPermission("essentialsc.command.workbench")) { + + if (CommandRegistry.isAvailable("workbench") && player.hasPermission("essentialsc.command.workbench")) { blockCommands.append(lang.getString("help.commands.workbench")).append("\n"); hasBlockCommands = true; } - if (player.hasPermission("essentialsc.command.anvil")) { + if (CommandRegistry.isAvailable("anvil") && player.hasPermission("essentialsc.command.anvil")) { blockCommands.append(lang.getString("help.commands.anvil")).append("\n"); hasBlockCommands = true; } - if (player.hasPermission("essentialsc.command.cartographytable")) { + if (CommandRegistry.isAvailable("cartographytable") && player.hasPermission("essentialsc.command.cartographytable")) { blockCommands.append(lang.getString("help.commands.cartographytable")).append("\n"); hasBlockCommands = true; } - if (player.hasPermission("essentialsc.command.grindstone")) { + if (CommandRegistry.isAvailable("grindstone") && player.hasPermission("essentialsc.command.grindstone")) { blockCommands.append(lang.getString("help.commands.grindstone")).append("\n"); hasBlockCommands = true; } - if (player.hasPermission("essentialsc.command.loom")) { + if (CommandRegistry.isAvailable("loom") && player.hasPermission("essentialsc.command.loom")) { blockCommands.append(lang.getString("help.commands.loom")).append("\n"); hasBlockCommands = true; } - if (player.hasPermission("essentialsc.command.smithingtable")) { + if (CommandRegistry.isAvailable("smithingtable") && player.hasPermission("essentialsc.command.smithingtable")) { blockCommands.append(lang.getString("help.commands.smithingtable")).append("\n"); hasBlockCommands = true; } - if (player.hasPermission("essentialsc.command.stonecutter")) { + if (CommandRegistry.isAvailable("stonecutter") && player.hasPermission("essentialsc.command.stonecutter")) { blockCommands.append(lang.getString("help.commands.stonecutter")).append("\n"); hasBlockCommands = true; } - if (player.hasPermission("essentialsc.command.enderchest")) { + if (CommandRegistry.isAvailable("enderchest") && player.hasPermission("essentialsc.command.enderchest")) { blockCommands.append(lang.getString("help.commands.enderchest")).append("\n"); hasBlockCommands = true; } - + if (hasBlockCommands) { player.sendMessage(lang.getString("help.section-blocks")); player.sendMessage(blockCommands.toString().trim()); player.sendMessage(""); } - - // 其他命令(检查权限后显示) + boolean hasOtherCommands = false; StringBuilder otherCommands = new StringBuilder(); - - if (player.hasPermission("essentialsc.command.hat")) { + + if (CommandRegistry.isAvailable("hat") && player.hasPermission("essentialsc.command.hat")) { otherCommands.append(lang.getString("help.commands.hat")).append("\n"); hasOtherCommands = true; } - if (player.hasPermission("essentialsc.command.suicide")) { + if (CommandRegistry.isAvailable("suicide") && player.hasPermission("essentialsc.command.suicide")) { otherCommands.append(lang.getString("help.commands.suicide")).append("\n"); hasOtherCommands = true; } - if (player.hasPermission("essentialsc.command.fly")) { + if (CommandRegistry.isAvailable("fly") && player.hasPermission("essentialsc.command.fly")) { otherCommands.append(lang.getString("help.commands.fly")).append("\n"); hasOtherCommands = true; } - if (player.hasPermission("essentialsc.command.heal")) { + if (CommandRegistry.isAvailable("heal") && player.hasPermission("essentialsc.command.heal")) { otherCommands.append(lang.getString("help.commands.heal")).append("\n"); hasOtherCommands = true; } - if (player.hasPermission("essentialsc.command.vanish")) { + if (CommandRegistry.isAvailable("vanish") && player.hasPermission("essentialsc.command.vanish")) { otherCommands.append(lang.getString("help.commands.vanish")).append("\n"); hasOtherCommands = true; } - if (player.hasPermission("essentialsc.command.seen")) { + if (CommandRegistry.isAvailable("seen") && player.hasPermission("essentialsc.command.seen")) { otherCommands.append(lang.getString("help.commands.seen")).append("\n"); hasOtherCommands = true; } - + if (hasOtherCommands) { player.sendMessage(lang.getString("help.section-other")); player.sendMessage(otherCommands.toString().trim()); player.sendMessage(""); } - + player.sendMessage(lang.getString("help.footer")); return true; } - - /** - * 将别名映射到实际命令名 - */ + private String getActualCommand(String alias) { - return switch (alias) { - case "wb" -> "workbench"; - case "cartography", "ct" -> "cartographytable"; - case "gs" -> "grindstone"; - case "smithing", "st" -> "smithingtable"; - case "sc" -> "stonecutter"; - case "ec" -> "enderchest"; - case "die" -> "suicide"; - case "info" -> "seen"; - case "rep" -> "repair"; - default -> alias; - }; + return CommandRegistry.resolveCommandName(alias); } - - /** - * 获取命令对应的权限节点 - */ - private String getPermissionForCommand(String command) { - if (command.equals("mobdrops")) { - return "essentialsc.mobdrops.enderman"; - } - return "essentialsc.command." + command; - } - + @Override public List onTabComplete(CommandSender sender, Command command, String label, String[] args) { if (args.length == 1) { List completions = new ArrayList<>(); String partial = args[0].toLowerCase(); - - // 所有可能的子命令及其权限(包括别名) + String[][] subCommands = { {"reload", "essentialsc.command.reload"}, {"blocks", "essentialsc.command.blocks"}, @@ -269,17 +219,23 @@ public class HelpCommand extends BaseCommand implements TabCompleter { {"version", null}, {"help", null} }; - + for (String[] subCmd : subCommands) { - if (subCmd[0].startsWith(partial)) { - if (subCmd[1] == null || sender.hasPermission(subCmd[1])) { - completions.add(subCmd[0]); - } + if (!subCmd[0].startsWith(partial)) { + continue; + } + + String actualCommand = getActualCommand(subCmd[0]); + boolean available = actualCommand == null || CommandRegistry.isAvailable(actualCommand); + if (available && (subCmd[1] == null || sender.hasPermission(subCmd[1]))) { + completions.add(subCmd[0]); } } - + return completions; - } else if (args.length == 2) { + } + + if (args.length == 2) { String subCmd = args[0].toLowerCase(); if ((subCmd.equals("seen") || subCmd.equals("info")) && sender.hasPermission("essentialsc.command.seen")) { List players = new ArrayList<>(); @@ -292,7 +248,7 @@ public class HelpCommand extends BaseCommand implements TabCompleter { return players; } } - + return new ArrayList<>(); } }