diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..ac19365 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/dictionaries/sky.xml b/.idea/dictionaries/sky.xml new file mode 100644 index 0000000..4b38855 --- /dev/null +++ b/.idea/dictionaries/sky.xml @@ -0,0 +1,8 @@ + + + + bukkit + zaphkiel + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..15a15b2 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..ff37055 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..101da1e --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,113 @@ + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__io_izzel_taboolib_TabooLib_all_5_13.xml b/.idea/libraries/Gradle__io_izzel_taboolib_TabooLib_all_5_13.xml new file mode 100644 index 0000000..e322630 --- /dev/null +++ b/.idea/libraries/Gradle__io_izzel_taboolib_TabooLib_all_5_13.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__io_izzel_taboolib_loader_TabooLibloader_all_1_2.xml b/.idea/libraries/Gradle__io_izzel_taboolib_loader_TabooLibloader_all_1_2.xml new file mode 100644 index 0000000..43e79d0 --- /dev/null +++ b/.idea/libraries/Gradle__io_izzel_taboolib_loader_TabooLibloader_all_1_2.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__org_jetbrains_annotations_13_0.xml b/.idea/libraries/Gradle__org_jetbrains_annotations_13_0.xml new file mode 100644 index 0000000..fc0d425 --- /dev/null +++ b/.idea/libraries/Gradle__org_jetbrains_annotations_13_0.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_1_2_41.xml b/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_1_2_41.xml new file mode 100644 index 0000000..86e2765 --- /dev/null +++ b/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_1_2_41.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_jdk7_1_2_41.xml b/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_jdk7_1_2_41.xml new file mode 100644 index 0000000..f3720c0 --- /dev/null +++ b/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_jdk7_1_2_41.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_jdk8_1_2_41.xml b/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_jdk8_1_2_41.xml new file mode 100644 index 0000000..ce6c83f --- /dev/null +++ b/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_jdk8_1_2_41.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Gradle__org_spigotmc_spigot_1_14_4_R0_1_SNAPSHOT.xml b/.idea/libraries/Gradle__org_spigotmc_spigot_1_14_4_R0_1_SNAPSHOT.xml new file mode 100644 index 0000000..5f26a17 --- /dev/null +++ b/.idea/libraries/Gradle__org_spigotmc_spigot_1_14_4_R0_1_SNAPSHOT.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..6a0ce10 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + Class structureJava + + + Code maturityJava + + + Google Web Toolkit + + + JUnitJava + + + Java + + + Java 5Java language level migration aidsJava + + + Java 7Java language level migration aidsJava + + + Java 8Java language level migration aidsJava + + + Java language level migration aidsJava + + + JavadocJava + + + Numeric issuesJava + + + PerformanceJava + + + PortabilityJava + + + Probable bugsJava + + + Resource managementJava + + + Spring + + + Spring AOPSpring + + + TestNGJava + + + Threading issuesJava + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..5c65c79 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules/me.skymc.taboolib.example.Zaphkiel.iml b/.idea/modules/me.skymc.taboolib.example.Zaphkiel.iml new file mode 100644 index 0000000..88d230e --- /dev/null +++ b/.idea/modules/me.skymc.taboolib.example.Zaphkiel.iml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules/me.skymc.taboolib.example.Zaphkiel.main.iml b/.idea/modules/me.skymc.taboolib.example.Zaphkiel.main.iml new file mode 100644 index 0000000..df3d76c --- /dev/null +++ b/.idea/modules/me.skymc.taboolib.example.Zaphkiel.main.iml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + SPIGOT + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules/me.skymc.taboolib.example.Zaphkiel.test.iml b/.idea/modules/me.skymc.taboolib.example.Zaphkiel.test.iml new file mode 100644 index 0000000..68c794b --- /dev/null +++ b/.idea/modules/me.skymc.taboolib.example.Zaphkiel.test.iml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + SPIGOT + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml new file mode 100644 index 0000000..e96534f --- /dev/null +++ b/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..b0aaff6 --- /dev/null +++ b/build.gradle @@ -0,0 +1,62 @@ +plugins { + id 'java' + id 'com.github.johnrengelman.shadow' version '4.0.4' + id 'org.jetbrains.kotlin.jvm' version '1.2.41' +} + +configurations { + group = 'me.skymc.taboolib.example' + version = '1.0-SNAPSHOT' + + sourceCompatibility = 1.8 + targetCompatibility = 1.8 + + tasks.build.dependsOn tasks.shadowJar + + tasks.withType(JavaCompile) { + options.encoding = 'UTF-8' + } + + defaultTasks 'clean', 'build' +} + +repositories { + maven { url "http://ptms.ink:8081/repository/codemc-nms/" } + maven { url "http://ptms.ink:8081/repository/maven-releases/" } + mavenCentral() +} + +dependencies { + compile 'org.spigotmc:spigot:1.14.4-R0.1-SNAPSHOT' + compile 'io.izzel.taboolib:TabooLib:5.13:all' + compile 'io.izzel.taboolib.loader:TabooLibloader:1.2:all' + compile "org.jetbrains.kotlin:kotlin-stdlib:1.2.41" + compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.2.41" + compileOnly fileTree(dir: 'libs', includes: ['*.jar']) +} + +shadowJar { + dependencies { + include dependency('io.izzel.taboolib.loader:.*') + } + relocate 'io.izzel.taboolib.loader', project.group +} + +processResources { + from(sourceSets.main.resources.srcDirs) { + include 'plugin.yml' + expand 'version': project.version + } +} + +compileKotlin { + kotlinOptions { + jvmTarget = "1.8" + } +} + +compileTestKotlin { + kotlinOptions { + jvmTarget = "1.8" + } +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..94336fc 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 new file mode 100644 index 0000000..290541c --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..cccdd3d --- /dev/null +++ b/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# 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 + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# 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" + which java >/dev/null 2>&1 || 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 + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..f955316 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ +@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=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@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= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="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! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..08b9725 --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'Zaphkiel' diff --git a/src/main/java/ink/ptms/zaphkiel/Zaphkiel.java b/src/main/java/ink/ptms/zaphkiel/Zaphkiel.java new file mode 100644 index 0000000..af0513d --- /dev/null +++ b/src/main/java/ink/ptms/zaphkiel/Zaphkiel.java @@ -0,0 +1,37 @@ +package ink.ptms.zaphkiel; + +import io.izzel.taboolib.loader.Plugin; +import io.izzel.taboolib.module.config.TConfig; +import io.izzel.taboolib.module.inject.TInject; +import io.izzel.taboolib.module.locale.logger.TLogger; + +import java.io.File; + +/** + * @Author sky + * @Since 2019-12-15 20:09 + */ +@Plugin.Version(5.13) +public class Zaphkiel extends Plugin { + + @TInject + public static final TLogger LOGS = null; + @TInject + public static final TConfig CONF = null; + + @Override + public void onStarting() { + if (!new File(getDataFolder(), "item").exists()) { + saveResource("item/def.yml", true); + } + if (!new File(getDataFolder(), "display").exists()) { + saveResource("display/def.yml", true); + } + } + + @Override + public void onActivated() { + ZaphkielAPI.INSTANCE.reloadItem(); + ZaphkielAPI.INSTANCE.reloadDisplay(); + } +} diff --git a/src/main/java/ink/ptms/zaphkiel/ZaphkielAPI.kt b/src/main/java/ink/ptms/zaphkiel/ZaphkielAPI.kt new file mode 100644 index 0000000..91ecc53 --- /dev/null +++ b/src/main/java/ink/ptms/zaphkiel/ZaphkielAPI.kt @@ -0,0 +1,95 @@ +package ink.ptms.zaphkiel + +import com.google.common.collect.Maps +import ink.ptms.zaphkiel.api.Item +import ink.ptms.zaphkiel.api.Display +import ink.ptms.zaphkiel.api.ItemStream +import ink.ptms.zaphkiel.api.event.ItemRebuildEvent +import io.izzel.taboolib.util.Files +import io.izzel.taboolib.util.item.Items +import org.bukkit.entity.Player +import org.bukkit.inventory.Inventory +import org.bukkit.inventory.ItemStack +import java.io.File +import java.lang.RuntimeException + +/** + * @Author sky + * @Since 2019-12-15 20:14 + */ +object ZaphkielAPI { + + val folderItem = File(Zaphkiel.getPlugin().dataFolder, "item") + val folderDisplay = File(Zaphkiel.getPlugin().dataFolder, "display") + val registeredItem = Maps.newHashMap()!! + val registeredDisplay = Maps.newHashMap()!! + + fun read(item: ItemStack): ItemStream { + if (Items.isNull(item)) { + throw RuntimeException("Could not read empty item."); + } + return ItemStream(item) + } + + fun rebuild(player: Player?, inventory: Inventory) { + for (i in 0..40) { + val item = inventory.getItem(i) + if (Items.isNull(item)) { + continue + } + val rebuild = rebuild(player, item!!) + if (rebuild.isFromRebuild) { + rebuild.save() + } + } + } + + fun rebuild(player: Player?, item: ItemStack): ItemStream { + if (Items.isNull(item)) { + throw RuntimeException("Could not read empty item."); + } + val itemStream = ItemStream(item, isFromRebuild = true) + if (itemStream.isVanilla()) { + return itemStream + } + val pre = ItemRebuildEvent(player, itemStream, itemStream.shouldRefresh()).call() + if (pre.isCancelled) { + return itemStream + } + return itemStream.getZaphkielItem().build(player, itemStream) + } + + fun reloadItem() { + registeredItem.clear() + reloadItem(folderItem) + Zaphkiel.LOGS.info("Loaded ${registeredItem.size} items.") + } + + fun reloadItem(file: File) { + if (file.isDirectory) { + file.listFiles().forEach { reloadItem(it) } + } else { + val conf = Files.load(file) + conf.getKeys(false).forEach { key -> + registeredItem[key] = Item(conf.getConfigurationSection(key)!!) + } + } + } + + fun reloadDisplay() { + registeredDisplay.clear() + reloadDisplay(folderDisplay) + Zaphkiel.LOGS.info("Loaded ${registeredDisplay.size} display plan.") + } + + fun reloadDisplay(file: File) { + if (file.isDirectory) { + file.listFiles().forEach { reloadDisplay(it) } + } else { + val conf = Files.load(file) + conf.getKeys(false).forEach { key -> + registeredDisplay[key] = Display(conf.getConfigurationSection(key)!!) + } + } + } +} \ No newline at end of file diff --git a/src/main/java/ink/ptms/zaphkiel/ZaphkielCommand.kt b/src/main/java/ink/ptms/zaphkiel/ZaphkielCommand.kt new file mode 100644 index 0000000..a3a1966 --- /dev/null +++ b/src/main/java/ink/ptms/zaphkiel/ZaphkielCommand.kt @@ -0,0 +1,89 @@ +package ink.ptms.zaphkiel + +import ink.ptms.zaphkiel.api.internal.ItemList +import io.izzel.taboolib.cronus.CronusUtils +import io.izzel.taboolib.module.command.base.* +import org.bukkit.Bukkit +import org.bukkit.Sound +import org.bukkit.command.Command +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player +import org.bukkit.util.NumberConversions + +/** + * @Author sky + * @Since 2019-12-15 22:39 + */ +@BaseCommand(name = "Zaphkiel", aliases = ["zl", "item"], permission = "*") +class ZaphkielCommand : BaseMainCommand() { + + @SubCommand(priority = 0.0) + val give = object : BaseSubCommand() { + + override fun getArguments(): Array = arrayOf(Argument("节点") { ZaphkielAPI.registeredItem.keys.toList() }, Argument("玩家", false), Argument("数量", false)) + + override fun getDescription(): String = "赋予物品" + + override fun onCommand(sender: CommandSender, command: Command?, label: String, args: Array) { + val item = ZaphkielAPI.registeredItem[args[0]] + if (item == null) { + notify(sender, "物品 \"&f${args[0]}&7\" 不存在.") + return + } + val target: Player? = when { + args.size > 1 -> Bukkit.getPlayerExact(args[1]) + sender is Player -> sender + else -> null + } + if (target == null) { + notify(sender, "缺少玩家.") + return + } + val itemStack = item.build(target).save() + if (args.size > 2) { + itemStack.amount = NumberConversions.toInt(args[2]) + } + CronusUtils.addItem(target, itemStack) + } + } + + @SubCommand(priority = 0.1) + val list = object : BaseSubCommand() { + + override fun getType(): CommandType = CommandType.PLAYER + + override fun getArguments(): Array = arrayOf(Argument("页数", false)) + + override fun getDescription(): String = "列出物品" + + override fun onCommand(sender: CommandSender, command: Command?, label: String, args: Array) { + val player = sender as Player + if (args.isEmpty()) { + ItemList.open(player, 0) + } else { + ItemList.open(player, NumberConversions.toInt(args[0]) - 1) + } + } + } + + @SubCommand(priority = 0.2) + val reload = object : BaseSubCommand() { + + override fun getDescription(): String = "重载物品" + + override fun onCommand(sender: CommandSender, command: Command?, label: String, args: Array) { + ZaphkielAPI.reloadItem() + ZaphkielAPI.reloadDisplay() + notify(sender, "物品已重载.") + } + } + + fun notify(sender: CommandSender, value: String) { + sender.sendMessage("§c[Zaphkiel] §7${value.replace("&", "§")}") + } + + fun notify(player: Player, value: String) { + player.sendMessage("§c[Zaphkiel] §7${value.replace("&", "§")}") + player.playSound(player.location, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1f, 2f) + } +} \ No newline at end of file diff --git a/src/main/java/ink/ptms/zaphkiel/api/Display.kt b/src/main/java/ink/ptms/zaphkiel/api/Display.kt new file mode 100644 index 0000000..f8fc337 --- /dev/null +++ b/src/main/java/ink/ptms/zaphkiel/api/Display.kt @@ -0,0 +1,28 @@ +package ink.ptms.zaphkiel.api + +import ink.ptms.zaphkiel.api.internal.StructureList +import ink.ptms.zaphkiel.api.internal.StructureSingle +import org.bukkit.configuration.ConfigurationSection + +/** + * @Author sky + * @Since 2019-12-15 14:49 + */ +data class Display( + val config: ConfigurationSection, + val id: String = config.name, + val name: String? = config.getString("name"), + val lore: List = config.getStringList("lore"), + val structureName: StructureSingle? = if (name != null) StructureSingle(name) else null, + val structureLore: StructureList = StructureList(lore)) { + + fun toProduct(name: Map, lore: Map>): Product { + return Product(structureName?.build(name), structureLore.build(lore.mapValues { it.value.toMutableList() })) + } + + fun toProductTrim(name: Map, lore: Map>): Product { + return Product(structureName?.buildTrim(name), structureLore.buildTrim(lore.mapValues { it.value.toMutableList() })) + } + + data class Product(val name: String?, val lore: List) +} \ No newline at end of file diff --git a/src/main/java/ink/ptms/zaphkiel/api/Item.kt b/src/main/java/ink/ptms/zaphkiel/api/Item.kt new file mode 100644 index 0000000..3cb13ac --- /dev/null +++ b/src/main/java/ink/ptms/zaphkiel/api/Item.kt @@ -0,0 +1,119 @@ +package ink.ptms.zaphkiel.api + +import ink.ptms.zaphkiel.ZaphkielAPI +import ink.ptms.zaphkiel.api.event.ItemBuildEvent +import ink.ptms.zaphkiel.api.internal.ItemKey +import ink.ptms.zaphkiel.api.internal.ScriptAPI +import io.izzel.taboolib.module.locale.TLocale +import io.izzel.taboolib.module.nms.nbt.NBTBase +import io.izzel.taboolib.module.nms.nbt.NBTCompound +import io.izzel.taboolib.util.Strings +import io.izzel.taboolib.util.item.Items +import io.izzel.taboolib.util.lite.Scripts +import org.bukkit.configuration.ConfigurationSection +import org.bukkit.configuration.file.YamlConfiguration +import org.bukkit.entity.Player +import org.bukkit.event.Event +import org.bukkit.inventory.ItemStack +import org.bukkit.util.NumberConversions +import java.util.* +import javax.script.CompiledScript +import javax.script.SimpleBindings + +/** + * @Author sky + * @Since 2019-12-15 16:09 + */ +data class Item( + val config: ConfigurationSection, + val id: String = config.name, + val display: String = config.getString("display") ?: "null", + val icon: ItemStack = parseIcon(config.getString("icon", "STONE")!!), + val name: Map = parseName(config), + val lore: Map> = parseLore(config), + val data: ConfigurationSection = config.getConfigurationSection("data") ?: config.createSection("data"), + val event: Map = parseEvent(config)) { + + val hash = YamlConfiguration().run { + this.set("value", config) + Strings.hashKeyForDisk(this.saveToString()) + }!! + + fun eval(key: String, bukkitEvent: Event) { + event[key]?.eval(bukkitEvent) + } + + fun build(player: Player?): ItemStream { + val itemStream = ItemStream(icon.clone()) + val compound = itemStream.compound.computeIfAbsent("zaphkiel") { NBTCompound() }.asCompound() + compound[ItemKey.ID.key] = NBTBase(id) + compound[ItemKey.DATA.key] = NBTBase.translateSection(NBTCompound(), data) + return build(player, itemStream) + } + + fun build(player: Player?, itemStream: ItemStream): ItemStream { + val pre = ItemBuildEvent.Pre(player, itemStream, name.toMutableMap(), lore.toMutableMap()).call() + val display = ZaphkielAPI.registeredDisplay[display] + if (display != null) { + val product = display.toProductTrim(pre.name, pre.lore) + pre.itemStream.setDisplayName(TLocale.Translate.setColored(product.name)) + pre.itemStream.setLore(TLocale.Translate.setColored(product.lore)) + } else { + pre.itemStream.setDisplayName("§c$id") + pre.itemStream.setLore(listOf("", "§4- NO DISPLAY PLAN -")) + } + val compound = pre.itemStream.compound.computeIfAbsent("zaphkiel") { NBTCompound() }.asCompound() + compound[ItemKey.HASH.key] = NBTBase(hash) + return ItemBuildEvent.Post(player, pre.itemStream, pre.name, pre.lore).call().itemStream + } + + data class ItemEvent(val name: String, val script: CompiledScript) { + + fun eval(bukkitEvent: Event) { + try { + script.eval(SimpleBindings(mapOf(Pair("event", bukkitEvent), Pair("api", ScriptAPI)))) + } catch (t: Throwable) { + t.printStackTrace() + } + } + } + + private companion object { + + fun parseIcon(icon: String): ItemStack { + val args = icon.split("~") + return ItemStack(Items.asMaterial(args[0]), 1, NumberConversions.toShort(args.getOrElse(1) { "0" })) + } + + fun parseName(config: ConfigurationSection): Map { + val map = HashMap() + val name = config.getConfigurationSection("name") ?: return emptyMap() + name.getKeys(false).forEach { key -> + map[key] = config.getString("name.$key")!! + } + return map + } + + fun parseLore(config: ConfigurationSection): Map> { + val map = HashMap>() + val lore = config.getConfigurationSection("lore") ?: return emptyMap() + lore.getKeys(false).forEach { key -> + if (config.isList("lore.$key")) { + map[key] = config.getStringList("lore.$key") + } else { + map[key] = listOf(config.getString("lore.$key")!!) + } + } + return map + } + + fun parseEvent(config: ConfigurationSection): Map { + val map = HashMap() + val event = config.getConfigurationSection("event") ?: return emptyMap() + event.getKeys(false).forEach { key -> + map[key] = ItemEvent(key, Scripts.compile(config.getString("event.$key")!!)) + } + return map + } + } +} \ No newline at end of file diff --git a/src/main/java/ink/ptms/zaphkiel/api/ItemStream.kt b/src/main/java/ink/ptms/zaphkiel/api/ItemStream.kt new file mode 100644 index 0000000..5953c1f --- /dev/null +++ b/src/main/java/ink/ptms/zaphkiel/api/ItemStream.kt @@ -0,0 +1,91 @@ +package ink.ptms.zaphkiel.api + +import ink.ptms.zaphkiel.ZaphkielAPI +import ink.ptms.zaphkiel.api.internal.ItemKey +import io.izzel.taboolib.module.nms.NMS +import io.izzel.taboolib.module.nms.nbt.NBTBase +import io.izzel.taboolib.module.nms.nbt.NBTCompound +import io.izzel.taboolib.module.nms.nbt.NBTList +import org.bukkit.inventory.ItemStack +import java.lang.RuntimeException + +/** + * @Author sky + * @Since 2019-12-15 16:58 + */ +data class ItemStream( + val itemStack: ItemStack, + val compound: NBTCompound = NMS.handle().loadNBT(itemStack), + val isFromRebuild: Boolean = false) { + + fun isExtension(): Boolean { + val compound = zaphkielCompound() ?: return false + if (compound.containsKey(ItemKey.ID.key)) { + return ZaphkielAPI.registeredItem.containsKey(compound[ItemKey.ID.key]!!.asString()) + } + return false + } + + fun isVanilla(): Boolean { + return !isExtension() + } + + fun setDisplayName(displayName: String) { + val display = compound.computeIfAbsent("display") { NBTCompound() } as NBTCompound + display["Name"] = NBTBase(displayName) + } + + fun setLore(lore: List) { + val display = compound.computeIfAbsent("display") { NBTCompound() } as NBTCompound + display["Lore"] = lore.map { NBTBase(it) }.toCollection(NBTList()) + } + + fun save(): ItemStack { + itemStack.itemMeta = NMS.handle().saveNBT(itemStack, compound).itemMeta!! + return itemStack + } + + fun save(itemStack: ItemStack): ItemStack { + itemStack.itemMeta = NMS.handle().saveNBT(itemStack, compound).itemMeta!! + return itemStack; + } + + fun getZaphkielItem(): Item { + if (isVanilla()) { + throw RuntimeException("This item is not extension item.") + } + return ZaphkielAPI.registeredItem[getZaphkielName()]!! + } + + fun getZaphkielName(): String { + if (isVanilla()) { + throw RuntimeException("This item is not extension item.") + } + return zaphkielCompound()!![ItemKey.ID.key]!!.asString() + } + + fun getZaphkielHash(): String { + if (isVanilla()) { + throw RuntimeException("This item is not extension item.") + } + return zaphkielCompound()!![ItemKey.HASH.key]!!.asString() + } + + fun getZaphkielData(): NBTCompound { + if (isVanilla()) { + throw RuntimeException("This item is not extension item.") + } + return zaphkielCompound()!![ItemKey.DATA.key]!!.asCompound() + } + + fun shouldRefresh(): Boolean { + if (isVanilla()) { + throw RuntimeException("This item is not extension item.") + } + return getZaphkielHash() != getZaphkielItem().hash + } + + private fun zaphkielCompound(): NBTCompound? { + return compound["zaphkiel"]?.asCompound() + } +} \ No newline at end of file diff --git a/src/main/java/ink/ptms/zaphkiel/api/event/ItemBuildEvent.kt b/src/main/java/ink/ptms/zaphkiel/api/event/ItemBuildEvent.kt new file mode 100644 index 0000000..118105d --- /dev/null +++ b/src/main/java/ink/ptms/zaphkiel/api/event/ItemBuildEvent.kt @@ -0,0 +1,52 @@ +package ink.ptms.zaphkiel.api.event + +import io.izzel.taboolib.module.event.EventNormal +import ink.ptms.zaphkiel.api.Item +import ink.ptms.zaphkiel.api.ItemStream +import io.izzel.taboolib.module.nms.nbt.NBTCompound +import org.bukkit.Bukkit +import org.bukkit.entity.Player + +/** + * @Author sky + * @Since 2019-12-15 16:44 + */ +class ItemBuildEvent { + + class Pre( + val player: Player?, + val itemStream: ItemStream, + val name: MutableMap, + val lore: MutableMap> + ) : EventNormal
() {
+
+        init {
+            async(!Bukkit.isPrimaryThread())
+        }
+
+        fun addName(key: String, value: Any) {
+            name[key] = value.toString()
+        }
+
+        fun addLore(key: String, value: Any) {
+            val list = lore.computeIfAbsent(key) { arrayListOf() } as ArrayList
+            list.add(value.toString())
+        }
+
+        fun addLore(key: String, value: List) {
+            value.forEach { addLore(key, it) }
+        }
+    }
+
+    class Post(
+            val player: Player?,
+            val itemStream: ItemStream,
+            val name: Map,
+            val lore: Map>
+    ) : EventNormal() {
+
+        init {
+            async(!Bukkit.isPrimaryThread())
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/ink/ptms/zaphkiel/api/event/ItemRebuildEvent.kt b/src/main/java/ink/ptms/zaphkiel/api/event/ItemRebuildEvent.kt
new file mode 100644
index 0000000..92d4dfa
--- /dev/null
+++ b/src/main/java/ink/ptms/zaphkiel/api/event/ItemRebuildEvent.kt
@@ -0,0 +1,19 @@
+package ink.ptms.zaphkiel.api.event
+
+import ink.ptms.zaphkiel.api.Item
+import ink.ptms.zaphkiel.api.ItemStream
+import io.izzel.taboolib.module.event.EventCancellable
+import org.bukkit.Bukkit
+import org.bukkit.entity.Player
+
+/**
+ * @Author sky
+ * @Since 2019-12-16 10:46
+ */
+class ItemRebuildEvent(val player: Player?, val itemStream: ItemStream, refresh: Boolean) : EventCancellable() {
+
+    init {
+        isCancelled = !refresh
+        async(!Bukkit.isPrimaryThread())
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/ink/ptms/zaphkiel/api/internal/ItemKey.kt b/src/main/java/ink/ptms/zaphkiel/api/internal/ItemKey.kt
new file mode 100644
index 0000000..9572b0f
--- /dev/null
+++ b/src/main/java/ink/ptms/zaphkiel/api/internal/ItemKey.kt
@@ -0,0 +1,10 @@
+package ink.ptms.zaphkiel.api.internal
+
+/**
+ * @Author sky
+ * @Since 2019-12-16 12:44
+ */
+enum class ItemKey(val key: String) {
+
+    ID("a"), HASH("b"), DATA("c")
+}
\ No newline at end of file
diff --git a/src/main/java/ink/ptms/zaphkiel/api/internal/ItemList.kt b/src/main/java/ink/ptms/zaphkiel/api/internal/ItemList.kt
new file mode 100644
index 0000000..e3af2b0
--- /dev/null
+++ b/src/main/java/ink/ptms/zaphkiel/api/internal/ItemList.kt
@@ -0,0 +1,103 @@
+package ink.ptms.zaphkiel.api.internal
+
+import com.google.common.collect.Lists
+import com.google.common.collect.Maps
+import ink.ptms.zaphkiel.ZaphkielAPI
+import ink.ptms.zaphkiel.api.Item
+import io.izzel.taboolib.module.inject.TListener
+import io.izzel.taboolib.module.lite.SimpleIterator
+import io.izzel.taboolib.util.item.ItemBuilder
+import io.izzel.taboolib.util.item.Items
+import org.bukkit.Bukkit
+import org.bukkit.Material
+import org.bukkit.entity.HumanEntity
+import org.bukkit.entity.Player
+import org.bukkit.event.EventHandler
+import org.bukkit.event.Listener
+import org.bukkit.event.inventory.InventoryClickEvent
+import org.bukkit.inventory.Inventory
+import org.bukkit.inventory.InventoryHolder
+import org.bukkit.inventory.ItemStack
+import org.bukkit.inventory.meta.ItemMeta
+import java.util.function.Consumer
+
+/**
+ * @Author 坏黑
+ * @Since 2019-01-03 20:25
+ */
+@TListener(cancel = "cancel")
+class ItemList : Listener {
+
+    fun cancel() {
+        Bukkit.getOnlinePlayers().stream().filter { player -> player.openInventory.topInventory.holder is ItemListHolder }.forEach { it.closeInventory() }
+    }
+
+    @EventHandler
+    fun onClick(e: InventoryClickEvent) {
+        if (e.inventory.holder is ItemListHolder) {
+            e.isCancelled = true
+            // 空物品
+            if (Items.isNull(e.currentItem)) {
+                return
+            }
+            // 点击物品
+            if ((e.inventory.holder as ItemListHolder).items.containsKey(e.rawSlot)) {
+                val item = ZaphkielAPI.registeredItem[(e.inventory.holder as ItemListHolder).items[e.rawSlot]]
+                if (item != null) {
+                    val itemStack = item.build(e.whoClicked as Player).save()
+                    itemStack.amount = if (e.click.isShiftClick) itemStack.type.maxStackSize else 1
+                    e.whoClicked.inventory.addItem(itemStack)
+                }
+            }
+            // 上一页
+            if (e.rawSlot == 47) {
+                open(e.whoClicked as Player, (e.inventory.holder as ItemListHolder).page - 1)
+            }
+            // 下一页
+            if (e.rawSlot == 51) {
+                open(e.whoClicked as Player, (e.inventory.holder as ItemListHolder).page + 1)
+            }
+        }
+    }
+
+    class ItemListHolder(val page: Int) : InventoryHolder {
+        val items = Maps.newHashMap()!!
+
+        override fun getInventory(): Inventory {
+            return Bukkit.createInventory(null, 0)
+        }
+    }
+
+    companion object {
+
+        fun open(player: Player, page: Int) {
+            val itemsAll = ZaphkielAPI.registeredItem.keys.toList()
+            val holder = ItemListHolder(page)
+            val maxPage = Math.floor(itemsAll.size / 28.0).toInt() + 1
+            val inventory = Bukkit.createInventory(holder, 54, "Zaphkiel ItemList : " + (page + 1) + "/" + maxPage)
+            val items = SimpleIterator(itemsAll).listIterator(page * 28, (page + 1) * 28)
+            for (i in items.indices) {
+                try {
+                    val item = ZaphkielAPI.registeredItem[items[i]]!!.build(player).save()
+                    val itemMeta = item.itemMeta
+                    val lore = if (itemMeta!!.hasLore()) itemMeta.lore else Lists.newArrayList()
+                    lore!!.add("")
+                    lore.add("§7序号: §8" + items[i])
+                    itemMeta.lore = lore
+                    item.itemMeta = itemMeta
+                    inventory.setItem(Items.INVENTORY_CENTER[i], item)
+                } catch (t: Throwable) {
+                    inventory.setItem(Items.INVENTORY_CENTER[i], ItemBuilder(Material.BARRIER).name("§4Invalid Item: " + items[i]).build())
+                }
+                holder.items[Items.INVENTORY_CENTER[i]] = items[i].toString()
+            }
+            if (page > 0) {
+                inventory.setItem(47, ItemBuilder(Material.ARROW).name("§f上一页").build())
+            }
+            if (page < maxPage - 1) {
+                inventory.setItem(51, ItemBuilder(Material.ARROW).name("§f下一页").build())
+            }
+            player.openInventory(inventory)
+        }
+    }
+}
diff --git a/src/main/java/ink/ptms/zaphkiel/api/internal/ScriptAPI.kt b/src/main/java/ink/ptms/zaphkiel/api/internal/ScriptAPI.kt
new file mode 100644
index 0000000..fbd10be
--- /dev/null
+++ b/src/main/java/ink/ptms/zaphkiel/api/internal/ScriptAPI.kt
@@ -0,0 +1,32 @@
+package ink.ptms.zaphkiel.api.internal
+
+import io.izzel.taboolib.util.Commands
+import org.bukkit.Bukkit
+import org.bukkit.command.CommandSender
+import org.bukkit.entity.Player
+
+/**
+ * @Author sky
+ * @Since 2019-12-15 22:30
+ */
+object ScriptAPI {
+
+    fun command(sender: CommandSender, command: String) {
+        Commands.dispatchCommand(sender, command)
+    }
+
+    fun commandOP(sender: CommandSender, command: String) {
+        val op = sender.isOp
+        sender.isOp = true
+        try {
+            Commands.dispatchCommand(sender, command)
+        } catch (t: Throwable) {
+            t.printStackTrace()
+        }
+        sender.isOp = op
+    }
+
+    fun commandConsole(command: String) {
+        Commands.dispatchCommand(Bukkit.getConsoleSender(), command)
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/ink/ptms/zaphkiel/api/internal/StructureList.kt b/src/main/java/ink/ptms/zaphkiel/api/internal/StructureList.kt
new file mode 100644
index 0000000..68335eb
--- /dev/null
+++ b/src/main/java/ink/ptms/zaphkiel/api/internal/StructureList.kt
@@ -0,0 +1,58 @@
+package ink.ptms.zaphkiel.api.internal
+
+import io.izzel.taboolib.util.Variables
+
+/**
+ * @Author sky
+ * @Since 2019-12-15 14:55
+ */
+class StructureList(source: List) {
+
+    val cache = source.map { Variables(it).find() }.toList()
+
+    fun buildTrim(vars: Map>): List {
+        val list = build(vars).toMutableList()
+        while (list.isNotEmpty() && list.last().isEmpty()) {
+            list.removeAt(list.size - 1)
+        }
+        return list
+    }
+
+    fun build(vars: Map>): List {
+        val out = arrayListOf()
+        val cache = cache.toMutableList()
+        while (cache.isNotEmpty()) {
+            var more = false
+            var pass = false
+            val builder = StringBuilder()
+            cache[0].variableList.forEach { variable ->
+                if (variable.isVariable) {
+                    if (variable.text.endsWith("...")) {
+                        val list = vars[variable.text.substring(0, variable.text.length - 3)]
+                        if (list?.isEmpty() != false) {
+                            pass = true
+                            return@forEach
+                        }
+                        if (list.isNotEmpty()) {
+                            builder.append(list.removeAt(0))
+                        }
+                        if (list.isNotEmpty()) {
+                            more = true
+                        }
+                    } else {
+                        builder.append(vars[variable.text]?.firstOrNull() ?: "")
+                    }
+                } else {
+                    builder.append(variable.text)
+                }
+            }
+            if (!more) {
+                cache.removeAt(0)
+            }
+            if (!pass) {
+                out.add(builder.toString())
+            }
+        }
+        return out
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/ink/ptms/zaphkiel/api/internal/StructureSingle.kt b/src/main/java/ink/ptms/zaphkiel/api/internal/StructureSingle.kt
new file mode 100644
index 0000000..2c9e8c9
--- /dev/null
+++ b/src/main/java/ink/ptms/zaphkiel/api/internal/StructureSingle.kt
@@ -0,0 +1,26 @@
+package ink.ptms.zaphkiel.api.internal
+
+import io.izzel.taboolib.util.Variables
+
+/**
+ * @Author sky
+ * @Since 2019-12-15 14:55
+ */
+class StructureSingle(source: String) {
+
+    val cache = Variables(source).find()!!
+
+    fun buildTrim(vars: Map): String {
+        return build(vars).trim()
+    }
+
+    fun build(vars: Map): String {
+        return cache.variableList.joinToString("") {
+            if (it.isVariable) {
+                vars[it.text] ?: ""
+            } else {
+                it.text
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/ink/ptms/zaphkiel/module/ItemEvents.kt b/src/main/java/ink/ptms/zaphkiel/module/ItemEvents.kt
new file mode 100644
index 0000000..0c8dd59
--- /dev/null
+++ b/src/main/java/ink/ptms/zaphkiel/module/ItemEvents.kt
@@ -0,0 +1,87 @@
+package ink.ptms.zaphkiel.module
+
+import ink.ptms.zaphkiel.Zaphkiel
+import ink.ptms.zaphkiel.ZaphkielAPI
+import io.izzel.taboolib.module.inject.TListener
+import io.izzel.taboolib.util.item.Items
+import org.bukkit.event.EventHandler
+import org.bukkit.event.EventPriority
+import org.bukkit.event.Listener
+import org.bukkit.event.block.Action
+import org.bukkit.event.player.*
+
+/**
+ * @Author sky
+ * @Since 2019-12-15 22:22
+ */
+@TListener
+class ItemEvents : Listener {
+
+    @EventHandler(ignoreCancelled = true)
+    fun e(e: PlayerDropItemEvent) {
+        val item = ZaphkielAPI.read(e.itemDrop.itemStack)
+        if (item.isExtension()) {
+            item.getZaphkielItem().eval("onDrop", e)
+        }
+    }
+
+    @EventHandler(ignoreCancelled = true)
+    fun e(e: PlayerItemBreakEvent) {
+        val item = ZaphkielAPI.read(e.brokenItem)
+        if (item.isExtension()) {
+            item.getZaphkielItem().eval("onBreak", e)
+        }
+    }
+
+    @EventHandler(ignoreCancelled = true)
+    fun e(e: PlayerItemConsumeEvent) {
+        val item = ZaphkielAPI.read(e.item)
+        if (item.isExtension()) {
+            item.getZaphkielItem().eval("onConsume", e)
+        }
+    }
+
+    @EventHandler(ignoreCancelled = true)
+    fun e(e: PlayerItemDamageEvent) {
+        val item = ZaphkielAPI.read(e.item)
+        if (item.isExtension()) {
+            item.getZaphkielItem().eval("onItemDamage", e)
+        }
+    }
+
+    @EventHandler(ignoreCancelled = true)
+    fun e(e: PlayerSwapHandItemsEvent) {
+        if (Items.nonNull(e.mainHandItem)) {
+            val item = ZaphkielAPI.read(e.mainHandItem!!)
+            if (item.isExtension()) {
+                item.getZaphkielItem().eval("onSwapHand", e)
+            }
+        }
+        if (Items.nonNull(e.offHandItem)) {
+            val item = ZaphkielAPI.read(e.offHandItem!!)
+            if (item.isExtension()) {
+                item.getZaphkielItem().eval("onSwapHand", e)
+            }
+        }
+    }
+
+    @EventHandler
+    fun e(e: PlayerInteractEvent) {
+        if (Items.nonNull(e.item)) {
+            val item = ZaphkielAPI.read(e.item!!)
+            if (item.isVanilla()) {
+                return
+            }
+            when (e.action) {
+                Action.LEFT_CLICK_AIR, Action.LEFT_CLICK_BLOCK -> {
+                    item.getZaphkielItem().eval("onLeftCLick", e)
+                }
+                Action.RIGHT_CLICK_AIR, Action.RIGHT_CLICK_BLOCK -> {
+                    item.getZaphkielItem().eval("onRightClick", e)
+                }
+                else -> {
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/ink/ptms/zaphkiel/module/ItemRefresher.kt b/src/main/java/ink/ptms/zaphkiel/module/ItemRefresher.kt
new file mode 100644
index 0000000..5584a3f
--- /dev/null
+++ b/src/main/java/ink/ptms/zaphkiel/module/ItemRefresher.kt
@@ -0,0 +1,66 @@
+package ink.ptms.zaphkiel.module
+
+import ink.ptms.zaphkiel.Zaphkiel
+import ink.ptms.zaphkiel.ZaphkielAPI
+import io.izzel.taboolib.module.inject.TListener
+import io.izzel.taboolib.module.inject.TSchedule
+import org.bukkit.Bukkit
+import org.bukkit.entity.Player
+import org.bukkit.event.EventHandler
+import org.bukkit.event.EventPriority
+import org.bukkit.event.Listener
+import org.bukkit.event.inventory.InventoryOpenEvent
+import org.bukkit.event.player.*
+
+/**
+ * @Author sky
+ * @Since 2019-12-16 10:40
+ */
+@TListener
+class ItemRefresher : Listener {
+
+    @EventHandler
+    fun e(e: PlayerJoinEvent) {
+        ZaphkielAPI.rebuild(e.player, e.player.inventory)
+    }
+
+    @EventHandler
+    fun e(e: PlayerRespawnEvent) {
+        ZaphkielAPI.rebuild(e.player, e.player.inventory)
+    }
+
+    @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
+    fun e(e: PlayerDropItemEvent) {
+        ZaphkielAPI.rebuild(e.player, e.itemDrop.itemStack).run {
+            if (this.isFromRebuild) {
+                e.itemDrop.setItemStack(this.save())
+            }
+        }
+    }
+
+    @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
+    fun e(e: PlayerPickupItemEvent) {
+        ZaphkielAPI.rebuild(e.player, e.item.itemStack).run {
+            if (this.isFromRebuild) {
+                e.item.setItemStack(this.save())
+            }
+        }
+    }
+
+    @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
+    fun e(e: InventoryOpenEvent) {
+        if (e.inventory.location != null) {
+            Bukkit.getScheduler().runTaskAsynchronously(Zaphkiel.getPlugin(), Runnable {
+                ZaphkielAPI.rebuild(e.player as Player, e.inventory)
+            })
+        }
+    }
+
+    companion object {
+
+        @TSchedule(period = 100, async = true)
+        fun tick() {
+            Bukkit.getOnlinePlayers().forEach { player -> ZaphkielAPI.rebuild(player, player.inventory) }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml
new file mode 100644
index 0000000..e69de29
diff --git a/src/main/resources/display/def.yml b/src/main/resources/display/def.yml
new file mode 100644
index 0000000..58b38b2
--- /dev/null
+++ b/src/main/resources/display/def.yml
@@ -0,0 +1,8 @@
+display0:
+  # 模板名称
+  name: '&7 '
+  # 模板描述
+  lore:
+    - '&9 &8'
+    - ''
+    - '&f'
\ No newline at end of file
diff --git a/src/main/resources/item/def.yml b/src/main/resources/item/def.yml
new file mode 100644
index 0000000..a970141
--- /dev/null
+++ b/src/main/resources/item/def.yml
@@ -0,0 +1,28 @@
+item0:
+  # 模板
+  display: display0
+  # 材质
+  icon: DRIED_KELP
+  # 名称
+  name:
+    # 名称
+    NAME: '&7黑黑黑羊肉'
+  # 描述
+  lore:
+    # 类型
+    TYPE: '&9食物'
+    # 描述
+    LORE:
+      - '&f黑羊身上撕下来的皮肉'
+  # 数据
+  data:
+    # 重量
+    weight: 0.01
+    # 耐久
+    durability: 3
+  # 事件
+  event:
+    # 消耗时
+    onConsume: |-
+      event.setCancelled(true)
+      event.getPlayer().setFoodLevel(event.getPlayer().getFoodLevel() + 2)
\ No newline at end of file
diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml
new file mode 100644
index 0000000..220e1e0
--- /dev/null
+++ b/src/main/resources/plugin.yml
@@ -0,0 +1,5 @@
+name: Zaphkiel
+main: ink.ptms.zaphkiel.Zaphkiel
+author: 坏黑
+
+version: ${version}
\ No newline at end of file
diff --git a/src/test/java/ink/ptms/zaphkiel/WeightHook.java b/src/test/java/ink/ptms/zaphkiel/WeightHook.java
new file mode 100644
index 0000000..6f07907
--- /dev/null
+++ b/src/test/java/ink/ptms/zaphkiel/WeightHook.java
@@ -0,0 +1,18 @@
+package ink.ptms.zaphkiel;
+
+import ink.ptms.zaphkiel.api.event.ItemBuildEvent;
+import ink.ptms.zaphkiel.api.event.ItemRebuildEvent;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+
+/**
+ * @Author sky
+ * @Since 2019-12-15 20:20
+ */
+public class WeightHook implements Listener {
+
+    @EventHandler
+    public void e(ItemBuildEvent.Pre e) {
+        e.addLore("WEIGHT", e.getItemStream().getZaphkielData().get("weight").asDouble());
+    }
+}