Initial project for 1.9.4+
This commit is contained in:
128
.gitignore
vendored
128
.gitignore
vendored
@ -1,98 +1,38 @@
|
|||||||
# ---> JetBrains
|
|
||||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
|
||||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
|
||||||
|
|
||||||
# User-specific stuff
|
|
||||||
.idea/**/workspace.xml
|
|
||||||
.idea/**/tasks.xml
|
|
||||||
.idea/**/usage.statistics.xml
|
|
||||||
.idea/**/dictionaries
|
|
||||||
.idea/**/shelf
|
|
||||||
|
|
||||||
# AWS User-specific
|
|
||||||
.idea/**/aws.xml
|
|
||||||
|
|
||||||
# Generated files
|
|
||||||
.idea/**/contentModel.xml
|
|
||||||
|
|
||||||
# Sensitive or high-churn files
|
|
||||||
.idea/**/dataSources/
|
|
||||||
.idea/**/dataSources.ids
|
|
||||||
.idea/**/dataSources.local.xml
|
|
||||||
.idea/**/sqlDataSources.xml
|
|
||||||
.idea/**/dynamic.xml
|
|
||||||
.idea/**/uiDesigner.xml
|
|
||||||
.idea/**/dbnavigator.xml
|
|
||||||
|
|
||||||
# Gradle
|
|
||||||
.idea/**/gradle.xml
|
|
||||||
.idea/**/libraries
|
|
||||||
|
|
||||||
# Gradle and Maven with auto-import
|
|
||||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
|
||||||
# since they will be recreated, and may cause churn. Uncomment if using
|
|
||||||
# auto-import.
|
|
||||||
# .idea/artifacts
|
|
||||||
# .idea/compiler.xml
|
|
||||||
# .idea/jarRepositories.xml
|
|
||||||
# .idea/modules.xml
|
|
||||||
# .idea/*.iml
|
|
||||||
# .idea/modules
|
|
||||||
# *.iml
|
|
||||||
# *.ipr
|
|
||||||
|
|
||||||
# CMake
|
|
||||||
cmake-build-*/
|
|
||||||
|
|
||||||
# Mongo Explorer plugin
|
|
||||||
.idea/**/mongoSettings.xml
|
|
||||||
|
|
||||||
# File-based project format
|
|
||||||
*.iws
|
|
||||||
|
|
||||||
# IntelliJ
|
|
||||||
out/
|
|
||||||
|
|
||||||
# mpeltonen/sbt-idea plugin
|
|
||||||
.idea_modules/
|
|
||||||
|
|
||||||
# JIRA plugin
|
|
||||||
atlassian-ide-plugin.xml
|
|
||||||
|
|
||||||
# Cursive Clojure plugin
|
|
||||||
.idea/replstate.xml
|
|
||||||
|
|
||||||
# SonarLint plugin
|
|
||||||
.idea/sonarlint/
|
|
||||||
|
|
||||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
|
||||||
com_crashlytics_export_strings.xml
|
|
||||||
crashlytics.properties
|
|
||||||
crashlytics-build.properties
|
|
||||||
fabric.properties
|
|
||||||
|
|
||||||
# Editor-based Rest Client
|
|
||||||
.idea/httpRequests
|
|
||||||
|
|
||||||
# Android studio 3.1+ serialized cache file
|
|
||||||
.idea/caches/build_file_checksums.ser
|
|
||||||
|
|
||||||
# ---> Maven
|
|
||||||
target/
|
target/
|
||||||
pom.xml.tag
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
pom.xml.releaseBackup
|
!**/src/main/**/target/
|
||||||
pom.xml.versionsBackup
|
!**/src/test/**/target/
|
||||||
pom.xml.next
|
|
||||||
release.properties
|
|
||||||
dependency-reduced-pom.xml
|
|
||||||
buildNumber.properties
|
|
||||||
.mvn/timing.properties
|
|
||||||
# https://github.com/takari/maven-wrapper#usage-without-binary-jar
|
|
||||||
.mvn/wrapper/maven-wrapper.jar
|
|
||||||
|
|
||||||
# Eclipse m2e generated files
|
### IntelliJ IDEA ###
|
||||||
# Eclipse Core
|
.idea/modules.xml
|
||||||
.project
|
.idea/jarRepositories.xml
|
||||||
# JDT-specific (Eclipse Java Development Tools)
|
.idea/compiler.xml
|
||||||
|
.idea/libraries/
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
|
||||||
|
### Eclipse ###
|
||||||
|
.apt_generated
|
||||||
.classpath
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
build/
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
### Mac OS ###
|
||||||
|
.DS_Store
|
||||||
3
.idea/.gitignore
generated
vendored
Normal file
3
.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
7
.idea/encodings.xml
generated
Normal file
7
.idea/encodings.xml
generated
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Encoding">
|
||||||
|
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
|
||||||
|
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
17
.idea/misc.xml
generated
Normal file
17
.idea/misc.xml
generated
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="EntryPointsManager">
|
||||||
|
<list size="1">
|
||||||
|
<item index="0" class="java.lang.String" itemvalue="org.bukkit.event.EventHandler" />
|
||||||
|
</list>
|
||||||
|
</component>
|
||||||
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
|
<component name="MavenProjectsManager">
|
||||||
|
<option name="originalFiles">
|
||||||
|
<list>
|
||||||
|
<option value="$PROJECT_DIR$/pom.xml" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="temurin-1.8" project-jdk-type="JavaSDK" />
|
||||||
|
</project>
|
||||||
124
.idea/uiDesigner.xml
generated
Normal file
124
.idea/uiDesigner.xml
generated
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Palette2">
|
||||||
|
<group name="Swing">
|
||||||
|
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
|
||||||
|
</item>
|
||||||
|
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.svg" removable="false" auto-create-binding="false" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
|
||||||
|
<initial-values>
|
||||||
|
<property name="text" value="Button" />
|
||||||
|
</initial-values>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||||
|
<initial-values>
|
||||||
|
<property name="text" value="RadioButton" />
|
||||||
|
</initial-values>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||||
|
<initial-values>
|
||||||
|
<property name="text" value="CheckBox" />
|
||||||
|
</initial-values>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
|
||||||
|
<initial-values>
|
||||||
|
<property name="text" value="Label" />
|
||||||
|
</initial-values>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||||
|
<preferred-size width="150" height="-1" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||||
|
<preferred-size width="150" height="-1" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||||
|
<preferred-size width="150" height="-1" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||||
|
<preferred-size width="150" height="50" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||||
|
<preferred-size width="150" height="50" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||||
|
<preferred-size width="150" height="50" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||||
|
<preferred-size width="150" height="50" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
|
||||||
|
<preferred-size width="150" height="50" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||||
|
<preferred-size width="150" height="50" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||||
|
<preferred-size width="200" height="200" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||||
|
<preferred-size width="200" height="200" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
|
||||||
|
<preferred-size width="-1" height="20" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
|
||||||
|
</item>
|
||||||
|
</group>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@ -1,3 +1,3 @@
|
|||||||
# FMCCore
|
# FMCCore - ForeverMC 服务器插件核心依赖
|
||||||
|
|
||||||
Minecraft Spigot 插件依赖
|
主分支仅作说明和向导,适用于不同 Minecraft 版本的插件源码在以该版本命名的分支中维护。
|
||||||
|
|||||||
82
pom.xml
Normal file
82
pom.xml
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>cn.forevermc.spigot</groupId>
|
||||||
|
<artifactId>fmc-core</artifactId>
|
||||||
|
<version>0.0.1+${mc.version}</version>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<mc.version>1.9.4+</mc.version>
|
||||||
|
<java.version>8</java.version>
|
||||||
|
<maven.compiler.source>8</maven.compiler.source>
|
||||||
|
<maven.compiler.target>8</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>spigot-repo</id>
|
||||||
|
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
|
<version>3.7.1</version>
|
||||||
|
<configuration>
|
||||||
|
<outputDirectory>E:\SpigotMC\test194\plugins</outputDirectory>
|
||||||
|
<finalName>FMCCore-${version}.jar</finalName>
|
||||||
|
<descriptorRefs>
|
||||||
|
<descriptorRef>jar-with-dependencies</descriptorRef>
|
||||||
|
</descriptorRefs>
|
||||||
|
</configuration>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>single</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.spigotmc</groupId>
|
||||||
|
<artifactId>spigot-api</artifactId>
|
||||||
|
<version>1.9.4-R0.1-SNAPSHOT</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>1.18.34</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.objecthunter</groupId>
|
||||||
|
<artifactId>exp4j</artifactId>
|
||||||
|
<version>0.4.8</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.dom4j</groupId>
|
||||||
|
<artifactId>dom4j</artifactId>
|
||||||
|
<version>2.1.4</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>xml-apis</groupId>
|
||||||
|
<artifactId>xml-apis</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
44
src/main/java/cn/forevermc/spigot/core/FMCCore.java
Normal file
44
src/main/java/cn/forevermc/spigot/core/FMCCore.java
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package cn.forevermc.spigot.core;
|
||||||
|
|
||||||
|
import cn.forevermc.spigot.core.command.FMCCommand;
|
||||||
|
import cn.forevermc.spigot.core.command.FMCCommandExecutor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2024-10-10 15:05
|
||||||
|
*/
|
||||||
|
public final class FMCCore extends JavaPlugin {
|
||||||
|
|
||||||
|
private static final List<FMCPlugin> FMC_PLUGIN_LIST = new ArrayList<>();
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private static FMCCore instance;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private FMCCommand fmcCommand;
|
||||||
|
|
||||||
|
public static <T extends FMCPlugin> void register(T fmcPlugin) {
|
||||||
|
FMC_PLUGIN_LIST.add(fmcPlugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnable() {
|
||||||
|
instance = this;
|
||||||
|
|
||||||
|
fmcCommand = new FMCCommand();
|
||||||
|
|
||||||
|
FMCCommandExecutor commandExecutor = new FMCCommandExecutor();
|
||||||
|
for (int i = 0; i < FMC_PLUGIN_LIST.size(); i++) {
|
||||||
|
FMC_PLUGIN_LIST.get(i).fmcCommand = fmcCommand;
|
||||||
|
}
|
||||||
|
Objects.requireNonNull(getCommand("fmc")).setExecutor(commandExecutor);
|
||||||
|
}
|
||||||
|
}
|
||||||
121
src/main/java/cn/forevermc/spigot/core/FMCPlugin.java
Normal file
121
src/main/java/cn/forevermc/spigot/core/FMCPlugin.java
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
package cn.forevermc.spigot.core;
|
||||||
|
|
||||||
|
import cn.forevermc.spigot.core.bean.ConfigPath;
|
||||||
|
import cn.forevermc.spigot.core.command.FMCCommand;
|
||||||
|
import cn.forevermc.spigot.core.util.Ref;
|
||||||
|
import org.bukkit.configuration.file.FileConfiguration;
|
||||||
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2024-10-19 16:07
|
||||||
|
*/
|
||||||
|
public abstract class FMCPlugin extends JavaPlugin {
|
||||||
|
|
||||||
|
protected FMCCommand fmcCommand;
|
||||||
|
|
||||||
|
public FMCPlugin() {
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final void defaultConfig(String path) {
|
||||||
|
try {
|
||||||
|
InputStream is = getClass().getClassLoader().getResourceAsStream(path);
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
assert is != null;
|
||||||
|
BufferedInputStream bis = new BufferedInputStream(is);
|
||||||
|
InputStreamReader isr = new InputStreamReader(bis, StandardCharsets.UTF_8);
|
||||||
|
char[] buffer = new char[4096];
|
||||||
|
int l;
|
||||||
|
while ((l = isr.read(buffer)) != -1) {
|
||||||
|
sb.append(buffer, 0, l);
|
||||||
|
}
|
||||||
|
isr.close();
|
||||||
|
bis.close();
|
||||||
|
is.close();
|
||||||
|
|
||||||
|
FileConfiguration config = getConfig();
|
||||||
|
YamlConfiguration defConfig = new YamlConfiguration();
|
||||||
|
defConfig.options().indent(4);
|
||||||
|
defConfig.loadFromString(sb.toString());
|
||||||
|
config.addDefaults(defConfig);
|
||||||
|
config.options().copyDefaults(true);
|
||||||
|
super.saveConfig();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final void saveConfigAs(Object obj, FileConfiguration config) {
|
||||||
|
try {
|
||||||
|
serializeConfig(obj, config);
|
||||||
|
super.saveConfig();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("serialize config error", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final <T> T loadConfigAs(Class<T> clazz, FileConfiguration config) {
|
||||||
|
try {
|
||||||
|
T t = Ref.newInstance(clazz);
|
||||||
|
deserializeConfig(t, config);
|
||||||
|
return t;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("deserialize config error", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void serializeConfig(Object obj, FileConfiguration config) throws Exception {
|
||||||
|
List<Field> fieldList = Ref.listFields(obj.getClass());
|
||||||
|
for (int i = 0; i < fieldList.size(); i++) {
|
||||||
|
Field field = fieldList.get(i);
|
||||||
|
ConfigPath configPath = field.getAnnotation(ConfigPath.class);
|
||||||
|
if (configPath == null) {
|
||||||
|
serializeConfig(Ref.getFieldValue(obj, field, Object.class), config);
|
||||||
|
} else {
|
||||||
|
if (Map.class.isAssignableFrom(field.getType())) {
|
||||||
|
Map<?, ?> map = Ref.getFieldValue(obj, field, Map.class);
|
||||||
|
map.forEach((key, value) -> config.set(configPath.value() + "." + key.toString(), value));
|
||||||
|
} else {
|
||||||
|
config.set(configPath.value(), Ref.getFieldValue(obj, field, Object.class));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deserializeConfig(Object obj, FileConfiguration config) throws Exception {
|
||||||
|
List<Field> fieldList = Ref.listFields(obj.getClass());
|
||||||
|
for (int i = 0; i < fieldList.size(); i++) {
|
||||||
|
Field field = fieldList.get(i);
|
||||||
|
ConfigPath configPath = field.getAnnotation(ConfigPath.class);
|
||||||
|
if (configPath == null) {
|
||||||
|
Ref.setFieldValue(obj, field, Ref.newInstance(field.getType()));
|
||||||
|
deserializeConfig(field.get(obj), config);
|
||||||
|
} else {
|
||||||
|
String configPathVal = configPath.value();
|
||||||
|
if (Map.class.isAssignableFrom(field.getType())) {
|
||||||
|
Map<String, Object> map = new HashMap<>();
|
||||||
|
Set<String> keys = config.getKeys(true);
|
||||||
|
for (String key : keys) {
|
||||||
|
if (key.length() != configPathVal.length() && key.startsWith(configPathVal)) {
|
||||||
|
map.put(key.substring(configPathVal.length() + 1), config.get(key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ref.setFieldValue(obj, field, map);
|
||||||
|
} else {
|
||||||
|
Ref.setFieldValue(obj, field, config.get(configPathVal));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
437
src/main/java/cn/forevermc/spigot/core/bean/BoundingBox.java
Normal file
437
src/main/java/cn/forevermc/spigot/core/bean/BoundingBox.java
Normal file
@ -0,0 +1,437 @@
|
|||||||
|
package cn.forevermc.spigot.core.bean;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.apache.commons.lang.Validate;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.block.Block;
|
||||||
|
import org.bukkit.configuration.serialization.ConfigurationSerializable;
|
||||||
|
import org.bukkit.configuration.serialization.SerializableAs;
|
||||||
|
import org.bukkit.util.NumberConversions;
|
||||||
|
import org.bukkit.util.Vector;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/** Copy for 1.13.2 org.bukkit.util.BoundingBox */
|
||||||
|
@Getter
|
||||||
|
@SerializableAs("BoundingBox")
|
||||||
|
public class BoundingBox implements Cloneable, ConfigurationSerializable {
|
||||||
|
|
||||||
|
private double minX;
|
||||||
|
private double minY;
|
||||||
|
private double minZ;
|
||||||
|
private double maxX;
|
||||||
|
private double maxY;
|
||||||
|
private double maxZ;
|
||||||
|
|
||||||
|
public static BoundingBox of(Vector corner1, Vector corner2) {
|
||||||
|
Validate.notNull(corner1, "Corner1 is null!");
|
||||||
|
Validate.notNull(corner2, "Corner2 is null!");
|
||||||
|
return new BoundingBox(corner1.getX(), corner1.getY(), corner1.getZ(), corner2.getX(), corner2.getY(), corner2.getZ());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BoundingBox of(Location corner1, Location corner2) {
|
||||||
|
Validate.notNull(corner1, "Corner1 is null!");
|
||||||
|
Validate.notNull(corner2, "Corner2 is null!");
|
||||||
|
Validate.isTrue(Objects.equals(corner1.getWorld(), corner2.getWorld()), "Locations from different worlds!");
|
||||||
|
return new BoundingBox(corner1.getX(), corner1.getY(), corner1.getZ(), corner2.getX(), corner2.getY(), corner2.getZ());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BoundingBox of(Block corner1, Block corner2) {
|
||||||
|
Validate.notNull(corner1, "Corner1 is null!");
|
||||||
|
Validate.notNull(corner2, "Corner2 is null!");
|
||||||
|
Validate.isTrue(Objects.equals(corner1.getWorld(), corner2.getWorld()), "Blocks from different worlds!");
|
||||||
|
int x1 = corner1.getX();
|
||||||
|
int y1 = corner1.getY();
|
||||||
|
int z1 = corner1.getZ();
|
||||||
|
int x2 = corner2.getX();
|
||||||
|
int y2 = corner2.getY();
|
||||||
|
int z2 = corner2.getZ();
|
||||||
|
int minX = Math.min(x1, x2);
|
||||||
|
int minY = Math.min(y1, y2);
|
||||||
|
int minZ = Math.min(z1, z2);
|
||||||
|
int maxX = Math.max(x1, x2) + 1;
|
||||||
|
int maxY = Math.max(y1, y2) + 1;
|
||||||
|
int maxZ = Math.max(z1, z2) + 1;
|
||||||
|
return new BoundingBox(minX, minY, minZ, maxX, maxY, maxZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BoundingBox of(Block block) {
|
||||||
|
Validate.notNull(block, "Block is null!");
|
||||||
|
return new BoundingBox(block.getX(), block.getY(), block.getZ(), block.getX() + 1, block.getY() + 1, block.getZ() + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BoundingBox of(Vector center, double x, double y, double z) {
|
||||||
|
Validate.notNull(center, "Center is null!");
|
||||||
|
return new BoundingBox(center.getX() - x, center.getY() - y, center.getZ() - z, center.getX() + x, center.getY() + y, center.getZ() + z);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BoundingBox of(Location center, double x, double y, double z) {
|
||||||
|
Validate.notNull(center, "Center is null!");
|
||||||
|
return new BoundingBox(center.getX() - x, center.getY() - y, center.getZ() - z, center.getX() + x, center.getY() + y, center.getZ() + z);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BoundingBox() {
|
||||||
|
this.resize(0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BoundingBox(double x1, double y1, double z1, double x2, double y2, double z2) {
|
||||||
|
this.resize(x1, y1, z1, x2, y2, z2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BoundingBox resize(double x1, double y1, double z1, double x2, double y2, double z2) {
|
||||||
|
NumberConversions.checkFinite(x1, "x1 not finite");
|
||||||
|
NumberConversions.checkFinite(y1, "y1 not finite");
|
||||||
|
NumberConversions.checkFinite(z1, "z1 not finite");
|
||||||
|
NumberConversions.checkFinite(x2, "x2 not finite");
|
||||||
|
NumberConversions.checkFinite(y2, "y2 not finite");
|
||||||
|
NumberConversions.checkFinite(z2, "z2 not finite");
|
||||||
|
this.minX = Math.min(x1, x2);
|
||||||
|
this.minY = Math.min(y1, y2);
|
||||||
|
this.minZ = Math.min(z1, z2);
|
||||||
|
this.maxX = Math.max(x1, x2);
|
||||||
|
this.maxY = Math.max(y1, y2);
|
||||||
|
this.maxZ = Math.max(z1, z2);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector getMin() {
|
||||||
|
return new Vector(this.minX, this.minY, this.minZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector getMax() {
|
||||||
|
return new Vector(this.maxX, this.maxY, this.maxZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getWidthX() {
|
||||||
|
return this.maxX - this.minX;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getWidthZ() {
|
||||||
|
return this.maxZ - this.minZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getHeight() {
|
||||||
|
return this.maxY - this.minY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getVolume() {
|
||||||
|
return this.getHeight() * this.getWidthX() * this.getWidthZ();
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getCenterX() {
|
||||||
|
return this.minX + this.getWidthX() * 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getCenterY() {
|
||||||
|
return this.minY + this.getHeight() * 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getCenterZ() {
|
||||||
|
return this.minZ + this.getWidthZ() * 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector getCenter() {
|
||||||
|
return new Vector(this.getCenterX(), this.getCenterY(), this.getCenterZ());
|
||||||
|
}
|
||||||
|
|
||||||
|
public BoundingBox copy(BoundingBox other) {
|
||||||
|
Validate.notNull(other, "Other bounding box is null!");
|
||||||
|
return this.resize(other.getMinX(), other.getMinY(), other.getMinZ(), other.getMaxX(), other.getMaxY(), other.getMaxZ());
|
||||||
|
}
|
||||||
|
|
||||||
|
public BoundingBox expand(double negativeX, double negativeY, double negativeZ, double positiveX, double positiveY, double positiveZ) {
|
||||||
|
if (negativeX == 0.0 && negativeY == 0.0 && negativeZ == 0.0 && positiveX == 0.0 && positiveY == 0.0 && positiveZ == 0.0) {
|
||||||
|
return this;
|
||||||
|
} else {
|
||||||
|
double newMinX = this.minX - negativeX;
|
||||||
|
double newMinY = this.minY - negativeY;
|
||||||
|
double newMinZ = this.minZ - negativeZ;
|
||||||
|
double newMaxX = this.maxX + positiveX;
|
||||||
|
double newMaxY = this.maxY + positiveY;
|
||||||
|
double newMaxZ = this.maxZ + positiveZ;
|
||||||
|
double centerZ;
|
||||||
|
if (newMinX > newMaxX) {
|
||||||
|
centerZ = this.getCenterX();
|
||||||
|
if (newMaxX >= centerZ) {
|
||||||
|
newMinX = newMaxX;
|
||||||
|
} else if (newMinX <= centerZ) {
|
||||||
|
newMaxX = newMinX;
|
||||||
|
} else {
|
||||||
|
newMinX = centerZ;
|
||||||
|
newMaxX = centerZ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (newMinY > newMaxY) {
|
||||||
|
centerZ = this.getCenterY();
|
||||||
|
if (newMaxY >= centerZ) {
|
||||||
|
newMinY = newMaxY;
|
||||||
|
} else if (newMinY <= centerZ) {
|
||||||
|
newMaxY = newMinY;
|
||||||
|
} else {
|
||||||
|
newMinY = centerZ;
|
||||||
|
newMaxY = centerZ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (newMinZ > newMaxZ) {
|
||||||
|
centerZ = this.getCenterZ();
|
||||||
|
if (newMaxZ >= centerZ) {
|
||||||
|
newMinZ = newMaxZ;
|
||||||
|
} else if (newMinZ <= centerZ) {
|
||||||
|
newMaxZ = newMinZ;
|
||||||
|
} else {
|
||||||
|
newMinZ = centerZ;
|
||||||
|
newMaxZ = centerZ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.resize(newMinX, newMinY, newMinZ, newMaxX, newMaxY, newMaxZ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public BoundingBox expand(double x, double y, double z) {
|
||||||
|
return this.expand(x, y, z, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BoundingBox expand(Vector expansion) {
|
||||||
|
Validate.notNull(expansion, "Expansion is null!");
|
||||||
|
double x = expansion.getX();
|
||||||
|
double y = expansion.getY();
|
||||||
|
double z = expansion.getZ();
|
||||||
|
return this.expand(x, y, z, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BoundingBox expand(double expansion) {
|
||||||
|
return this.expand(expansion, expansion, expansion, expansion, expansion, expansion);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BoundingBox expand(double dirX, double dirY, double dirZ, double expansion) {
|
||||||
|
if (expansion == 0.0) {
|
||||||
|
return this;
|
||||||
|
} else if (dirX == 0.0 && dirY == 0.0 && dirZ == 0.0) {
|
||||||
|
return this;
|
||||||
|
} else {
|
||||||
|
double negativeX = dirX < 0.0 ? -dirX * expansion : 0.0;
|
||||||
|
double negativeY = dirY < 0.0 ? -dirY * expansion : 0.0;
|
||||||
|
double negativeZ = dirZ < 0.0 ? -dirZ * expansion : 0.0;
|
||||||
|
double positiveX = dirX > 0.0 ? dirX * expansion : 0.0;
|
||||||
|
double positiveY = dirY > 0.0 ? dirY * expansion : 0.0;
|
||||||
|
double positiveZ = dirZ > 0.0 ? dirZ * expansion : 0.0;
|
||||||
|
return this.expand(negativeX, negativeY, negativeZ, positiveX, positiveY, positiveZ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public BoundingBox expand(Vector direction, double expansion) {
|
||||||
|
Validate.notNull(direction, "Direction is null!");
|
||||||
|
return this.expand(direction.getX(), direction.getY(), direction.getZ(), expansion);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BoundingBox expandDirectional(double dirX, double dirY, double dirZ) {
|
||||||
|
return this.expand(dirX, dirY, dirZ, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BoundingBox expandDirectional(Vector direction) {
|
||||||
|
Validate.notNull(direction, "Expansion is null!");
|
||||||
|
return this.expand(direction.getX(), direction.getY(), direction.getZ(), 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BoundingBox union(double posX, double posY, double posZ) {
|
||||||
|
double newMinX = Math.min(this.minX, posX);
|
||||||
|
double newMinY = Math.min(this.minY, posY);
|
||||||
|
double newMinZ = Math.min(this.minZ, posZ);
|
||||||
|
double newMaxX = Math.max(this.maxX, posX);
|
||||||
|
double newMaxY = Math.max(this.maxY, posY);
|
||||||
|
double newMaxZ = Math.max(this.maxZ, posZ);
|
||||||
|
return newMinX == this.minX && newMinY == this.minY && newMinZ == this.minZ && newMaxX == this.maxX && newMaxY == this.maxY && newMaxZ == this.maxZ ? this : this.resize(newMinX, newMinY, newMinZ, newMaxX, newMaxY, newMaxZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BoundingBox union(Vector position) {
|
||||||
|
Validate.notNull(position, "Position is null!");
|
||||||
|
return this.union(position.getX(), position.getY(), position.getZ());
|
||||||
|
}
|
||||||
|
|
||||||
|
public BoundingBox union(Location position) {
|
||||||
|
Validate.notNull(position, "Position is null!");
|
||||||
|
return this.union(position.getX(), position.getY(), position.getZ());
|
||||||
|
}
|
||||||
|
|
||||||
|
public BoundingBox union(BoundingBox other) {
|
||||||
|
Validate.notNull(other, "Other bounding box is null!");
|
||||||
|
if (this.contains(other)) {
|
||||||
|
return this;
|
||||||
|
} else {
|
||||||
|
double newMinX = Math.min(this.minX, other.minX);
|
||||||
|
double newMinY = Math.min(this.minY, other.minY);
|
||||||
|
double newMinZ = Math.min(this.minZ, other.minZ);
|
||||||
|
double newMaxX = Math.max(this.maxX, other.maxX);
|
||||||
|
double newMaxY = Math.max(this.maxY, other.maxY);
|
||||||
|
double newMaxZ = Math.max(this.maxZ, other.maxZ);
|
||||||
|
return this.resize(newMinX, newMinY, newMinZ, newMaxX, newMaxY, newMaxZ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public BoundingBox intersection(BoundingBox other) {
|
||||||
|
Validate.notNull(other, "Other bounding box is null!");
|
||||||
|
Validate.isTrue(this.overlaps(other), "The bounding boxes do not overlap!");
|
||||||
|
double newMinX = Math.max(this.minX, other.minX);
|
||||||
|
double newMinY = Math.max(this.minY, other.minY);
|
||||||
|
double newMinZ = Math.max(this.minZ, other.minZ);
|
||||||
|
double newMaxX = Math.min(this.maxX, other.maxX);
|
||||||
|
double newMaxY = Math.min(this.maxY, other.maxY);
|
||||||
|
double newMaxZ = Math.min(this.maxZ, other.maxZ);
|
||||||
|
return this.resize(newMinX, newMinY, newMinZ, newMaxX, newMaxY, newMaxZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BoundingBox shift(double shiftX, double shiftY, double shiftZ) {
|
||||||
|
return shiftX == 0.0 && shiftY == 0.0 && shiftZ == 0.0 ? this : this.resize(this.minX + shiftX, this.minY + shiftY, this.minZ + shiftZ, this.maxX + shiftX, this.maxY + shiftY, this.maxZ + shiftZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BoundingBox shift(Vector shift) {
|
||||||
|
Validate.notNull(shift, "Shift is null!");
|
||||||
|
return this.shift(shift.getX(), shift.getY(), shift.getZ());
|
||||||
|
}
|
||||||
|
|
||||||
|
public BoundingBox shift(Location shift) {
|
||||||
|
Validate.notNull(shift, "Shift is null!");
|
||||||
|
return this.shift(shift.getX(), shift.getY(), shift.getZ());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean overlaps(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) {
|
||||||
|
return this.minX < maxX && this.maxX > minX && this.minY < maxY && this.maxY > minY && this.minZ < maxZ && this.maxZ > minZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean overlaps(BoundingBox other) {
|
||||||
|
Validate.notNull(other, "Other bounding box is null!");
|
||||||
|
return this.overlaps(other.minX, other.minY, other.minZ, other.maxX, other.maxY, other.maxZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean overlaps(Vector min, Vector max) {
|
||||||
|
Validate.notNull(min, "Min is null!");
|
||||||
|
Validate.notNull(max, "Max is null!");
|
||||||
|
double x1 = min.getX();
|
||||||
|
double y1 = min.getY();
|
||||||
|
double z1 = min.getZ();
|
||||||
|
double x2 = max.getX();
|
||||||
|
double y2 = max.getY();
|
||||||
|
double z2 = max.getZ();
|
||||||
|
return this.overlaps(Math.min(x1, x2), Math.min(y1, y2), Math.min(z1, z2), Math.max(x1, x2), Math.max(y1, y2), Math.max(z1, z2));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean contains(double x, double y, double z) {
|
||||||
|
return x >= this.minX && x < this.maxX && y >= this.minY && y < this.maxY && z >= this.minZ && z < this.maxZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean contains(Vector position) {
|
||||||
|
Validate.notNull(position, "Position is null!");
|
||||||
|
return this.contains(position.getX(), position.getY(), position.getZ());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean contains(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) {
|
||||||
|
return this.minX <= minX && this.maxX >= maxX && this.minY <= minY && this.maxY >= maxY && this.minZ <= minZ && this.maxZ >= maxZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean contains(BoundingBox other) {
|
||||||
|
Validate.notNull(other, "Other bounding box is null!");
|
||||||
|
return this.contains(other.minX, other.minY, other.minZ, other.maxX, other.maxY, other.maxZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean contains(Vector min, Vector max) {
|
||||||
|
Validate.notNull(min, "Min is null!");
|
||||||
|
Validate.notNull(max, "Max is null!");
|
||||||
|
double x1 = min.getX();
|
||||||
|
double y1 = min.getY();
|
||||||
|
double z1 = min.getZ();
|
||||||
|
double x2 = max.getX();
|
||||||
|
double y2 = max.getY();
|
||||||
|
double z2 = max.getZ();
|
||||||
|
return this.contains(Math.min(x1, x2), Math.min(y1, y2), Math.min(z1, z2), Math.max(x1, x2), Math.max(y1, y2), Math.max(z1, z2));
|
||||||
|
}
|
||||||
|
|
||||||
|
public int hashCode() {
|
||||||
|
int result = 1;
|
||||||
|
long temp = Double.doubleToLongBits(this.maxX);
|
||||||
|
result = 31 * result + (int) (temp ^ temp >>> 32);
|
||||||
|
temp = Double.doubleToLongBits(this.maxY);
|
||||||
|
result = 31 * result + (int) (temp ^ temp >>> 32);
|
||||||
|
temp = Double.doubleToLongBits(this.maxZ);
|
||||||
|
result = 31 * result + (int) (temp ^ temp >>> 32);
|
||||||
|
temp = Double.doubleToLongBits(this.minX);
|
||||||
|
result = 31 * result + (int) (temp ^ temp >>> 32);
|
||||||
|
temp = Double.doubleToLongBits(this.minY);
|
||||||
|
result = 31 * result + (int) (temp ^ temp >>> 32);
|
||||||
|
temp = Double.doubleToLongBits(this.minZ);
|
||||||
|
result = 31 * result + (int) (temp ^ temp >>> 32);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
} else if (!(obj instanceof BoundingBox)) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
BoundingBox other = (BoundingBox) obj;
|
||||||
|
if (Double.doubleToLongBits(this.maxX) != Double.doubleToLongBits(other.maxX)) {
|
||||||
|
return false;
|
||||||
|
} else if (Double.doubleToLongBits(this.maxY) != Double.doubleToLongBits(other.maxY)) {
|
||||||
|
return false;
|
||||||
|
} else if (Double.doubleToLongBits(this.maxZ) != Double.doubleToLongBits(other.maxZ)) {
|
||||||
|
return false;
|
||||||
|
} else if (Double.doubleToLongBits(this.minX) != Double.doubleToLongBits(other.minX)) {
|
||||||
|
return false;
|
||||||
|
} else if (Double.doubleToLongBits(this.minY) != Double.doubleToLongBits(other.minY)) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return Double.doubleToLongBits(this.minZ) == Double.doubleToLongBits(other.minZ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public BoundingBox clone() {
|
||||||
|
try {
|
||||||
|
return (BoundingBox) super.clone();
|
||||||
|
} catch (CloneNotSupportedException var2) {
|
||||||
|
throw new Error(var2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> serialize() {
|
||||||
|
Map<String, Object> result = new LinkedHashMap<>();
|
||||||
|
result.put("minX", this.minX);
|
||||||
|
result.put("minY", this.minY);
|
||||||
|
result.put("minZ", this.minZ);
|
||||||
|
result.put("maxX", this.maxX);
|
||||||
|
result.put("maxY", this.maxY);
|
||||||
|
result.put("maxZ", this.maxZ);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BoundingBox deserialize(Map<String, Object> args) {
|
||||||
|
double minX = 0.0;
|
||||||
|
double minY = 0.0;
|
||||||
|
double minZ = 0.0;
|
||||||
|
double maxX = 0.0;
|
||||||
|
double maxY = 0.0;
|
||||||
|
double maxZ = 0.0;
|
||||||
|
if (args.containsKey("minX")) {
|
||||||
|
minX = ((Number) args.get("minX")).doubleValue();
|
||||||
|
}
|
||||||
|
if (args.containsKey("minY")) {
|
||||||
|
minY = ((Number) args.get("minY")).doubleValue();
|
||||||
|
}
|
||||||
|
if (args.containsKey("minZ")) {
|
||||||
|
minZ = ((Number) args.get("minZ")).doubleValue();
|
||||||
|
}
|
||||||
|
if (args.containsKey("maxX")) {
|
||||||
|
maxX = ((Number) args.get("maxX")).doubleValue();
|
||||||
|
}
|
||||||
|
if (args.containsKey("maxY")) {
|
||||||
|
maxY = ((Number) args.get("maxY")).doubleValue();
|
||||||
|
}
|
||||||
|
if (args.containsKey("maxZ")) {
|
||||||
|
maxZ = ((Number) args.get("maxZ")).doubleValue();
|
||||||
|
}
|
||||||
|
return new BoundingBox(minX, minY, minZ, maxX, maxY, maxZ);
|
||||||
|
}
|
||||||
|
}
|
||||||
17
src/main/java/cn/forevermc/spigot/core/bean/ConfigPath.java
Normal file
17
src/main/java/cn/forevermc/spigot/core/bean/ConfigPath.java
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package cn.forevermc.spigot.core.bean;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2025-01-19 21:57
|
||||||
|
*/
|
||||||
|
@Target(ElementType.FIELD)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface ConfigPath {
|
||||||
|
|
||||||
|
String value();
|
||||||
|
}
|
||||||
130
src/main/java/cn/forevermc/spigot/core/bean/NameEntity.java
Normal file
130
src/main/java/cn/forevermc/spigot/core/bean/NameEntity.java
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
package cn.forevermc.spigot.core.bean;
|
||||||
|
|
||||||
|
import cn.forevermc.spigot.core.FMCCore;
|
||||||
|
import cn.forevermc.spigot.core.util.Util;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.entity.Entity;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 命名化实体,用于绑定和持久化储存
|
||||||
|
*
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2024-08-29 16:50
|
||||||
|
*/
|
||||||
|
public class NameEntity {
|
||||||
|
|
||||||
|
/** 实体 UUID */
|
||||||
|
@Getter
|
||||||
|
protected String uuid;
|
||||||
|
|
||||||
|
/** 名称 */
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
|
protected String name;
|
||||||
|
|
||||||
|
/** 显示名称 */
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
|
protected String displayName;
|
||||||
|
|
||||||
|
/** 类型 */
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
|
protected EntityType type;
|
||||||
|
|
||||||
|
/** 位置 */
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
|
protected Location location;
|
||||||
|
|
||||||
|
/** true 为启用 */
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
|
protected boolean enable;
|
||||||
|
|
||||||
|
/** UUID */
|
||||||
|
protected transient UUID uuidObj;
|
||||||
|
|
||||||
|
/** 实体(禁用或未生成时可能不存在) */
|
||||||
|
@Getter
|
||||||
|
protected transient Entity entity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发光
|
||||||
|
*
|
||||||
|
* @param ttl 持续时间(TPS)
|
||||||
|
*/
|
||||||
|
public void glowing(int ttl) {
|
||||||
|
entity.setGlowing(true);
|
||||||
|
Bukkit.getScheduler().runTaskLater(FMCCore.getInstance(), () -> entity.setGlowing(false), ttl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 传送至新的位置
|
||||||
|
*
|
||||||
|
* @param location 位置
|
||||||
|
*/
|
||||||
|
public void teleportTo(Location location) {
|
||||||
|
this.location = location;
|
||||||
|
entity.teleport(location);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return true 为存活于世界中 */
|
||||||
|
public boolean isAlive() {
|
||||||
|
return get() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 生成实体 */
|
||||||
|
public Entity spawn() {
|
||||||
|
Entity entity = Objects.requireNonNull(location.getWorld()).spawnEntity(location, type);
|
||||||
|
entity.setCustomName(displayName);
|
||||||
|
entity.setCustomNameVisible(true);
|
||||||
|
entity.setInvulnerable(true);
|
||||||
|
this.uuidObj = entity.getUniqueId();
|
||||||
|
this.uuid = uuidObj.toString();
|
||||||
|
this.entity = entity;
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 重新生成 */
|
||||||
|
public Entity respawn() {
|
||||||
|
kill();
|
||||||
|
return spawn();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return 获取实体 */
|
||||||
|
public Entity get() {
|
||||||
|
return Util.getEntityByUID(getUID());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return 实体 UUID */
|
||||||
|
public UUID getUID() {
|
||||||
|
if (uuidObj == null) {
|
||||||
|
uuidObj = UUID.fromString(uuid);
|
||||||
|
}
|
||||||
|
return uuidObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 击杀 */
|
||||||
|
public void kill() {
|
||||||
|
get().remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置实体对象
|
||||||
|
*
|
||||||
|
* @param entity 实体
|
||||||
|
*/
|
||||||
|
public void setEntity(Entity entity) {
|
||||||
|
this.entity = entity;
|
||||||
|
this.uuidObj = entity.getUniqueId();
|
||||||
|
this.uuid = this.uuidObj.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
179
src/main/java/cn/forevermc/spigot/core/bean/Region.java
Normal file
179
src/main/java/cn/forevermc/spigot/core/bean/Region.java
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
package cn.forevermc.spigot.core.bean;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.World;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 区域(两点产生的区间,可能是线、面、长方体)
|
||||||
|
*
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2024-08-19 00:38
|
||||||
|
*/
|
||||||
|
public class Region implements Cloneable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 遍历区域回调
|
||||||
|
*
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2024-08-19 15:06
|
||||||
|
*/
|
||||||
|
public interface ForEachCallback {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行器
|
||||||
|
*
|
||||||
|
* @param world 世界
|
||||||
|
* @param x X 坐标
|
||||||
|
* @param y Y 坐标
|
||||||
|
* @param z Z 坐标
|
||||||
|
*/
|
||||||
|
void handler(World world, double x, double y, double z);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 区域顶点 */
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
|
private Location left, right;
|
||||||
|
|
||||||
|
/** 碰撞箱 */
|
||||||
|
private transient BoundingBox boundingBox;
|
||||||
|
|
||||||
|
/** 安全区域(必须已选两点且同一世界) */
|
||||||
|
private void assetSafeLocation() {
|
||||||
|
if (isFull() && !Objects.equals(left.getWorld(), right.getWorld())) {
|
||||||
|
throw new IllegalArgumentException("无法执行:选区不完整或世界不一致");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 遍历区域
|
||||||
|
*
|
||||||
|
* @param callback 遍历回调
|
||||||
|
*/
|
||||||
|
public void forEach(ForEachCallback callback) {
|
||||||
|
final World world = left.getWorld();
|
||||||
|
if (world == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
assetSafeLocation();
|
||||||
|
|
||||||
|
final int x1 = left.getBlockX();
|
||||||
|
final int y1 = left.getBlockY();
|
||||||
|
final int z1 = left.getBlockZ();
|
||||||
|
final int x2 = right.getBlockX();
|
||||||
|
final int y2 = right.getBlockY();
|
||||||
|
final int z2 = right.getBlockZ();
|
||||||
|
|
||||||
|
for (int x = Math.min(x1, x2); x <= Math.max(x1, x2); x++) {
|
||||||
|
for (int y = Math.min(y1, y2); y <= Math.max(y1, y2); y++) {
|
||||||
|
for (int z = Math.min(z1, z2); z <= Math.max(z1, z2); z++) {
|
||||||
|
callback.handler(world, x, y, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 坐标是否在区域内
|
||||||
|
*
|
||||||
|
* @param location 位置
|
||||||
|
* @return true 为该坐标在区域内
|
||||||
|
*/
|
||||||
|
public boolean contain(Location location) {
|
||||||
|
return contain(location.getX(), location.getY(), location.getZ());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 坐标是否在区域内
|
||||||
|
*
|
||||||
|
* @param x X 坐标
|
||||||
|
* @param y Y 坐标
|
||||||
|
* @param z Z 坐标
|
||||||
|
* @return true 为该坐标在区域内
|
||||||
|
*/
|
||||||
|
public boolean contain(double x, double y, double z) {
|
||||||
|
if (!isFull()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
assetSafeLocation();
|
||||||
|
return getBoundingBox().contains(x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return 获取区域碰撞箱 */
|
||||||
|
public BoundingBox getBoundingBox() {
|
||||||
|
assetSafeLocation();
|
||||||
|
if (boundingBox == null) {
|
||||||
|
final int x1 = left.getBlockX();
|
||||||
|
final int z1 = left.getBlockZ();
|
||||||
|
final int y1 = left.getBlockY();
|
||||||
|
final int x2 = right.getBlockX();
|
||||||
|
final int y2 = right.getBlockY();
|
||||||
|
final int z2 = right.getBlockZ();
|
||||||
|
|
||||||
|
final double minX = Math.min(x1, x2);
|
||||||
|
final double minY = Math.min(y1, y2);
|
||||||
|
final double minZ = Math.min(z1, z2);
|
||||||
|
final double maxX = Math.max(x1, x2) + .99;
|
||||||
|
final double maxY = Math.max(y1, y2) + .99;
|
||||||
|
final double maxZ = Math.max(z1, z2) + .99;
|
||||||
|
|
||||||
|
boundingBox = new BoundingBox(minX, minY, minZ, maxX, maxY, maxZ);
|
||||||
|
}
|
||||||
|
return boundingBox;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return true 为没有选择区域 */
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return left == null && right == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return true 为仅选择一点 */
|
||||||
|
public boolean isHalf() {
|
||||||
|
return !isEmpty() && !isFull();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return true 为已选择两点区域 */
|
||||||
|
public boolean isFull() {
|
||||||
|
return left != null && right != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 清空顶点 */
|
||||||
|
public void clear() {
|
||||||
|
left = right = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return 获取顶点 1 */
|
||||||
|
public Location getP1() {
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return 获取顶点 2 */
|
||||||
|
public Location getP2() {
|
||||||
|
return right;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return 中间位置 */
|
||||||
|
public Location getCenter() {
|
||||||
|
double centerX = (left.getX() + right.getX()) * .5;
|
||||||
|
double centerY = (left.getY() + right.getY()) * .5;
|
||||||
|
double centerZ = (left.getZ() + right.getZ()) * .5;
|
||||||
|
return new Location(left.getWorld(), centerX, centerY, centerZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return 克隆区域 */
|
||||||
|
@Override
|
||||||
|
public Region clone() {
|
||||||
|
try {
|
||||||
|
Region clone = (Region) super.clone();
|
||||||
|
clone.left = left.clone();
|
||||||
|
clone.right = right.clone();
|
||||||
|
return clone;
|
||||||
|
} catch (CloneNotSupportedException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,138 @@
|
|||||||
|
package cn.forevermc.spigot.core.command;
|
||||||
|
|
||||||
|
import cn.forevermc.spigot.core.FMCCore;
|
||||||
|
import cn.forevermc.spigot.core.exception.ArgsValueException;
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.Particle;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.command.ConsoleCommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 抽象指令
|
||||||
|
*
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2024-08-15 09:59
|
||||||
|
*/
|
||||||
|
public abstract class AbstractCommand {
|
||||||
|
|
||||||
|
/** 指令参数提示(子级) */
|
||||||
|
protected final Map<Integer, Collection<String>> argsTabCompleterMap = new HashMap<>();
|
||||||
|
|
||||||
|
/** 子级 */
|
||||||
|
protected final Map<String, AbstractCommand> childrenCommandMap = new HashMap<>();
|
||||||
|
|
||||||
|
// ---------- 实时参数(每次触发更新) ----------
|
||||||
|
|
||||||
|
/** 发送玩家 */
|
||||||
|
protected Player senderPlayer;
|
||||||
|
|
||||||
|
/** 动作 */
|
||||||
|
protected List<String> actionList;
|
||||||
|
|
||||||
|
/** 发送者 */
|
||||||
|
protected CommandSender sender;
|
||||||
|
|
||||||
|
/** 触发指令,由 {@link FMCCommandExecutor} 调度 */
|
||||||
|
final void run0(CommandSender sender, String[] args) {
|
||||||
|
this.sender = sender;
|
||||||
|
// 发送对象
|
||||||
|
if (!(this instanceof ConsolePlayerCommand)) {
|
||||||
|
if (this instanceof PlayerCommand && !Player.class.isAssignableFrom(sender.getClass())) {
|
||||||
|
error("不允许的执行对象");
|
||||||
|
} else {
|
||||||
|
senderPlayer = (Player) sender;
|
||||||
|
// 权限
|
||||||
|
if (this instanceof OPCommand && !senderPlayer.isOp()) {
|
||||||
|
error("没有权限操作");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this instanceof ConsoleCommand && !ConsoleCommandSender.class.isAssignableFrom(sender.getClass())) {
|
||||||
|
error("不允许的执行对象");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 触发
|
||||||
|
try {
|
||||||
|
run(args);
|
||||||
|
} catch (ArgsValueException e) {
|
||||||
|
error("参数错误:" + e.getMessage());
|
||||||
|
} catch (Exception e) {
|
||||||
|
error("执行错误:" + e.getMessage());
|
||||||
|
throw new RuntimeException("exec command error", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行指令
|
||||||
|
*
|
||||||
|
* @param args 执行参数
|
||||||
|
*/
|
||||||
|
protected abstract void run(String[] args) throws ArgsValueException;
|
||||||
|
|
||||||
|
protected void appendArgsTabCompleter(String... tips) {
|
||||||
|
appendArgsTabCompleter(Arrays.asList(tips));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void appendArgsTabCompleter(Collection<String> list) {
|
||||||
|
argsTabCompleterMap.put(argsTabCompleterMap.size(), list);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void putArgsTabCompleter(int index, Collection<String> list) {
|
||||||
|
argsTabCompleterMap.put(index, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void success(String msg) {
|
||||||
|
msg(Level.INFO, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void error(String msg) {
|
||||||
|
msg(Level.WARNING, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void msg(Level level, String msg) {
|
||||||
|
if (sender instanceof Player) {
|
||||||
|
Player player = (Player) sender;
|
||||||
|
ChatColor color = level == Level.INFO ? ChatColor.GREEN : ChatColor.RED;
|
||||||
|
player.spawnParticle(Particle.SMOKE_NORMAL, new Location(player.getWorld(), 0, 0, 0), 10);
|
||||||
|
player.sendMessage(color + msg);
|
||||||
|
} else {
|
||||||
|
FMCCore.getInstance().getLogger().log(level, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AbstractCommand getCommand(String command) {
|
||||||
|
return this.childrenCommandMap.get(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final boolean hasChildren() {
|
||||||
|
return !childrenCommandMap.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getTabCompleterList(CommandSender sender) {
|
||||||
|
List<String> result = new ArrayList<>();
|
||||||
|
if (sender instanceof Player && !sender.isOp()) {
|
||||||
|
for (Map.Entry<String, AbstractCommand> item : childrenCommandMap.entrySet()) {
|
||||||
|
if (!OPCommand.class.isAssignableFrom(item.getValue().getClass())) {
|
||||||
|
result.add(item.getKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result.addAll(childrenCommandMap.keySet());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void appendChildren(String name, AbstractCommand command) {
|
||||||
|
childrenCommandMap.put(name, command);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
package cn.forevermc.spigot.core.command;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2024-10-10 14:48
|
||||||
|
*/
|
||||||
|
public abstract class ConsoleCommand extends AbstractCommand {
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
package cn.forevermc.spigot.core.command;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2024-08-29 20:40
|
||||||
|
*/
|
||||||
|
public abstract class ConsolePlayerCommand extends AbstractCommand {
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
package cn.forevermc.spigot.core.command;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ForeverMC 指令
|
||||||
|
*
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2024-08-15 10:21
|
||||||
|
*/
|
||||||
|
public class FMCCommand extends AbstractCommand {
|
||||||
|
|
||||||
|
public FMCCommand() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(String[] args) {
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,83 @@
|
|||||||
|
package cn.forevermc.spigot.core.command;
|
||||||
|
|
||||||
|
import cn.forevermc.spigot.core.FMCCore;
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.CommandExecutor;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指令执行器
|
||||||
|
*
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2024-08-15 19:39
|
||||||
|
*/
|
||||||
|
public class FMCCommandExecutor implements CommandExecutor {
|
||||||
|
|
||||||
|
public FMCCommandExecutor() {
|
||||||
|
FMCCore fmcCore = FMCCore.getInstance();
|
||||||
|
// 指令提示
|
||||||
|
Objects.requireNonNull(fmcCore.getCommand("fmc")).setTabCompleter((sender, command, label, args) -> {
|
||||||
|
AbstractCommand cmd = fmcCore.getFmcCommand();
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
List<String> result = new ArrayList<>();
|
||||||
|
while (!cmd.childrenCommandMap.isEmpty()) {
|
||||||
|
final int j = i;
|
||||||
|
if (i == args.length - 1) {
|
||||||
|
result.addAll(cmd.getTabCompleterList(sender).stream().filter(item -> item.contains(args[j])).collect(Collectors.toList()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (cmd.getCommand(args[i]) == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cmd = cmd.getCommand(args[i]);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
// 无子级指令
|
||||||
|
if (!cmd.argsTabCompleterMap.isEmpty()) {
|
||||||
|
Collection<String> argsTabCompleterList = cmd.argsTabCompleterMap.get(args.length - 1 - i);
|
||||||
|
if (argsTabCompleterList != null) {
|
||||||
|
final String key = args[args.length - 1];
|
||||||
|
result.addAll(argsTabCompleterList.stream().filter(item -> item != null && item.contains(key)).collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||||
|
List<String> action = new ArrayList<>();
|
||||||
|
AbstractCommand cmd = FMCCore.getInstance().getFmcCommand();
|
||||||
|
if (args == null || args.length == 0) {
|
||||||
|
cmd.run0(sender, new String[0]);
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < args.length; i++) {
|
||||||
|
if (cmd.hasChildren() && cmd.getCommand(args[i]) != null) {
|
||||||
|
action.add(args[i]);
|
||||||
|
|
||||||
|
cmd = cmd.getCommand(args[i]);
|
||||||
|
if (i == args.length - 1) {
|
||||||
|
String[] subArgs = new String[0];
|
||||||
|
cmd.actionList = action;
|
||||||
|
cmd.run0(sender, subArgs);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 全部视为参数
|
||||||
|
String[] newArgs = new String[args.length - i];
|
||||||
|
System.arraycopy(args, i, newArgs, 0, newArgs.length);
|
||||||
|
cmd.actionList = action;
|
||||||
|
cmd.run0(sender, newArgs);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
package cn.forevermc.spigot.core.command;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2024-10-10 14:43
|
||||||
|
*/
|
||||||
|
public abstract class OPCommand extends PlayerCommand {
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
package cn.forevermc.spigot.core.command;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2024-08-29 20:39
|
||||||
|
*/
|
||||||
|
public abstract class PlayerCommand extends AbstractCommand {
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
package cn.forevermc.spigot.core.exception;
|
||||||
|
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2024-08-31 00:20
|
||||||
|
*/
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class ArgsValueException extends Exception {
|
||||||
|
|
||||||
|
public ArgsValueException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
package cn.forevermc.spigot.core.serializable;
|
||||||
|
|
||||||
|
import cn.forevermc.spigot.core.FMCCore;
|
||||||
|
import com.google.gson.JsonDeserializationContext;
|
||||||
|
import com.google.gson.JsonDeserializer;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
|
import com.google.gson.JsonSerializationContext;
|
||||||
|
import com.google.gson.JsonSerializer;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.World;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Location 对象序列化和反序列化
|
||||||
|
*
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2024-08-29 10:21
|
||||||
|
*/
|
||||||
|
public class LocationSerializer implements JsonSerializer<Location>, JsonDeserializer<Location> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Location deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||||
|
JsonObject obj = json.getAsJsonObject();
|
||||||
|
|
||||||
|
World world = FMCCore.getInstance().getServer().getWorld(UUID.fromString(obj.get("world").getAsString()));
|
||||||
|
Location location = new Location(world, obj.get("x").getAsDouble(), obj.get("y").getAsDouble(), obj.get("z").getAsDouble());
|
||||||
|
location.setYaw(obj.get("yaw").getAsFloat());
|
||||||
|
location.setPitch(obj.get("pitch").getAsFloat());
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonElement serialize(Location src, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
|
JsonObject obj = new JsonObject();
|
||||||
|
obj.addProperty("world", Objects.requireNonNull(src.getWorld()).getUID().toString());
|
||||||
|
obj.addProperty("x", src.getX());
|
||||||
|
obj.addProperty("y", src.getY());
|
||||||
|
obj.addProperty("z", src.getZ());
|
||||||
|
obj.addProperty("yaw", src.getYaw());
|
||||||
|
obj.addProperty("pitch", src.getPitch());
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
package cn.forevermc.spigot.core.serializable;
|
||||||
|
|
||||||
|
import cn.forevermc.spigot.core.bean.Region;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.JsonDeserializationContext;
|
||||||
|
import com.google.gson.JsonDeserializer;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
|
import com.google.gson.JsonSerializationContext;
|
||||||
|
import com.google.gson.JsonSerializer;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2024-08-29 10:05
|
||||||
|
*/
|
||||||
|
public class RegionSerializer implements JsonSerializer<Region>, JsonDeserializer<Region> {
|
||||||
|
|
||||||
|
private static final Gson GSON = new Gson();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Region deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||||
|
Region region = new Region();
|
||||||
|
|
||||||
|
JsonObject obj = json.getAsJsonObject();
|
||||||
|
region.setLeft(GSON.fromJson(obj.get("p1"), Location.class));
|
||||||
|
region.setRight(GSON.fromJson(obj.get("p2"), Location.class));
|
||||||
|
return region;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonElement serialize(Region src, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
|
JsonObject obj = new JsonObject();
|
||||||
|
obj.add("p1", GSON.toJsonTree(src.getP1()));
|
||||||
|
obj.add("p2", GSON.toJsonTree(src.getP2()));
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
255
src/main/java/cn/forevermc/spigot/core/util/Ref.java
Normal file
255
src/main/java/cn/forevermc/spigot/core/util/Ref.java
Normal file
@ -0,0 +1,255 @@
|
|||||||
|
package cn.forevermc.spigot.core.util;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 反射相关
|
||||||
|
*
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2023-05-04 15:05
|
||||||
|
*/
|
||||||
|
public class Ref {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取类字段列表
|
||||||
|
*
|
||||||
|
* @param clazz 类
|
||||||
|
* @return 字段列表
|
||||||
|
*/
|
||||||
|
public static List<Field> listFields(Class<?> clazz) {
|
||||||
|
return listFields(clazz, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取类字段列表
|
||||||
|
*
|
||||||
|
* @param clazz 类
|
||||||
|
* @param fieldType 字段类过滤
|
||||||
|
* @return 字段列表
|
||||||
|
*/
|
||||||
|
public static List<Field> listFields(Class<?> clazz, Class<?> fieldType) {
|
||||||
|
Field[] fields = clazz.getDeclaredFields();
|
||||||
|
if (fieldType == null) {
|
||||||
|
return Arrays.asList(fields);
|
||||||
|
}
|
||||||
|
return Arrays.stream(fields).filter(f -> fieldType.isAssignableFrom(f.getType())).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param keyName
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String getFieldName(String keyName) {
|
||||||
|
String[] splits = {"-", "_", " "};
|
||||||
|
StringBuilder full = new StringBuilder(keyName.substring(0, 1).toUpperCase() + keyName.substring(1));
|
||||||
|
for (int i = 0; i < splits.length; i++) {
|
||||||
|
if (keyName.contains(splits[i])) {
|
||||||
|
// 存在分隔符
|
||||||
|
full.setLength(0);
|
||||||
|
String[] word = keyName.split(splits[i]);
|
||||||
|
for (int j = 0; j < word.length; j++) {
|
||||||
|
full.append(word[j].substring(0, 1).toUpperCase()).append(word[j].substring(1));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return String.valueOf(full.charAt(0)).toLowerCase() + full.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 反射获取对象字段,包括父级类,直至 {@link Object},如果都不存在则返回 null
|
||||||
|
*
|
||||||
|
* @param clazz 类
|
||||||
|
* @param fieldName 字段名
|
||||||
|
* @return 字段
|
||||||
|
*/
|
||||||
|
public static Field getField(Class<?> clazz, String fieldName) {
|
||||||
|
do {
|
||||||
|
try {
|
||||||
|
Field field = clazz.getDeclaredField(fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field;
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
clazz = clazz.getSuperclass();
|
||||||
|
}
|
||||||
|
} while (clazz != Object.class);
|
||||||
|
throw new NullPointerException(String.format("not found field: %s in %s", fieldName, clazz));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 反射获取对象字段值,包括父级类,直至 {@link Object}
|
||||||
|
*
|
||||||
|
* @param object 对象
|
||||||
|
* @param fieldName 字段名
|
||||||
|
* @param toClass 返回类
|
||||||
|
* @param <T> 返回类型
|
||||||
|
* @return 字段值
|
||||||
|
* @throws IllegalAccessException 反射访问失败
|
||||||
|
* @throws NullPointerException 向上反射直至 {@link Object} 也找不到该字段
|
||||||
|
*/
|
||||||
|
public static <T> T getFieldValue(Object object, String fieldName, Class<? extends T> toClass) throws IllegalAccessException, NullPointerException {
|
||||||
|
return getFieldValue(object, getField(object.getClass(), fieldName), toClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 反射获取对象字段值
|
||||||
|
*
|
||||||
|
* @param object 对象
|
||||||
|
* @param field 字段
|
||||||
|
* @param toClass 返回类
|
||||||
|
* @param <T> 返回类型
|
||||||
|
* @return 字段值
|
||||||
|
* @throws IllegalAccessException 反射访问失败
|
||||||
|
* @throws NullPointerException 向上反射直至 {@link Object} 也找不到该字段
|
||||||
|
*/
|
||||||
|
public static <T> T getFieldValue(Object object, Field field, Class<? extends T> toClass) throws IllegalAccessException, NullPointerException {
|
||||||
|
if (field == null) {
|
||||||
|
throw new NullPointerException("field can not be null");
|
||||||
|
}
|
||||||
|
field.setAccessible(true);
|
||||||
|
return toClass.cast(field.get(object));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 反射获取类字段
|
||||||
|
*
|
||||||
|
* @param objectClass 类
|
||||||
|
* @param fieldName 字段名
|
||||||
|
* @return 字段
|
||||||
|
*/
|
||||||
|
public static Field getClassField(Class<?> objectClass, String fieldName) {
|
||||||
|
try {
|
||||||
|
Field field = objectClass.getDeclaredField(fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field;
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 反射获取指定类字段值
|
||||||
|
*
|
||||||
|
* @param object 对象
|
||||||
|
* @param objectClass 类
|
||||||
|
* @param fieldName 字段名
|
||||||
|
* @param toClass 值类型
|
||||||
|
* @param <T> 值类型
|
||||||
|
* @return 字段值
|
||||||
|
* @throws IllegalAccessException 反射访问失败
|
||||||
|
* @throws NoSuchFieldException 字段不存在
|
||||||
|
*/
|
||||||
|
public static <T> T getClassFieldValue(Object object, Class<?> objectClass, String fieldName, Class<T> toClass) throws IllegalAccessException, NoSuchFieldException {
|
||||||
|
Field field = getClassField(objectClass, fieldName);
|
||||||
|
if (field == null) {
|
||||||
|
throw new NoSuchFieldException("not found " + fieldName + " field in " + objectClass.getSimpleName());
|
||||||
|
}
|
||||||
|
field.setAccessible(true);
|
||||||
|
return toClass.cast(field.get(object));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 反射设置对象字段值,包括父级类,直至 {@link Object}
|
||||||
|
*
|
||||||
|
* @param object 对象
|
||||||
|
* @param fieldName 字段名
|
||||||
|
* @param value 字段值
|
||||||
|
* @throws IllegalAccessException 反射访问失败
|
||||||
|
* @throws NoSuchFieldException 向上反射直至 {@link Object} 也找不到该字段
|
||||||
|
*/
|
||||||
|
public static void setFieldValue(Object object, String fieldName, Object value) throws IllegalAccessException, NoSuchFieldException {
|
||||||
|
Field field = getField(object.getClass(), fieldName);
|
||||||
|
if (field == null) {
|
||||||
|
throw new NoSuchFieldException("not found " + fieldName + " field in " + object.getClass().getSimpleName());
|
||||||
|
}
|
||||||
|
setFieldValue(object, field, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setFieldValue(Object object, Field field, Object value) throws IllegalAccessException {
|
||||||
|
field.setAccessible(true);
|
||||||
|
field.set(object, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 反射设置对象字段值
|
||||||
|
*
|
||||||
|
* @param object 对象
|
||||||
|
* @param objectClass 类
|
||||||
|
* @param fieldName 字段名
|
||||||
|
* @param value 字段值
|
||||||
|
* @throws IllegalAccessException 反射访问失败
|
||||||
|
* @throws NullPointerException 字段不存在
|
||||||
|
*/
|
||||||
|
public static void setClassFieldValue(Object object, Class<?> objectClass, String fieldName, Object value) throws IllegalAccessException, NullPointerException {
|
||||||
|
Field field = getClassField(objectClass, fieldName);
|
||||||
|
if (field == null) {
|
||||||
|
throw new NullPointerException("not found " + fieldName + " field in " + objectClass.getSimpleName() + " class");
|
||||||
|
}
|
||||||
|
field.setAccessible(true);
|
||||||
|
field.set(object, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 反射查找方法,包括父级类,直至 {@link Object},如果都不存在则返回 null
|
||||||
|
*
|
||||||
|
* @param clazz 类
|
||||||
|
* @param methodName 方法名
|
||||||
|
* @param parameterTypes 可选参
|
||||||
|
* @return 方法对象
|
||||||
|
*/
|
||||||
|
public static Method getMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) {
|
||||||
|
if (clazz == null) {
|
||||||
|
throw new NullPointerException("class can not be null");
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
try {
|
||||||
|
Method method = clazz.getDeclaredMethod(methodName, parameterTypes);
|
||||||
|
method.setAccessible(true);
|
||||||
|
return method;
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
clazz = clazz.getSuperclass();
|
||||||
|
}
|
||||||
|
} while (clazz != Object.class);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字符串转枚举
|
||||||
|
*
|
||||||
|
* @param clazz 枚举类
|
||||||
|
* @param string 字符串
|
||||||
|
* @param <T> 泛型
|
||||||
|
* @return 泛型
|
||||||
|
*/
|
||||||
|
public static <T extends Enum<?>> T toType(Class<T> clazz, String string) {
|
||||||
|
if (string == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
T[] ts = clazz.getEnumConstants();
|
||||||
|
if (ts == null) {
|
||||||
|
throw new IllegalArgumentException(clazz.getName() + " is not an enum type");
|
||||||
|
}
|
||||||
|
for (int i = 0; i < ts.length; i++) {
|
||||||
|
if (ts[i].name().equalsIgnoreCase(string)) {
|
||||||
|
return ts[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param type
|
||||||
|
* @return
|
||||||
|
* @param <T>
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public static <T> T newInstance(Class<T> type) throws Exception {
|
||||||
|
return type.getDeclaredConstructor().newInstance();
|
||||||
|
}
|
||||||
|
}
|
||||||
163
src/main/java/cn/forevermc/spigot/core/util/Util.java
Normal file
163
src/main/java/cn/forevermc/spigot/core/util/Util.java
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
package cn.forevermc.spigot.core.util;
|
||||||
|
|
||||||
|
import cn.forevermc.spigot.core.exception.ArgsValueException;
|
||||||
|
import net.objecthunter.exp4j.ExpressionBuilder;
|
||||||
|
import net.objecthunter.exp4j.function.Function;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.entity.Entity;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.util.Vector;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.math.RoundingMode;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工具
|
||||||
|
*
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2024-08-19 15:05
|
||||||
|
*/
|
||||||
|
public class Util {
|
||||||
|
|
||||||
|
private static final Function EXPRESSION_ROUND = new Function("round") {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double apply(double... args) {
|
||||||
|
return BigDecimal.valueOf(args[0]).setScale(4, RoundingMode.HALF_UP).doubleValue();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static Entity getEntityByUID(UUID uuid) {
|
||||||
|
List<World> worldList = Bukkit.getWorlds();
|
||||||
|
for (int i = 0; i < worldList.size(); i++) {
|
||||||
|
List<Entity> entityList = worldList.get(i).getEntities();
|
||||||
|
for (int j = 0; j < entityList.size(); j++) {
|
||||||
|
if (entityList.get(j).getUniqueId().equals(uuid)) {
|
||||||
|
return entityList.get(j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 看向某个位置
|
||||||
|
*
|
||||||
|
* @param entity
|
||||||
|
* @param lookAt
|
||||||
|
*/
|
||||||
|
public static void lookAt(Entity entity, Location lookAt) {
|
||||||
|
Location loc = entity.getLocation();
|
||||||
|
loc.setDirection(lookAt.toVector().subtract(loc.toVector()).normalize());
|
||||||
|
entity.teleport(loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 合并参数
|
||||||
|
*
|
||||||
|
* @param args
|
||||||
|
* @param from
|
||||||
|
* @param to
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String joinArgs(String[] args, int from, int to) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (int i = from; i < to; i++) {
|
||||||
|
sb.append(args[i]).append(' ');
|
||||||
|
}
|
||||||
|
sb.deleteCharAt(sb.length() - 1);
|
||||||
|
if (sb.charAt(0) == '"') {
|
||||||
|
sb.deleteCharAt(0);
|
||||||
|
}
|
||||||
|
if (sb.charAt(sb.length() - 1) == '"') {
|
||||||
|
sb.deleteCharAt(sb.length() - 1);
|
||||||
|
}
|
||||||
|
if (sb.charAt(0) == '/') {
|
||||||
|
sb.deleteCharAt(0);
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算表达式
|
||||||
|
*
|
||||||
|
* @param expression
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static double argExpression(String expression) {
|
||||||
|
return new ExpressionBuilder(String.format("round(%s)", expression)).function(EXPRESSION_ROUND).build().evaluate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 颜色消息
|
||||||
|
*
|
||||||
|
* @param msg
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String colorMsg(String msg) {
|
||||||
|
return ChatColor.translateAlternateColorCodes('&', msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取必要参数值
|
||||||
|
*
|
||||||
|
* @param args
|
||||||
|
* @param index
|
||||||
|
* @param name
|
||||||
|
* @return
|
||||||
|
* @throws ArgsValueException
|
||||||
|
*/
|
||||||
|
public static String argsValueRequired(String[] args, int index, String name) throws ArgsValueException {
|
||||||
|
if (args.length - 1 < index) {
|
||||||
|
throw new ArgsValueException(String.format("缺少 %s", name));
|
||||||
|
}
|
||||||
|
String value = args[index];
|
||||||
|
if (value.isEmpty()) {
|
||||||
|
throw new ArgsValueException(String.format("缺少 %s", name));
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取参数值
|
||||||
|
*
|
||||||
|
* @param args
|
||||||
|
* @param index
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String argsValue(String[] args, int index) {
|
||||||
|
if (args.length - 1 < index) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String value = args[index];
|
||||||
|
if (value.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取玩家看向的实体
|
||||||
|
*
|
||||||
|
* @param player 玩家
|
||||||
|
* @param range 直线范围
|
||||||
|
* @return 实体
|
||||||
|
*/
|
||||||
|
public static Entity getTargetEntity(Player player, double range) {
|
||||||
|
Vector direction = player.getEyeLocation().getDirection();
|
||||||
|
Vector start = player.getEyeLocation().toVector();
|
||||||
|
Vector end = start.add(direction.multiply(range));
|
||||||
|
|
||||||
|
for (Entity entity : player.getWorld().getNearbyEntities(player.getLocation(), range, range, range)) {
|
||||||
|
if (entity.getLocation().toVector().isInAABB(start, end)) {
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
9
src/main/resources/plugin.yml
Normal file
9
src/main/resources/plugin.yml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
name: FMCCore
|
||||||
|
version: '0.0.1'
|
||||||
|
main: cn.forevermc.spigot.core.FMCCore
|
||||||
|
api-version: '1.9'
|
||||||
|
|
||||||
|
commands:
|
||||||
|
fmc:
|
||||||
|
description: FMC Command
|
||||||
|
usage: "/<command>"
|
||||||
Reference in New Issue
Block a user