├── .github
└── workflows
│ └── buildJar.yml
├── .gitignore
├── LICENSE
├── README.md
├── Version.txt
├── build.gradle.kts
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── libs
├── SkinsRestorer.jar
└── TrMenu-3.0-RC-2.jar
├── settings.gradle.kts
└── src
└── main
├── kotlin
└── me
│ └── arasple
│ └── mc
│ └── trhologram
│ ├── TrHologram.kt
│ ├── api
│ ├── InstantVector.kt
│ ├── Position.kt
│ ├── Settings.kt
│ ├── TrHologramAPI.kt
│ ├── base
│ │ ├── BaseCondition.kt
│ │ ├── ClickHandler.kt
│ │ ├── ItemTexture.kt
│ │ └── TickEvent.kt
│ ├── event
│ │ └── HologramInteractEvent.kt
│ ├── hologram
│ │ ├── HologramBuilder.kt
│ │ ├── HologramComponent.kt
│ │ ├── ItemHologram.kt
│ │ └── TextHologram.kt
│ └── nms
│ │ ├── NMS.kt
│ │ ├── NMSImpl.kt
│ │ ├── NMSListener.kt
│ │ └── packet
│ │ ├── PacketArmorStandModify.kt
│ │ ├── PacketArmorStandName.kt
│ │ ├── PacketEntity.kt
│ │ ├── PacketEntityDestroy.kt
│ │ ├── PacketEntityMount.kt
│ │ ├── PacketEntitySpawn.kt
│ │ ├── PacketEntityVelocity.kt
│ │ ├── PacketEquipment.kt
│ │ ├── PacketHeadRotation.kt
│ │ └── PacketItemModify.kt
│ ├── module
│ ├── action
│ │ ├── ClickReaction.kt
│ │ └── Reaction.kt
│ ├── command
│ │ ├── CommandExecutor.kt
│ │ ├── CommandHandler.kt
│ │ └── impl
│ │ │ ├── CommandCreate.kt
│ │ │ ├── CommandDelete.kt
│ │ │ ├── CommandList.kt
│ │ │ ├── CommandMirror.kt
│ │ │ ├── CommandMovehere.kt
│ │ │ ├── CommandReload.kt
│ │ │ └── CommandTeleport.kt
│ ├── condition
│ │ └── Condition.kt
│ ├── conf
│ │ ├── HologramLoader.kt
│ │ └── Property.kt
│ ├── display
│ │ ├── Hologram.kt
│ │ └── texture
│ │ │ ├── Texture.kt
│ │ │ ├── TextureMeta.kt
│ │ │ ├── TextureType.kt
│ │ │ └── TrMenuTexture.kt
│ ├── editor
│ │ └── Editor.kt
│ ├── hook
│ │ ├── HookAbstract.kt
│ │ ├── HookPlugin.kt
│ │ └── impl
│ │ │ ├── HookSkinsRestorer.kt
│ │ │ └── HookTrMenu.kt
│ ├── listener
│ │ ├── ListenerHologramInteract.kt
│ │ ├── ListenerJoin.kt
│ │ ├── ListenerMovement.kt
│ │ ├── ListenerQuit.kt
│ │ ├── ListenerRespawn.kt
│ │ └── ListenerWorldChange.kt
│ └── service
│ │ ├── Metrics.kt
│ │ └── Updater.kt
│ └── util
│ ├── Heads.kt
│ ├── ItemHelper.kt
│ ├── Parser.kt
│ └── Variables.kt
└── resources
├── holograms
└── Demo.yml
├── lang
├── RU_ru.yml
├── en_US.yml
└── zh_CN.yml
└── settings.yml
/.github/workflows/buildJar.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 | pull_request:
7 | branches: [ master ]
8 |
9 | jobs:
10 | releaseJar:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v2
14 | - uses: actions/setup-java@v1
15 | with:
16 | java-version: 8
17 | - name: Cache .gradle/caches
18 | uses: actions/cache@v1
19 | with:
20 | path: ~/.gradle/caches
21 | key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}
22 | restore-keys: ${{ runner.os }}-gradle-
23 | - name: Cache .gradle/wrapper
24 | uses: actions/cache@v1
25 | with:
26 | path: ~/.gradle/wrapper
27 | key: ${{ runner.os }}-gradle-wrapper-${{ hashFiles('**/*.gradle') }}
28 | restore-keys: ${{ runner.os }}-gradle-wrapper-
29 | - name: Grant execute permission for gradlew
30 | run: chmod +x gradlew
31 | - name: Build with Gradle
32 | run: ./gradlew clean build -s
33 | - name: Upload Artifacts
34 | uses: actions/upload-artifact@v2
35 | with:
36 | name: TrHologram Artifact
37 | path: build/libs/*.jar
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | .idea
3 | build
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Arasple
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 | # TrHologram
3 |
4 | #### Modern & Lightweight Holographic-Plugin
5 | 
6 |
7 | ---
8 |
9 | 
10 |
11 | ---
12 |
13 | ### Oops!
14 | Because of my studies, the updates is slowing down. Sorry :(
15 |
16 | ### Features
17 |
18 | - **Highly Optimized**
19 | - 100% Packet-based hologram (armorstand, item), no-lag
20 | - Async update tasks
21 |
22 | - **Light & Powerful**
23 | - Individual update task for each line
24 | - Custom view distance & view condition
25 | - Custom line spacing and offset for individual line
26 | - Support to display floating item with custom texture
27 | - Interactive holograms (4 clicktypes integrated)
28 | - PlaceholderAPI and custom functions support
29 |
30 | - **API**
31 | - Friendly developer API, create dynamic holograms easily
32 |
33 | ---
34 |
35 | ### API
36 |
37 | #### Dependency
38 |
39 | In Maven:
40 | ```xml
41 |
42 |
43 | roselle-public
44 | https://repo.mcage.cn/repository/maven-public/
45 |
46 |
47 |
48 |
49 |
50 | me.arasple
51 | trmenu
52 | 3.0-PRE-20
53 | pure
54 | provided
55 |
56 |
57 | ```
58 |
59 | In Gradle Kotlin DSL:
60 | ```kotlin
61 | repositories {
62 | maven("https://repo.mcage.cn/repository/maven-public/")
63 | }
64 | dependencies {
65 | compileOnly("me.arasple:TrMenu:3.0-PRE-20:pure")
66 | }
67 |
68 | ```
69 |
70 | #### Usage
71 |
72 | ```java
73 | class Demo {
74 |
75 | public void display(Player viewer) {
76 | Hologram hologram = TrHologramAPI
77 | .builder(viewer.getLocation())
78 | .append("Hello World")
79 | .append(player -> player.getInventory().getItemInMainHand(), 40)
80 | .interspace(0.5)
81 | .append("Time: %server_time_ss%", 20)
82 | .build();
83 |
84 | hologram.refreshVisibility(viewer);
85 |
86 | TextHologram line = hologram.getTextLine(0);
87 | line.setText("Hello TrHologram");
88 | }
89 |
90 | }
91 | ```
--------------------------------------------------------------------------------
/Version.txt:
--------------------------------------------------------------------------------
1 | 2.4-pre25
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | `maven-publish`
3 | id("java")
4 | id("io.izzel.taboolib") version "1.34"
5 | id("org.jetbrains.kotlin.jvm") version "1.5.10"
6 | id("com.github.johnrengelman.shadow") version "7.0.0"
7 | }
8 |
9 | group = "me.arasple.mc.trhologram"
10 | version = "2.4-pre29"
11 | description = "Modern & Advanced Hologram-Plugin for Minecraft Servers"
12 |
13 | taboolib {
14 | install(
15 | "common",
16 | "common-5",
17 | "platform-bukkit",
18 | "module-configuration",
19 | "module-chat",
20 | "module-lang",
21 | "module-nms",
22 | "module-nms-util",
23 | "module-metrics",
24 | "module-kether"
25 | )
26 |
27 | description {
28 | contributors {
29 | name("Arasple")
30 | }
31 | dependencies {
32 | name("PlaceholderAPI").optional(true)
33 | name("TrMenu").optional(true)
34 | name("SkinsRestorer").optional(true)
35 | name("Multiverse-Core").loadafter(true)
36 | name("PlotSquared").loadafter(true)
37 | }
38 | }
39 |
40 | classifier = null
41 | version = "6.0.9-65"
42 | }
43 |
44 | repositories {
45 | mavenCentral()
46 | maven("https://repo.extendedclip.com/content/repositories/placeholderapi/")
47 | maven("https://repo.codemc.org/repository/maven-public/")
48 | }
49 |
50 | dependencies {
51 | compileOnly("org.jetbrains.kotlin:kotlin-stdlib")
52 | compileOnly("ink.ptms:nms-all:1.0.0")
53 | compileOnly("ink.ptms.core:v11900:11900-minimize:mapped")
54 | compileOnly("ink.ptms.core:v11900:11900-minimize:universal")
55 | compileOnly("ink.ptms.core:v11800:11800-minimize:mapped")
56 | compileOnly("ink.ptms.core:v11800:11800-minimize:universal")
57 | compileOnly("ink.ptms.core:v11701:11701:mapped")
58 | compileOnly("ink.ptms.core:v11701:11701:universal")
59 | compileOnly("me.clip:placeholderapi:2.10.9")
60 | compileOnly(fileTree("libs"))
61 | }
62 |
63 |
64 | tasks.shadowJar {
65 | dependencies {
66 | taboolib.modules.forEach { exclude(dependency("io.izzel:taboolib:${taboolib.version}:$it")) }
67 | exclude(dependency("com.google.code.gson:gson:2.8.6"))
68 | exclude(dependency("org.bstats:bstats-bukkit:1.5"))
69 |
70 | exclude("data")
71 | exclude("META-INF/*.kotlin_module")
72 | exclude("META-INF/maven")
73 | exclude("lang")
74 | exclude("holograms")
75 | exclude("*.yml")
76 | }
77 | relocate("taboolib", "${project.group}.taboolib")
78 | archiveClassifier.set("pure")
79 | }
80 |
81 | configure {
82 | publications {
83 | create("shadow") {
84 | shadow.component(this)
85 | groupId = "me.arasple"
86 | }
87 | }
88 | repositories {
89 | maven {
90 | url = uri("https://repo.iroselle.com/repository/maven-releases/")
91 | credentials {
92 | file("access.txt").also {
93 | if (!it.exists()) return@credentials
94 | }.readLines().apply {
95 | username = this[0]
96 | password = this[1]
97 | }
98 | }
99 | }
100 | }
101 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | kotlin.code.style=official
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Micalhl/TrHologram/6cbaeab5511fc7030db15ed92a5edc77ac30d2fe/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/libs/SkinsRestorer.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Micalhl/TrHologram/6cbaeab5511fc7030db15ed92a5edc77ac30d2fe/libs/SkinsRestorer.jar
--------------------------------------------------------------------------------
/libs/TrMenu-3.0-RC-2.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Micalhl/TrHologram/6cbaeab5511fc7030db15ed92a5edc77ac30d2fe/libs/TrMenu-3.0-RC-2.jar
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | rootProject.name = "TrHologram"
2 |
3 |
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/TrHologram.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram
2 |
3 | import me.arasple.mc.trhologram.api.Settings
4 | import me.arasple.mc.trhologram.module.conf.HologramLoader
5 | import me.arasple.mc.trhologram.module.display.Hologram
6 | import me.arasple.mc.trhologram.module.service.Updater
7 | import org.bukkit.Bukkit
8 | import taboolib.common.platform.Plugin
9 | import taboolib.common.platform.function.console
10 | import taboolib.common.platform.function.disablePlugin
11 | import taboolib.common.platform.function.pluginVersion
12 | import taboolib.module.lang.sendLang
13 | import taboolib.module.nms.MinecraftVersion
14 | import taboolib.platform.BukkitPlugin
15 |
16 | /**
17 | * @author Arasple
18 | * @date 2021/1/25 12:11
19 | */
20 | object TrHologram : Plugin() {
21 |
22 | val plugin by lazy { BukkitPlugin.getInstance() }
23 |
24 | override fun onLoad() {
25 | console().sendLang("Plugin-Loading", Bukkit.getBukkitVersion())
26 | }
27 |
28 | override fun onEnable() {
29 | if (MinecraftVersion.majorLegacy <= 10900) {
30 | console().sendLang("Plugin-UnsupportedVersion", pluginVersion)
31 | disablePlugin()
32 | return
33 | }
34 |
35 | Settings.init()
36 | HologramLoader.load(Bukkit.getConsoleSender())
37 | console().sendLang("Plugin-Enabled", pluginVersion)
38 |
39 | Updater.init()
40 | }
41 |
42 | override fun onDisable() {
43 | Hologram.holograms.forEach(Hologram::destroy)
44 | }
45 |
46 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/api/InstantVector.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.api
2 |
3 | import me.arasple.mc.trhologram.api.nms.NMS
4 | import me.arasple.mc.trhologram.api.nms.packet.PacketEntityVelocity
5 | import org.bukkit.World
6 | import org.bukkit.entity.Player
7 | import org.bukkit.util.Vector
8 | import taboolib.common.platform.function.submit
9 |
10 | // 模 / 时间 = 速度
11 | class InstantVector(val x: Double, val y: Double, val z: Double, val arrival: Long = 0): Cloneable {
12 |
13 | val useing = mutableMapOf()
14 |
15 | val hasArrival get() = arrival > 0
16 |
17 | val planVector: InstantVector get() {
18 | if (!hasArrival) {
19 | return this
20 | }
21 | return InstantVector(x / arrival, y / arrival, z / arrival)
22 | }
23 |
24 | fun useToVanilla(player: Player, entityId: Int) {
25 | if (!hasArrival) {
26 | NMS.INSTANCE.sendEntityPacket(player, PacketEntityVelocity(entityId, x, y, z))
27 | return
28 | }
29 | val vec = planVector
30 | useing[entityId] = vec
31 | NMS.INSTANCE.sendEntityPacket(player, PacketEntityVelocity(entityId, vec.x, vec.y, vec.z))
32 | submit(delay = arrival) {
33 | NMS.INSTANCE.sendEntityPacket(player, PacketEntityVelocity(entityId, 0.0, 0.0, 0.0))
34 | useing.remove(entityId)
35 | }
36 | }
37 |
38 | fun toPosition(w: World) =
39 | Position(w, x, y, z)
40 |
41 | override fun clone() =
42 | super.clone() as InstantVector
43 |
44 | companion object {
45 |
46 | @JvmStatic
47 | fun fromVector(v: Vector, arrival: Long= 0) =
48 | InstantVector(v.x, v.y, v.z, arrival)
49 |
50 | }
51 |
52 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/api/Position.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.api
2 |
3 | import org.bukkit.Bukkit
4 | import org.bukkit.Location
5 | import org.bukkit.World
6 | import kotlin.math.pow
7 | import kotlin.math.sqrt
8 |
9 | /**
10 | * @author Arasple
11 | * @date 2021/2/10 10:53
12 | */
13 | class Position: Cloneable {
14 |
15 | val world: World
16 | val x: Double
17 | val y: Double
18 | val z: Double
19 |
20 | constructor(x: Double = 0.0, y: Double = 0.0, z: Double = 0.0) : this(Bukkit.getWorlds()[0], x, y, z)
21 |
22 | constructor(world: World = Bukkit.getWorlds()[0], x: Double = 0.0, y: Double = 0.0, z: Double = 0.0) {
23 | this.world = world
24 | this.x = x
25 | this.y = y
26 | this.z = z
27 | }
28 |
29 | fun distance(l: Position) =
30 | distance(l.x, l.y, l.z)
31 |
32 | fun distance(l: Location) =
33 | distance(l.x, l.y, l.z)
34 |
35 | fun clone(x: Double = 0.0, y: Double = 0.0, z: Double = 0.0) =
36 | Position(world, this.x + x, this.y + y, this.z + z)
37 |
38 | private fun distance(x: Double, y: Double, z: Double) =
39 | sqrt((this.x - x).pow(2) + (this.y - y).pow(2) + (this.z - z).pow(2))
40 |
41 | fun toVelocity() =
42 | InstantVector(x, y, z)
43 |
44 | fun toLocation() =
45 | Location(world, x, y, z)
46 |
47 | override fun toString() =
48 | "${world.name} ~ $x, $y, $z"
49 |
50 | override fun clone(): Location {
51 | return super.clone() as Location
52 | }
53 |
54 | companion object {
55 |
56 | @JvmStatic
57 | fun fromLocation(l: Location) =
58 | Position(l.world!!, l.x, l.y, l.z)
59 |
60 | }
61 |
62 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/api/Settings.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.api
2 |
3 | import taboolib.common.platform.function.console
4 | import taboolib.common5.Baffle
5 | import taboolib.module.configuration.Config
6 | import taboolib.module.configuration.Configuration
7 | import taboolib.module.lang.sendLang
8 | import java.util.concurrent.TimeUnit
9 |
10 | /**
11 | * @author Arasple
12 | * @date 2021/2/11 10:07
13 | */
14 | class Settings {
15 |
16 | companion object {
17 |
18 | @Config("settings.yml", migrate = true, autoReload = true)
19 | lateinit var CONF: Configuration
20 | private set
21 |
22 | internal var INSTANCE = Settings()
23 |
24 | fun init() {
25 | CONF.onReload {
26 | onSettingsReload()
27 | console().sendLang("Configuration-Auto-Reload")
28 | }
29 | }
30 |
31 | fun onSettingsReload() {
32 | INSTANCE = Settings()
33 | }
34 |
35 | }
36 |
37 | val loadPaths: List by lazy {
38 | CONF.getStringList("Loader.Hologram-Files")
39 | }
40 |
41 | val interactDelay by lazy {
42 | Baffle.of(CONF.getLong("Hologram.Interact-Min-Delay").coerceAtLeast(100), TimeUnit.MILLISECONDS)
43 | }
44 |
45 | val lineSpacing by lazy {
46 | CONF.getDouble("Hologram.Options.Default-Line-Spacing", 0.25)
47 | }
48 |
49 | val viewDistance by lazy {
50 | CONF.getDouble("Hologram.Options.Default-View-Distance", 20.0)
51 | }
52 |
53 | val viewCondition by lazy {
54 | CONF.getString("Hologram.Options.Default-View-Condition", "")
55 | }
56 |
57 | val refershCondition by lazy {
58 | CONF.getLong("Hologram.Options.Default-Refresh-Condition", -1)
59 | }
60 |
61 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/api/TrHologramAPI.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.api
2 |
3 | import me.arasple.mc.trhologram.api.hologram.HologramBuilder
4 | import me.arasple.mc.trhologram.api.hologram.HologramComponent
5 | import me.arasple.mc.trhologram.api.hologram.ItemHologram
6 | import me.arasple.mc.trhologram.api.hologram.TextHologram
7 | import me.arasple.mc.trhologram.module.display.Hologram
8 | import org.bukkit.Location
9 | import org.bukkit.entity.Player
10 | import org.bukkit.inventory.ItemStack
11 | import taboolib.common.platform.function.adaptPlayer
12 | import taboolib.common5.mirrorNow
13 | import taboolib.library.kether.LocalizedException
14 | import taboolib.module.kether.KetherShell
15 | import java.util.concurrent.CompletableFuture
16 |
17 | /**
18 | * @author Arasple
19 | * @date 2021/2/10 9:38
20 | */
21 | object TrHologramAPI {
22 |
23 | /**
24 | * 实体 ID 取得
25 | */
26 | private var INDEX = resetIndex()
27 |
28 | fun getIndex(): Int {
29 | return INDEX++
30 | }
31 |
32 | internal fun resetIndex(): Int {
33 | return 1197897763 + (0..7763).random()
34 | }
35 |
36 | @JvmStatic
37 | fun eval(player: Player, script: String): CompletableFuture {
38 | return mirrorNow("Hologram:Handler:ScriptEval") {
39 | return@mirrorNow try {
40 | KetherShell.eval(script, namespace = listOf("trhologram", "trmenu")) {
41 | sender = adaptPlayer(player)
42 | }
43 | } catch (e: LocalizedException) {
44 | println("[TrHologram] Unexpected exception while parsing kether shell:")
45 | e.localizedMessage.split("\n").forEach {
46 | println("[TrHologram] $it")
47 | }
48 | CompletableFuture.completedFuture(false)
49 | }
50 | }
51 | }
52 |
53 | @JvmStatic
54 | fun getHologramById(id: String): Hologram? {
55 | return Hologram.holograms.find { it.id == id }
56 | }
57 |
58 | @JvmStatic
59 | fun createTextCompoent(
60 | initText: String,
61 | location: Location,
62 | tick: Long = -1,
63 | onTick: (HologramComponent) -> Unit = {}
64 | ): TextHologram {
65 | return TextHologram(initText, Position.fromLocation(location), tick, onTick)
66 | }
67 |
68 | @JvmStatic
69 | fun createItemCompoent(
70 | initItem: ItemStack,
71 | location: Location,
72 | tick: Long = -1,
73 | onTick: (HologramComponent) -> Unit = {}
74 | ): ItemHologram {
75 | return ItemHologram(initItem, Position.fromLocation(location), tick, onTick)
76 | }
77 |
78 | @JvmStatic
79 | fun builder(location: Location): HologramBuilder {
80 | return HologramBuilder(location = location)
81 | }
82 |
83 |
84 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/api/base/BaseCondition.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.api.base
2 |
3 | import org.bukkit.entity.Player
4 | import java.util.concurrent.CompletableFuture
5 |
6 | /**
7 | * @author Arasple
8 | * @date 2021/2/12 14:08
9 | */
10 | fun interface BaseCondition {
11 |
12 | fun eval(player: Player): CompletableFuture
13 |
14 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/api/base/ClickHandler.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.api.base
2 |
3 | import me.arasple.mc.trhologram.api.event.HologramInteractEvent
4 | import org.bukkit.entity.Player
5 |
6 | /**
7 | * @author Arasple
8 | * @date 2021/2/12 14:10
9 | */
10 | fun interface ClickHandler {
11 |
12 | fun eval(player: Player, type: HologramInteractEvent.Type)
13 |
14 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/api/base/ItemTexture.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.api.base
2 |
3 | import org.bukkit.entity.Player
4 | import org.bukkit.inventory.ItemStack
5 |
6 | /**
7 | * @author Arasple
8 | * @date 2021/2/11 22:50
9 | */
10 | fun interface ItemTexture {
11 |
12 | fun generate(player: Player): ItemStack
13 |
14 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/api/base/TickEvent.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.api.base
2 |
3 | import me.arasple.mc.trhologram.api.hologram.HologramComponent
4 |
5 | /**
6 | * @author Arasple
7 | * @date 2021/2/12 20:38
8 | */
9 | fun interface TickEvent {
10 |
11 | fun run(hologramComponent: HologramComponent)
12 |
13 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/api/event/HologramInteractEvent.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.api.event
2 |
3 | import me.arasple.mc.trhologram.module.display.Hologram
4 | import org.bukkit.entity.Player
5 | import taboolib.platform.type.BukkitProxyEvent
6 |
7 | /**
8 | * @author Arasple
9 | * @date 2021/2/11 16:34
10 | */
11 | class HologramInteractEvent(val player: Player, val type: Type, val hologram: Hologram) : BukkitProxyEvent() {
12 |
13 | enum class Type {
14 |
15 | ALL,
16 |
17 | LEFT,
18 |
19 | RIGHT,
20 |
21 | SHIFT_LEFT,
22 |
23 | SHIFT_RIGHT
24 |
25 | }
26 |
27 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/api/hologram/HologramBuilder.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.api.hologram
2 |
3 | import me.arasple.mc.trhologram.api.Position
4 | import me.arasple.mc.trhologram.api.base.BaseCondition
5 | import me.arasple.mc.trhologram.api.base.ClickHandler
6 | import me.arasple.mc.trhologram.api.base.ItemTexture
7 | import me.arasple.mc.trhologram.api.base.TickEvent
8 | import me.arasple.mc.trhologram.module.display.Hologram
9 | import org.bukkit.Location
10 | import java.util.*
11 |
12 | /**
13 | * @author Arasple
14 | * @date 2021/2/12 14:04
15 | */
16 | class HologramBuilder(
17 | private val id: String = UUID.randomUUID().toString(),
18 | private val location: Location,
19 | var offset: Double = 0.25,
20 | var viewDistance: Double = 15.0,
21 | var viewCondition: BaseCondition? = null,
22 | var refreshCondition: Long = -1,
23 | var clickHandler: ClickHandler = ClickHandler { _, _ -> }
24 | ) {
25 |
26 | private val components = mutableListOf()
27 | private var position = Position.fromLocation(location).clone(y = -1.25)
28 |
29 | fun offset(value: Double): HologramBuilder {
30 | offset = value
31 | return this
32 | }
33 |
34 | fun interspace(value: Double): HologramBuilder {
35 | position = position.clone(y = -value)
36 | return this
37 | }
38 |
39 | /**
40 | * Append a text line
41 | */
42 | @JvmOverloads
43 | fun append(
44 | text: String,
45 | update: Long = -1,
46 | offset: Double = this.offset,
47 | onTick: TickEvent? = null
48 | ): HologramBuilder {
49 | interspace(offset)
50 | components.add(TextHologram(text, position, update, onTick))
51 | return this
52 | }
53 |
54 | /**
55 | * Append a floating item line
56 | */
57 | @JvmOverloads
58 | fun append(
59 | texture: ItemTexture,
60 | update: Long = -1,
61 | offset: Double = this.offset,
62 | onTick: TickEvent? = null
63 | ): HologramBuilder {
64 | interspace(0.5 + offset)
65 | components.add(ItemHologram(texture, position, update, onTick))
66 |
67 | return this
68 | }
69 |
70 | fun viewDistance(value: Double): HologramBuilder {
71 | viewDistance = value
72 | return this
73 | }
74 |
75 | fun viewCondition(value: BaseCondition): HologramBuilder {
76 | viewCondition = value
77 | return this
78 | }
79 |
80 | fun refreshCondition(period: Long): HologramBuilder {
81 | refreshCondition = period
82 | return this
83 | }
84 |
85 | fun click(handler: ClickHandler): HologramBuilder {
86 | clickHandler = handler
87 | return this
88 | }
89 |
90 | fun removeLast() {
91 | components.removeLast()
92 | position = components.last().position
93 | }
94 |
95 | /**
96 | * @param external
97 | */
98 | @JvmOverloads
99 | fun build(external: Boolean = true): Hologram {
100 | return Hologram(
101 | id,
102 | Position.fromLocation(location),
103 | viewDistance,
104 | viewCondition,
105 | refreshCondition,
106 | components,
107 | clickHandler
108 | ).also {
109 | (if (external) Hologram.externalHolograms
110 | else Hologram.holograms).add(it)
111 | }
112 | }
113 |
114 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/api/hologram/HologramComponent.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.api.hologram
2 |
3 | import me.arasple.mc.trhologram.api.Position
4 | import me.arasple.mc.trhologram.api.TrHologramAPI
5 | import me.arasple.mc.trhologram.api.base.TickEvent
6 | import me.arasple.mc.trhologram.api.nms.packet.PacketEntityDestroy
7 | import org.bukkit.Bukkit
8 | import org.bukkit.entity.Player
9 | import taboolib.common.platform.function.submit
10 | import taboolib.common.platform.service.PlatformExecutor
11 | import kotlin.properties.Delegates
12 |
13 | /**
14 | * @author Arasple
15 | * @date 2021/2/10 10:36
16 | */
17 | abstract class HologramComponent(
18 | val position: Position,
19 | tick: Long = -1,
20 | val onTick: TickEvent?
21 | ) {
22 |
23 | abstract fun spawn(player: Player)
24 |
25 | abstract fun onTick()
26 |
27 | val entityId by lazy { TrHologramAPI.getIndex() }
28 |
29 | internal val viewers = mutableSetOf()
30 |
31 | private var task: PlatformExecutor.PlatformTask? = null
32 |
33 | var period0 by Delegates.observable(tick) { _, _, _ ->
34 | deployment()
35 | }
36 |
37 | init {
38 | deployment()
39 | }
40 |
41 | private fun deployment() {
42 | task?.cancel()
43 | if (period0 > 0) task = submit(delay = period0, period = period0, async = true) {
44 | viewers.removeIf {
45 | val player = Bukkit.getPlayerExact(it)
46 | player == null || !player.isOnline
47 | }
48 | onTick()
49 | onTick?.run(this@HologramComponent)
50 | }
51 | }
52 |
53 | fun destroy(player: Player) {
54 | PacketEntityDestroy(entityId).send(player)
55 | viewers.remove(player.name)
56 | }
57 |
58 | fun destroy() {
59 | task?.cancel()
60 | forViewers { destroy(it) }
61 | }
62 |
63 | fun forViewers(viewer: (Player) -> Unit) {
64 | viewers.mapNotNull { Bukkit.getPlayerExact(it) }.forEach(viewer)
65 | }
66 |
67 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/api/hologram/ItemHologram.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.api.hologram
2 |
3 | import me.arasple.mc.trhologram.api.Position
4 | import me.arasple.mc.trhologram.api.TrHologramAPI
5 | import me.arasple.mc.trhologram.api.base.ItemTexture
6 | import me.arasple.mc.trhologram.api.base.TickEvent
7 | import me.arasple.mc.trhologram.api.nms.packet.*
8 | import me.arasple.mc.trhologram.module.display.texture.Texture
9 | import org.bukkit.Bukkit
10 | import org.bukkit.entity.Player
11 | import org.bukkit.inventory.ItemStack
12 | import taboolib.common.platform.function.submit
13 | import taboolib.common5.mirrorNow
14 |
15 | /**
16 | * @author Arasple
17 | * @date 2021/2/10 21:29
18 | *
19 | * Y: position.Y + 0.75
20 | */
21 | class ItemHologram(
22 | texture: ItemTexture,
23 | position: Position,
24 | tick: Long = -1,
25 | onTick: TickEvent? = null
26 | ) : HologramComponent(position.clone(y = 1.3), tick, onTick) {
27 |
28 | constructor(
29 | itemStack: ItemStack,
30 | position: Position,
31 | tick: Long = -1,
32 | onTick: (HologramComponent) -> Unit = {}
33 | ) : this(
34 | Texture.createTexture(itemStack), position.clone(y = 1.3), tick, onTick
35 | )
36 |
37 | var display: ItemTexture = texture
38 | set(value) {
39 | forViewers { updateItem(it) }
40 | field = value
41 | }
42 |
43 | override fun spawn(player: Player) {
44 | destroy(player)
45 | val teid = TrHologramAPI.getIndex()
46 |
47 | PacketEntitySpawn(teid, position).send(player)
48 | PacketArmorStandModify(
49 | teid,
50 | isInvisible = true,
51 | isGlowing = false,
52 | isSmall = true,
53 | hasArms = false,
54 | noBasePlate = true,
55 | isMarker = true
56 | ).send(player)
57 |
58 | PacketEntitySpawn(entityId, position, false).send(player)
59 | updateItem(player)
60 |
61 | submit(delay = 1L, async = !Bukkit.isPrimaryThread()) {
62 | PacketEntityMount(teid, IntArray(1) { entityId }).send(player)
63 | submit(delay = 20 * 5, async = !Bukkit.isPrimaryThread()) {
64 | PacketEntityDestroy(teid).send(player)
65 | }
66 | }
67 |
68 | viewers.add(player.name)
69 | }
70 |
71 | override fun onTick() {
72 | mirrorNow("Hologram:Event:Tick:ItemComponent") {
73 | forViewers { updateItem(it) }
74 | }
75 | }
76 |
77 | private fun updateItem(player: Player) {
78 | PacketItemModify(entityId, isInvisible = false, isGlowing = false, itemStack = display.generate(player)).send(
79 | player
80 | )
81 | }
82 |
83 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/api/hologram/TextHologram.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.api.hologram
2 |
3 | import me.arasple.mc.trhologram.api.Position
4 | import me.arasple.mc.trhologram.api.base.TickEvent
5 | import me.arasple.mc.trhologram.api.nms.packet.PacketArmorStandModify
6 | import me.arasple.mc.trhologram.api.nms.packet.PacketArmorStandName
7 | import me.arasple.mc.trhologram.api.nms.packet.PacketEntitySpawn
8 | import me.arasple.mc.trhologram.util.parseString
9 | import org.bukkit.entity.Player
10 | import taboolib.common5.mirrorNow
11 |
12 | /**
13 | * @author Arasple
14 | * @date 2021/2/10 10:28
15 | *
16 | * Y: rawY + 1 ~ rawY + 1.25
17 | */
18 | class TextHologram(
19 | name: String,
20 | position: Position,
21 | tick: Long,
22 | onTick: TickEvent? = null
23 | ) : HologramComponent(position, tick, onTick) {
24 |
25 | var text: String = name
26 | set(value) {
27 | onTick()
28 | field = value
29 | }
30 |
31 | private fun updateName(player: Player) {
32 | PacketArmorStandName(entityId, true, player.parseString(text)).send(player)
33 | }
34 |
35 | override fun spawn(player: Player) {
36 | PacketEntitySpawn(entityId, position).send(player)
37 | PacketArmorStandModify(
38 | entityId,
39 | isInvisible = true,
40 | isGlowing = false,
41 | isSmall = true,
42 | hasArms = false,
43 | noBasePlate = true,
44 | isMarker = false
45 | ).send(player)
46 | updateName(player)
47 |
48 | viewers.add(player.name)
49 | }
50 |
51 | override fun onTick() {
52 | mirrorNow("Hologram:Event:Tick:TextComponent") {
53 | forViewers { updateName(it) }
54 | }
55 | }
56 |
57 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/api/nms/NMS.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.api.nms
2 |
3 | import com.mojang.authlib.GameProfile
4 | import me.arasple.mc.trhologram.api.nms.packet.PacketEntity
5 | import org.bukkit.entity.Player
6 | import org.bukkit.util.Vector
7 | import taboolib.common.reflect.Reflex.Companion.setProperty
8 | import taboolib.module.nms.nmsProxy
9 | import taboolib.module.nms.sendPacket
10 |
11 | /**
12 | * @author Arasple
13 | * @date 2020/12/4 21:20
14 | */
15 | abstract class NMS {
16 |
17 | companion object {
18 |
19 | val INSTANCE by lazy {
20 | nmsProxy()
21 | }
22 | }
23 |
24 | abstract fun sendEntityPacket(player: Player, vararg packets: PacketEntity)
25 |
26 | abstract fun sendEntityMetadata(player: Player, entityId: Int, vararg objects: Any)
27 |
28 | abstract fun parseVec3d(obj: Any): Vector
29 |
30 | fun sendPacket(player: Player, packet: Any, vararg fields: Pair) {
31 | fields.forEach { packet.setProperty(it.first.toString(), it.second) }
32 | player.sendPacket(packet)
33 | }
34 |
35 | abstract fun getGameProfile(player: Player): GameProfile
36 |
37 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/api/nms/NMSImpl.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.api.nms
2 |
3 | import com.mojang.authlib.GameProfile
4 | import com.mojang.datafixers.util.Pair
5 | import me.arasple.mc.trhologram.api.nms.packet.*
6 | import me.arasple.mc.trhologram.util.ItemHelper
7 | import net.minecraft.server.v1_16_R1.*
8 | import net.minecraft.server.v1_16_R3.ChatComponentText
9 | import net.minecraft.server.v1_16_R3.EntityTypes
10 | import net.minecraft.server.v1_16_R3.PacketPlayOutMount
11 | import org.bukkit.Material
12 | import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer
13 | import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftItemStack
14 | import org.bukkit.craftbukkit.v1_16_R3.util.CraftChatMessage
15 | import org.bukkit.entity.Player
16 | import org.bukkit.inventory.EquipmentSlot
17 | import org.bukkit.inventory.ItemStack
18 | import org.bukkit.util.Vector
19 | import taboolib.common.reflect.Reflex.Companion.unsafeInstance
20 | import taboolib.module.nms.MinecraftVersion
21 | import taboolib.type.BukkitEquipment
22 | import java.util.*
23 |
24 | /**
25 | * @author Arasple
26 | * @date 2020/12/4 21:25
27 | */
28 | class NMSImpl : NMS() {
29 |
30 | private val version = MinecraftVersion.majorLegacy
31 | private val emptyItemStack = CraftItemStack.asNMSCopy((ItemStack(Material.AIR)))
32 | private val indexs = arrayOf(
33 | // armorstand
34 | arrayOf(11500 to 14, 11400 to 13, 11000 to 11, 10900 to 10),
35 | // item
36 | arrayOf(11300 to 7, 11000 to 6, 10900 to 5),
37 | ).map { it -> it.firstOrNull { version >= it.first }?.second ?: -1 }
38 |
39 | /**
40 | * 处理实体封装后的数据包
41 | */
42 | override fun sendEntityPacket(player: Player, vararg packets: PacketEntity) {
43 | packets.forEach {
44 | val id = it.entityId
45 |
46 | when (it) {
47 | // Set Entity velocity
48 | is PacketEntityVelocity -> {
49 | sendPacket(player, PacketPlayOutEntityVelocity(id, Vec3D(it.x, it.y, it.z)))
50 | }
51 | // Destroy entity
52 | is PacketEntityDestroy -> sendPacket(player, PacketPlayOutEntityDestroy(id))
53 | // Spawn Armor Stand
54 | is PacketEntitySpawn -> {
55 | if (MinecraftVersion.isUniversal) {
56 | sendPacket(
57 | player,
58 | PacketPlayOutSpawnEntity::class.java.unsafeInstance(),
59 | "id" to id,
60 | "uuid" to (it.uuid ?: UUID.randomUUID()),
61 | "x" to it.position.x,
62 | "y" to it.position.y,
63 | "z" to it.position.z,
64 | "xa" to 0,
65 | "ya" to 0,
66 | "za" to 0,
67 | "type" to if (it.type) EntityTypes.ARMOR_STAND else EntityTypes.ITEM
68 | )
69 | } else {
70 | sendPacket(
71 | player,
72 | PacketPlayOutSpawnEntity(),
73 | "a" to id,
74 | "b" to (it.uuid ?: UUID.randomUUID()),
75 | "c" to it.position.x,
76 | "d" to it.position.y,
77 | "e" to it.position.z,
78 | "f" to 0.toByte(),
79 | "g" to 0.toByte(),
80 | "k" to if (it.type) if (version >= 11400) EntityTypes.ARMOR_STAND else 78 else if (version >= 11400) EntityTypes.ITEM else 2
81 | )
82 | }
83 | // Cancel gravity
84 | sendEntityMetadata(
85 | player,
86 | id,
87 | getMetaEntityBoolean(5, true)
88 | )
89 | }
90 | is PacketEntityMount -> {
91 | if (MinecraftVersion.isUniversal) {
92 | sendPacket(player, PacketPlayOutMount::class.java.unsafeInstance(), "vehicle" to it.entityId, "passengers" to it.mount)
93 | } else {
94 | sendPacket(player, PacketPlayOutMount(), "a" to it.entityId, "b" to it.mount)
95 | }
96 | }
97 | // Packet Modify
98 | is PacketArmorStandModify -> {
99 | var entity = 0
100 | var armorstand = 0
101 | if (it.isInvisible) entity += 0x20.toByte()
102 | if (it.isGlowing) entity += 0x40.toByte()
103 | if (it.isSmall) armorstand += 0x01.toByte()
104 | if (it.hasArms) armorstand += 0x04.toByte()
105 | if (it.noBasePlate) armorstand += 0x08.toByte()
106 | if (it.isMarker) armorstand += 0x10.toByte()
107 | sendEntityMetadata(
108 | player, id,
109 | getMetaEntityByte(0, entity.toByte()),
110 | getMetaEntityByte(indexs[0], armorstand.toByte()),
111 | )
112 | }
113 | // Modify Item
114 | is PacketItemModify -> {
115 | var entity = 0
116 | val itemNull = ItemHelper.isNull(it.itemStack)
117 | if (it.isInvisible || itemNull) entity += 0x80.toByte()
118 | if (it.isGlowing) entity += 0x40.toByte()
119 |
120 | sendEntityMetadata(player, id, getMetaEntityByte(0, entity.toByte()))
121 | if (!itemNull) sendEntityMetadata(player, id, getMetaItem(indexs[1], it.itemStack))
122 | }
123 | // Entity Name
124 | is PacketArmorStandName -> {
125 | sendEntityMetadata(
126 | player, id,
127 | getMetaEntityChatBaseComponent(2, it.name),
128 | getMetaEntityBoolean(3, it.isCustomNameVisible)
129 | )
130 | }
131 | is PacketHeadRotation -> {
132 | if (MinecraftVersion.isUniversal) {
133 | sendPacket(
134 | player,
135 | PacketPlayOutEntityHeadRotation::class.java.unsafeInstance(),
136 | "id" to id,
137 | "headYaw" to it.headYaw
138 | )
139 | } else {
140 | sendPacket(
141 | player,
142 | PacketPlayOutEntityHeadRotation(),
143 | "a" to id,
144 | "b" to it.headYaw
145 | )
146 | }
147 | }
148 | is PacketEquipment -> updateEquipment(player, id, it.slot, it.itemStack)
149 | }
150 | }
151 |
152 | }
153 |
154 | /**
155 | * 更新实体属性 Metadata
156 | */
157 | override fun sendEntityMetadata(player: Player, entityId: Int, vararg objects: Any) {
158 | if (MinecraftVersion.isUniversal) {
159 | sendPacket(
160 | player,
161 | PacketPlayOutEntityMetadata::class.java.unsafeInstance(),
162 | "id" to entityId,
163 | "packedItems" to objects.map { it as DataWatcher.Item<*> }.toList()
164 | )
165 | } else {
166 | sendPacket(
167 | player,
168 | PacketPlayOutEntityMetadata(),
169 | "a" to entityId,
170 | "b" to objects.map { it as DataWatcher.Item<*> }.toList()
171 | )
172 | }
173 | }
174 |
175 | override fun parseVec3d(obj: Any): Vector {
176 | return Vector((obj as Vec3D).x, obj.y, obj.z)
177 | }
178 |
179 | override fun getGameProfile(player: Player): GameProfile {
180 | return (player as CraftPlayer).profile
181 | }
182 |
183 | /**
184 | * 私有方法 & NMS 相关处理
185 | */
186 |
187 | // TODO
188 | private fun updateEquipment(player: Player, entityId: Int, slot: EquipmentSlot, itemStack: ItemStack) {
189 | when {
190 | version >= 11600 -> {
191 | sendPacket(
192 | player,
193 | PacketPlayOutEntityEquipment(
194 | entityId,
195 | listOf(
196 | Pair(
197 | EnumItemSlot.fromName(BukkitEquipment.fromBukkit(slot)?.nms),
198 | org.bukkit.craftbukkit.v1_16_R1.inventory.CraftItemStack.asNMSCopy(itemStack)
199 | )
200 | )
201 | )
202 | )
203 | }
204 | version >= 11300 -> {
205 | sendPacket(
206 | player,
207 | net.minecraft.server.v1_13_R2.PacketPlayOutEntityEquipment(
208 | entityId,
209 | net.minecraft.server.v1_13_R2.EnumItemSlot.fromName(BukkitEquipment.fromBukkit(slot)?.nms),
210 | org.bukkit.craftbukkit.v1_13_R2.inventory.CraftItemStack.asNMSCopy(itemStack)
211 | )
212 | )
213 | }
214 | else -> {
215 | sendPacket(
216 | player,
217 | net.minecraft.server.v1_12_R1.PacketPlayOutEntityEquipment(
218 | entityId,
219 | net.minecraft.server.v1_12_R1.EnumItemSlot.a(BukkitEquipment.fromBukkit(slot)?.nms),
220 | org.bukkit.craftbukkit.v1_12_R1.inventory.CraftItemStack.asNMSCopy(itemStack)
221 | )
222 | )
223 | }
224 | }
225 | }
226 |
227 |
228 | private fun getMetaEntityByte(index: Int, value: Byte): Any {
229 | return DataWatcher.Item(DataWatcherObject(index, DataWatcherRegistry.a), value)
230 | }
231 |
232 | private fun getMetaEntityBoolean(index: Int, value: Boolean): Any {
233 | return if (version >= 11300) {
234 | DataWatcher.Item(DataWatcherObject(index, DataWatcherRegistry.i), value)
235 | } else {
236 | net.minecraft.server.v1_12_R1.DataWatcher.Item(
237 | net.minecraft.server.v1_12_R1.DataWatcherObject(
238 | index,
239 | net.minecraft.server.v1_12_R1.DataWatcherRegistry.h
240 | ), value
241 | )
242 | }
243 | }
244 |
245 | private fun getMetaEntityChatBaseComponent(index: Int, name: String?): Any {
246 | return if (version >= 11300) {
247 | DataWatcher.Item>(
248 | DataWatcherObject(index, DataWatcherRegistry.f),
249 | Optional.ofNullable(
250 | (if (name == null) null else toChatBaseComponent(name, true)) as IChatBaseComponent?
251 | )
252 | )
253 | } else {
254 | net.minecraft.server.v1_12_R1.DataWatcher.Item(
255 | net.minecraft.server.v1_12_R1.DataWatcherObject(
256 | index,
257 | net.minecraft.server.v1_12_R1.DataWatcherRegistry.d
258 | ), name ?: ""
259 | )
260 | }
261 | }
262 |
263 | private fun getMetaItem(index: Int, itemStack: ItemStack): Any {
264 | return when {
265 | version >= 11300 -> {
266 | DataWatcher.Item(
267 | DataWatcherObject(index, DataWatcherRegistry.g),
268 | org.bukkit.craftbukkit.v1_16_R1.inventory.CraftItemStack.asNMSCopy(itemStack)
269 | )
270 | }
271 | version >= 11200 -> {
272 | net.minecraft.server.v1_12_R1.DataWatcher.Item(
273 | net.minecraft.server.v1_12_R1.DataWatcherObject(
274 | 6,
275 | net.minecraft.server.v1_12_R1.DataWatcherRegistry.f
276 | ), org.bukkit.craftbukkit.v1_12_R1.inventory.CraftItemStack.asNMSCopy(itemStack)
277 | )
278 | }
279 | else -> {
280 | return net.minecraft.server.v1_9_R2.DataWatcher.Item(
281 | net.minecraft.server.v1_9_R2.DataWatcherObject(
282 | 6,
283 | net.minecraft.server.v1_9_R2.DataWatcherRegistry.f
284 | ),
285 | com.google.common.base.Optional.fromNullable(
286 | org.bukkit.craftbukkit.v1_9_R2.inventory.CraftItemStack.asNMSCopy(
287 | itemStack
288 | )
289 | )
290 | )
291 | }
292 | }
293 | }
294 |
295 | private fun toChatBaseComponent(string: String, craftChatMessage: Boolean): Any {
296 | return if (craftChatMessage) CraftChatMessage.fromString(string).first()
297 | else ChatComponentText(string)
298 | }
299 |
300 | private fun toNMSItemStack(vararg itemStack: ItemStack?): Any {
301 | if (itemStack.size > 1) {
302 | return itemStack.map { asNMSCopy(it) }
303 | }
304 | return asNMSCopy(itemStack[0])
305 | }
306 |
307 | private fun asNMSCopy(itemStack: ItemStack?): Any {
308 | return if (itemStack == null || itemStack.type == Material.AIR) emptyItemStack
309 | else CraftItemStack.asNMSCopy(itemStack)
310 | }
311 |
312 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/api/nms/NMSListener.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.api.nms
2 |
3 | import me.arasple.mc.trhologram.api.event.HologramInteractEvent
4 | import me.arasple.mc.trhologram.api.event.HologramInteractEvent.Type.*
5 | import me.arasple.mc.trhologram.module.display.Hologram
6 | import taboolib.common.platform.event.SubscribeEvent
7 | import taboolib.common5.mirrorNow
8 | import taboolib.module.nms.MinecraftVersion
9 | import taboolib.module.nms.PacketReceiveEvent
10 |
11 | /**
12 | * @author Arasple
13 | * @date 2021/2/10 11:27
14 | */
15 | object NMSListener {
16 |
17 | @SubscribeEvent
18 | fun e(e: PacketReceiveEvent) {
19 | if (e.packet.name == "PacketPlayInUseEntity") {
20 | mirrorNow("Hologram:Event:Interact") {
21 | val entityId = e.packet.read("a").also {
22 | if (it == null || it < 1197897763) {
23 | return@mirrorNow
24 | }
25 | }
26 | val hologram =
27 | Hologram.findHologram { it -> it.components.any { it.entityId == entityId } } ?: return@mirrorNow
28 |
29 | val sneaking = e.player.isSneaking
30 | if (MinecraftVersion.isUniversal) {
31 | val action = e.packet.read("action")!!
32 | when (action.javaClass.simpleName) {
33 | // ATTACK
34 | "d" -> if (sneaking) SHIFT_LEFT else LEFT
35 | else -> if (sneaking) SHIFT_RIGHT else RIGHT
36 | }
37 | } else {
38 | val type = when (e.packet.read("action").toString()) {
39 | "ATTACK" -> if (sneaking) SHIFT_LEFT else LEFT
40 | else -> if (sneaking) SHIFT_RIGHT else RIGHT
41 | }
42 |
43 | HologramInteractEvent(e.player, type, hologram).call()
44 | }
45 | }
46 | }
47 | }
48 |
49 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/api/nms/packet/PacketArmorStandModify.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.api.nms.packet
2 |
3 | /**
4 | * @author Arasple
5 | * @date 2021/1/25 12:20
6 | */
7 | class PacketArmorStandModify(
8 | entityId: Int,
9 | val isInvisible: Boolean,
10 | val isGlowing: Boolean,
11 | val isSmall: Boolean,
12 | val hasArms: Boolean,
13 | val noBasePlate: Boolean,
14 | val isMarker: Boolean
15 | ) : PacketEntity(entityId)
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/api/nms/packet/PacketArmorStandName.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.api.nms.packet
2 |
3 | /**
4 | * @author Arasple
5 | * @date 2021/1/25 12:20
6 | */
7 | class PacketArmorStandName(entityId: Int, val isCustomNameVisible: Boolean, val name: String) : PacketEntity(entityId)
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/api/nms/packet/PacketEntity.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.api.nms.packet
2 |
3 | import me.arasple.mc.trhologram.api.nms.NMS
4 | import org.bukkit.entity.Player
5 | import java.util.*
6 |
7 | /**
8 | * @author Arasple
9 | * @date 2020/12/4 21:22
10 | */
11 | abstract class PacketEntity(val entityId: Int = -1, val uuid: UUID? = null) {
12 |
13 | fun send(player: Player) = NMS.INSTANCE.sendEntityPacket(player, this)
14 |
15 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/api/nms/packet/PacketEntityDestroy.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.api.nms.packet
2 |
3 | /**
4 | * @author Arasple
5 | * @date 2021/1/25 12:20
6 | */
7 | class PacketEntityDestroy(entityId: Int) : PacketEntity(entityId)
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/api/nms/packet/PacketEntityMount.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.api.nms.packet
2 |
3 | /**
4 | * @author Arasple
5 | * @date 2020/12/4 21:22
6 | */
7 | class PacketEntityMount(entityId: Int, val mount: IntArray) : PacketEntity(entityId)
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/api/nms/packet/PacketEntitySpawn.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.api.nms.packet
2 |
3 | import me.arasple.mc.trhologram.api.Position
4 |
5 | /**
6 | * @author Arasple
7 | * @date 2021/1/25 12:20
8 | */
9 | class PacketEntitySpawn(entityId: Int, val position: Position, val type: Boolean = true) : PacketEntity(entityId)
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/api/nms/packet/PacketEntityVelocity.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.api.nms.packet
2 |
3 | import me.arasple.mc.trhologram.api.InstantVector
4 |
5 | class PacketEntityVelocity(entityId: Int, val x: Double, val y: Double, val z: Double): PacketEntity(entityId)
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/api/nms/packet/PacketEquipment.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.api.nms.packet
2 |
3 | import org.bukkit.inventory.EquipmentSlot
4 | import org.bukkit.inventory.ItemStack
5 |
6 | /**
7 | * @author Arasple
8 | * @date 2020/12/4 21:22
9 | */
10 | class PacketEquipment(entityId: Int, val slot: EquipmentSlot, val itemStack: ItemStack) : PacketEntity(entityId)
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/api/nms/packet/PacketHeadRotation.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.api.nms.packet
2 |
3 | /**
4 | * @author Mical
5 | * @date 2021/8/28 23:16
6 | */
7 | class PacketHeadRotation(entityId: Int, val headYaw: Int) : PacketEntity(entityId)
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/api/nms/packet/PacketItemModify.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.api.nms.packet
2 |
3 | import org.bukkit.inventory.ItemStack
4 |
5 | /**
6 | * @author Arasple
7 | * @date 2021/2/10 10:19
8 | */
9 | class PacketItemModify(
10 | entityId: Int,
11 | val isInvisible: Boolean,
12 | val isGlowing: Boolean,
13 | val itemStack: ItemStack
14 | ) : PacketEntity(entityId)
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/module/action/ClickReaction.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.module.action
2 |
3 | import me.arasple.mc.trhologram.api.base.ClickHandler
4 | import me.arasple.mc.trhologram.api.event.HologramInteractEvent.Type
5 | import org.bukkit.entity.Player
6 |
7 | /**
8 | * @author Arasple
9 | * @date 2021/2/12 14:11
10 | */
11 | @JvmInline
12 | value class ClickReaction(private val reactions: Map) : ClickHandler {
13 |
14 | override fun eval(player: Player, type: Type) {
15 | reactions[Type.ALL]?.eval(player)
16 | reactions[type]?.eval(player)
17 | }
18 |
19 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/module/action/Reaction.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.module.action
2 |
3 | import me.arasple.mc.trhologram.api.TrHologramAPI
4 | import org.bukkit.entity.Player
5 | import java.util.concurrent.CompletableFuture
6 |
7 | /**
8 | * @author Arasple
9 | * @date 2021/2/11 16:31
10 | */
11 | @JvmInline
12 | value class Reaction(private val kether: String) {
13 |
14 | constructor(kethers: List) : this(kethers.joinToString("\n"))
15 |
16 | fun eval(player: Player): CompletableFuture {
17 | return TrHologramAPI.eval(player, kether)
18 | }
19 |
20 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/module/command/CommandExecutor.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.module.command
2 |
3 | import taboolib.common.platform.ProxyCommandSender
4 | import taboolib.common.platform.command.SimpleCommandBody
5 | import taboolib.module.chat.colored
6 | import taboolib.module.lang.asLangText
7 |
8 | /**
9 | * @author Mical
10 | * @date 2021/8/17 11:56
11 | */
12 | interface CommandExecutor {
13 |
14 | val command: SimpleCommandBody
15 |
16 | fun description(sender: ProxyCommandSender): String =
17 | sender.asLangText("COMMAND-${name.uppercase()}-DESCRIPTION").colored()
18 |
19 | val name: String
20 |
21 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/module/command/CommandHandler.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.module.command
2 |
3 | import me.arasple.mc.trhologram.module.command.impl.*
4 | import org.bukkit.command.CommandSender
5 | import taboolib.common.platform.command.CommandBody
6 | import taboolib.common.platform.command.CommandHeader
7 | import taboolib.common.platform.command.mainCommand
8 | import taboolib.common.platform.command.subCommand
9 | import taboolib.common.platform.function.adaptCommandSender
10 | import taboolib.common.platform.function.pluginVersion
11 | import taboolib.module.chat.TellrawJson
12 | import taboolib.module.nms.MinecraftVersion
13 | import taboolib.platform.util.asLangText
14 |
15 | /**
16 | * @author Arasple
17 | * @author Score2
18 | * @date 2021/2/11 16:46
19 | */
20 | @CommandHeader(name = "trhologram", aliases = ["hologram", "trhd", "hd"], permission = "trhologram.access")
21 | object CommandHandler {
22 |
23 | val sub = hashMapOf()
24 |
25 | @CommandBody(permission = "trhologram.command.list", optional = true)
26 | val list = CommandList.command
27 |
28 | @CommandBody(permission = "trhologram.command.teleport", optional = true)
29 | val teleport = CommandTeleport.command
30 |
31 | @CommandBody(permission = "trhologram.command.movehere", optional = true)
32 | val movehere = CommandMovehere.command
33 |
34 | @CommandBody(permission = "trhologram.command.create", optional = true)
35 | val create = CommandCreate.command
36 |
37 | @CommandBody(permission = "trhologram.command.delete", optional = true)
38 | val delete = CommandDelete.command
39 |
40 | @CommandBody(permission = "trhologram.command.reload", optional = true)
41 | val reload = CommandReload.command
42 |
43 | @CommandBody(permission = "trhologram.command.mirror", optional = true)
44 | val mirror = CommandMirror.command
45 |
46 | @CommandBody
47 | val help = subCommand {
48 | execute { sender, _, _ ->
49 | generateMainHelper(sender)
50 | }
51 | }
52 |
53 | @CommandBody
54 | val main = mainCommand {
55 | execute { sender, _, argument ->
56 | if (argument.isEmpty()) {
57 | generateMainHelper(sender)
58 | return@execute
59 | }
60 | sender.sendMessage("§8[§2Tr§aHologram§8] §cERROR §3| Args §6$argument §3not found.")
61 | TellrawJson()
62 | .append("§8[§2Tr§aHologram§8] §bINFO §3| Type ").append("§f/trhologram help")
63 | .hoverText("§f/trhologram help §8- §7more help...")
64 | .suggestCommand("/trhologram help")
65 | .append("§3 for help.")
66 | .sendTo(adaptCommandSender(sender))
67 | }
68 | }
69 |
70 | private fun generateMainHelper(sender: CommandSender) {
71 | val proxySender = adaptCommandSender(sender)
72 | proxySender.sendMessage("")
73 | TellrawJson()
74 | .append(" ").append("§2TrHologram")
75 | .hoverText("§7TrHologram is modern and advanced Hologram-Plugin")
76 | .append(" ").append("§f${pluginVersion}")
77 | .hoverText(
78 | """
79 | §7Plugin version: §2${pluginVersion}
80 | §7NMS version: §b${MinecraftVersion.minecraftVersion}
81 | """.trimIndent()
82 | ).sendTo(proxySender)
83 | proxySender.sendMessage("")
84 | TellrawJson()
85 | .append(" §7${sender.asLangText("Command-Help-Type")}: ").append("§f/trhologram §8[...]")
86 | .hoverText("§f/trhologram §8[...]")
87 | .suggestCommand("/trhologram ")
88 | .sendTo(proxySender)
89 | proxySender.sendMessage(" §7${sender.asLangText("Command-Help-Args")}:")
90 |
91 | fun displayArg(name: String, desc: String) {
92 | TellrawJson()
93 | .append(" §8- ").append("§f$name")
94 | .hoverText("§f/trhologram $name §8- §7$desc")
95 | .suggestCommand("/trhologram $name ")
96 | .sendTo(proxySender)
97 | proxySender.sendMessage(" §7$desc")
98 | }
99 | sub.forEach { (name, executor) -> displayArg(name, executor.description(proxySender)) }
100 | proxySender.sendMessage("")
101 | }
102 |
103 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/module/command/impl/CommandCreate.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.module.command.impl
2 |
3 | import me.arasple.mc.trhologram.module.command.CommandExecutor
4 | import me.arasple.mc.trhologram.module.command.CommandHandler
5 | import me.arasple.mc.trhologram.module.conf.HologramLoader
6 | import me.arasple.mc.trhologram.module.display.Hologram
7 | import org.bukkit.entity.Player
8 | import taboolib.common.platform.command.subCommand
9 | import taboolib.platform.util.sendLang
10 |
11 | /**
12 | * @author Arasple
13 | * @date 2021/2/12 14:52
14 | */
15 | object CommandCreate : CommandExecutor {
16 |
17 | override val command = subCommand {
18 | dynamic {
19 | suggestion(uncheck = true) { _, _ ->
20 | Hologram.holograms.map { it.id }
21 | }
22 | execute { sender, _, argument ->
23 | val args = argument.split(" ")
24 | commandCreate(sender, args[0])
25 | }
26 | }
27 | }
28 |
29 | override val name: String = "create"
30 |
31 | init {
32 | CommandHandler.sub[name] = this
33 | }
34 |
35 | private fun commandCreate(sender: Player, name: String) {
36 | val hologram = Hologram.findHologram { it.id.equals(name, true) }
37 |
38 | if (hologram != null) {
39 | sender.sendLang("Command-Existed")
40 | return
41 | }
42 |
43 | HologramLoader.create(name, sender.location.clone().add(0.0, 2.0, 0.0)).refreshVisibility(sender)
44 | sender.sendLang("Command-Created")
45 | }
46 |
47 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/module/command/impl/CommandDelete.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.module.command.impl
2 |
3 | import me.arasple.mc.trhologram.module.command.CommandExecutor
4 | import me.arasple.mc.trhologram.module.command.CommandHandler
5 | import me.arasple.mc.trhologram.module.display.Hologram
6 | import org.bukkit.entity.Player
7 | import taboolib.common.platform.command.subCommand
8 | import taboolib.platform.util.sendLang
9 | import java.io.File
10 |
11 | /**
12 | * @author Arasple
13 | * @date 2021/2/12 17:41
14 | */
15 | object CommandDelete : CommandExecutor {
16 |
17 | override val command = subCommand {
18 | dynamic {
19 | suggestion(uncheck = true) { _, _ ->
20 | Hologram.holograms.map { it.id }
21 | }
22 | execute { sender, _, argument ->
23 | val args = argument.split(" ")
24 | commandDelete(sender, args[0])
25 | }
26 | }
27 | }
28 |
29 | override val name: String = "delete"
30 |
31 | init {
32 | CommandHandler.sub[name] = this
33 | }
34 |
35 | private fun commandDelete(sender: Player, name: String) {
36 | val hologram = Hologram.findHologram { it.id.equals(name, true) }
37 |
38 | if (hologram == null) {
39 | sender.sendLang("Command-Not-Exists", name)
40 | return
41 | }
42 | hologram.loadedPath?.let { File(it).delete() }
43 | hologram.destroy()
44 | Hologram.holograms.remove(hologram)
45 | sender.sendLang("Command-Deleted", name)
46 | }
47 |
48 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/module/command/impl/CommandList.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.module.command.impl
2 |
3 | import me.arasple.mc.trhologram.module.command.CommandExecutor
4 | import me.arasple.mc.trhologram.module.command.CommandHandler
5 | import me.arasple.mc.trhologram.module.display.Hologram
6 | import taboolib.common.platform.ProxyCommandSender
7 | import taboolib.common.platform.command.subCommand
8 | import taboolib.module.lang.sendLang
9 |
10 | /**
11 | * @author Arasple
12 | * @date 2021/2/12 17:59
13 | */
14 | object CommandList : CommandExecutor {
15 |
16 | override val command = subCommand {
17 | dynamic(optional = true) {
18 | execute { sender, _, argument ->
19 | commandList(sender, argument)
20 | }
21 | }
22 | execute { sender, _, _ ->
23 | commandList(sender, null)
24 | }
25 | }
26 |
27 | override val name: String = "list"
28 |
29 | init {
30 | CommandHandler.sub[name] = this
31 | }
32 |
33 | private fun commandList(sender: ProxyCommandSender, filter: String?) {
34 | val holograms = Hologram.holograms.filter { filter == null || it.id.contains(filter, true) }.sortedBy { it.id }
35 |
36 | if (holograms.isEmpty()) {
37 | sender.sendLang("Command-List-Error", filter ?: "*")
38 | } else {
39 | sender.sendLang("Command-List-Header", holograms.size, filter ?: "*")
40 |
41 | holograms.forEach {
42 | sender.sendLang(
43 | "Command-List-Format",
44 | it.id,
45 | it.components.size
46 | )
47 | }
48 | }
49 | }
50 |
51 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/module/command/impl/CommandMirror.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.module.command.impl
2 |
3 | import me.arasple.mc.trhologram.module.command.CommandExecutor
4 | import me.arasple.mc.trhologram.module.command.CommandHandler
5 | import org.bukkit.Bukkit
6 | import taboolib.common.platform.ProxyCommandSender
7 | import taboolib.common.platform.command.subCommand
8 | import taboolib.common.platform.function.submit
9 | import taboolib.common5.Mirror
10 |
11 | /**
12 | * @author Arasple
13 | * @date 2021/2/12 18:45
14 | */
15 | object CommandMirror : CommandExecutor {
16 |
17 | override val command = subCommand {
18 | execute { sender, _, _ ->
19 | commandMirror(sender)
20 | }
21 | }
22 |
23 | override val name: String = "mirror"
24 |
25 | init {
26 | CommandHandler.sub[name] = this
27 | }
28 |
29 | private fun commandMirror(sender: ProxyCommandSender) {
30 | submit(async = !Bukkit.isPrimaryThread()) {
31 | Mirror.report(sender) {
32 | childFormat = "§8 {0}§7{1} §2[{3} ms] §7{4}%"
33 | parentFormat = "§8 §8{0}§7{1} §8[{3} ms] §7{4}%"
34 | }.run {
35 | sender.sendMessage("\n§2§lHologram §a§l§nPerformance Mirror\n§r")
36 | }
37 | }
38 | }
39 |
40 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/module/command/impl/CommandMovehere.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.module.command.impl
2 |
3 | import me.arasple.mc.trhologram.module.command.CommandExecutor
4 | import me.arasple.mc.trhologram.module.command.CommandHandler
5 | import me.arasple.mc.trhologram.module.display.Hologram
6 | import me.arasple.mc.trhologram.module.editor.Editor
7 | import me.arasple.mc.trhologram.util.parseString
8 | import org.bukkit.entity.Player
9 | import taboolib.common.platform.command.subCommand
10 | import taboolib.platform.util.sendLang
11 |
12 | /**
13 | * @author Arasple
14 | * @date 2021/2/12 20:45
15 | */
16 | object CommandMovehere : CommandExecutor {
17 |
18 | override val command = subCommand {
19 | dynamic {
20 | suggestion { _, _ ->
21 | Hologram.holograms.map { it.id }
22 | }
23 | execute { sender, _, argument ->
24 | val args = argument.split(" ")
25 | commandMoveHere(sender, args[0])
26 | }
27 | }
28 | }
29 |
30 | override val name: String = "movehere"
31 |
32 | init {
33 | CommandHandler.sub[name] = this
34 | }
35 |
36 | private fun commandMoveHere(sender: Player, name: String) {
37 | val hologram = Hologram.findHologram { it.id.equals(name, true) }
38 |
39 | if (hologram == null) {
40 | sender.sendLang("Command-Not-Exists", name)
41 | return
42 | }
43 |
44 | Editor.modify(hologram) {
45 | it["Location"] = sender.location.parseString()
46 | }
47 | }
48 |
49 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/module/command/impl/CommandReload.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.module.command.impl
2 |
3 | import me.arasple.mc.trhologram.api.Settings
4 | import me.arasple.mc.trhologram.module.command.CommandExecutor
5 | import me.arasple.mc.trhologram.module.command.CommandHandler
6 | import me.arasple.mc.trhologram.module.conf.HologramLoader
7 | import me.arasple.mc.trhologram.module.display.Hologram
8 | import org.bukkit.Bukkit
9 | import org.bukkit.command.CommandSender
10 | import taboolib.common.platform.command.subCommand
11 | import taboolib.platform.util.sendLang
12 | import java.io.File
13 |
14 | /**
15 | * @author Arasple
16 | * @date 2021/2/12 17:43
17 | */
18 | object CommandReload : CommandExecutor {
19 |
20 | override val command = subCommand {
21 | dynamic(optional = true) {
22 | suggestion { _, _ ->
23 | Hologram.holograms.map { it.id }
24 | }
25 | execute { sender, _, argument ->
26 | val args = argument.split(" ")
27 | commandReload(sender, args[0])
28 | }
29 | }
30 | execute { sender, _, _ ->
31 | commandReload(sender, null)
32 | }
33 | }
34 |
35 | override val name: String = "reload"
36 |
37 | init {
38 | CommandHandler.sub[name] = this
39 | }
40 |
41 | private fun commandReload(sender: CommandSender, name: String?) {
42 | Settings.onSettingsReload()
43 | val hologram = Hologram.findHologram { it.id.equals(name, true) }
44 |
45 | if (hologram != null) {
46 | hologram.destroy()
47 | Hologram.holograms.remove(hologram)
48 |
49 | hologram.loadedPath?.let {
50 | HologramLoader.load(File(it))
51 | sender.sendLang("Command-Reload", hologram.id)
52 | }
53 | } else {
54 | HologramLoader.load(sender)
55 | Bukkit.getOnlinePlayers().forEach(Hologram::refreshAll)
56 | }
57 | }
58 |
59 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/module/command/impl/CommandTeleport.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.module.command.impl
2 |
3 | import me.arasple.mc.trhologram.module.command.CommandExecutor
4 | import me.arasple.mc.trhologram.module.command.CommandHandler
5 | import me.arasple.mc.trhologram.module.display.Hologram
6 | import org.bukkit.entity.Player
7 | import taboolib.common.platform.command.subCommand
8 | import taboolib.platform.util.sendLang
9 |
10 | /**
11 | * @author Arasple
12 | * @date 2021/2/13 10:43
13 | */
14 | object CommandTeleport : CommandExecutor {
15 |
16 | override val command = subCommand {
17 | dynamic {
18 | suggestion { _, _ ->
19 | Hologram.holograms.map { it.id }
20 | }
21 | execute { sender, _, argument ->
22 | val args = argument.split(" ")
23 | commandTeleport(sender, args[0])
24 | }
25 | }
26 | }
27 |
28 | override val name: String = "teleport"
29 |
30 | init {
31 | CommandHandler.sub[name] = this
32 | }
33 |
34 | private fun commandTeleport(sender: Player, name: String) {
35 | val hologram = Hologram.findHologram { it.id.equals(name, true) }
36 |
37 | if (hologram == null) {
38 | sender.sendLang("Command-Not-Exists", name)
39 | return
40 | }
41 |
42 | sender.teleport(hologram.position.toLocation())
43 | }
44 |
45 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/module/condition/Condition.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.module.condition
2 |
3 | import me.arasple.mc.trhologram.api.TrHologramAPI
4 | import me.arasple.mc.trhologram.api.base.BaseCondition
5 | import org.bukkit.entity.Player
6 | import taboolib.common5.Coerce
7 | import java.util.concurrent.CompletableFuture
8 |
9 | /**
10 | * @author Arasple
11 | * @date 2021/2/10 11:02
12 | */
13 | @JvmInline
14 | value class Condition(private val expression: String) : BaseCondition {
15 |
16 | override fun eval(player: Player): CompletableFuture {
17 | return if (expression.isEmpty()) CompletableFuture.completedFuture(true)
18 | else eval(player, expression)
19 | }
20 |
21 | override fun toString(): String {
22 | return expression
23 | }
24 |
25 | companion object {
26 |
27 | fun eval(player: Player, script: String): CompletableFuture {
28 | return TrHologramAPI.eval(player, script).thenApply {
29 | Coerce.toBoolean(it)
30 | }
31 | }
32 |
33 | }
34 |
35 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/module/conf/HologramLoader.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.module.conf
2 |
3 | import me.arasple.mc.trhologram.TrHologram
4 | import me.arasple.mc.trhologram.api.Position
5 | import me.arasple.mc.trhologram.api.Settings
6 | import me.arasple.mc.trhologram.api.TrHologramAPI
7 | import me.arasple.mc.trhologram.api.event.HologramInteractEvent.Type
8 | import me.arasple.mc.trhologram.api.hologram.HologramComponent
9 | import me.arasple.mc.trhologram.api.hologram.ItemHologram
10 | import me.arasple.mc.trhologram.api.hologram.TextHologram
11 | import me.arasple.mc.trhologram.module.action.ClickReaction
12 | import me.arasple.mc.trhologram.module.action.Reaction
13 | import me.arasple.mc.trhologram.module.condition.Condition
14 | import me.arasple.mc.trhologram.module.display.Hologram
15 | import me.arasple.mc.trhologram.module.display.texture.Texture
16 | import me.arasple.mc.trhologram.module.display.texture.TrMenuTexture
17 | import me.arasple.mc.trhologram.module.hook.HookPlugin
18 | import me.arasple.mc.trhologram.util.parseLocation
19 | import me.arasple.mc.trhologram.util.parseString
20 | import org.bukkit.Location
21 | import org.bukkit.command.CommandSender
22 | import org.bukkit.configuration.file.YamlConfiguration
23 | import taboolib.common.io.newFile
24 | import taboolib.common.platform.function.releaseResourceFile
25 | import taboolib.platform.util.sendLang
26 | import java.io.File
27 | import java.nio.charset.StandardCharsets
28 | import kotlin.system.measureNanoTime
29 |
30 | /**
31 | * @author Arasple
32 | * @date 2021/2/11 10:03
33 | */
34 | object HologramLoader {
35 |
36 | private val folder by lazy {
37 | Hologram.clear()
38 | val folder = File(TrHologram.plugin.dataFolder, "holograms")
39 |
40 | if (!folder.exists()) {
41 | releaseResourceFile("holograms/Demo.yml", true)
42 | }
43 |
44 | folder
45 | }
46 |
47 | fun create(id: String, location: Location): Hologram {
48 | val hologram =
49 | """
50 | Location: ${location.parseString()}
51 |
52 | Contents:
53 | - '&7Hello, &2Tr&aHologram&7!'
54 | - '{item: emerald}'
55 | - '&3Nice to meet you, &a{{player name}}'
56 | - '&3Author: &aArasple{offset=0.35}'
57 |
58 | Actions:
59 | All: 'tell color *"&bHi, you just clicked this hologram"'
60 | """.trimIndent()
61 |
62 | newFile(folder, "$id.yml").also {
63 | it.writeText(hologram, StandardCharsets.UTF_8)
64 | return load(it)
65 | }
66 | }
67 |
68 | fun load(sender: CommandSender) {
69 | measureNanoTime { load() }.div(1000000.0).let {
70 | sender.sendLang("Hologram-Loaded", Hologram.holograms.size, it)
71 | }
72 | }
73 |
74 | fun load(): Int {
75 | Hologram.clear()
76 | TrHologramAPI.resetIndex()
77 |
78 | filterHologramFiles(folder).forEach { load(it) }
79 | Settings.INSTANCE.loadPaths.flatMap { filterHologramFiles(File(it)) }.forEach { load(it) }
80 |
81 | return Hologram.holograms.size
82 | }
83 |
84 | fun load(file: File): Hologram {
85 | val id = file.nameWithoutExtension
86 | val conf = YamlConfiguration.loadConfiguration(file)
87 | val location = conf.getString("Location")?.parseLocation() ?: throw Exception("No valid location")
88 | val lineSpacing = conf.getDouble("Options.Line-Spacing", Settings.INSTANCE.lineSpacing)
89 | val viewDistance =
90 | conf.getDouble("Options.View-Distance", Settings.INSTANCE.viewDistance).coerceAtMost(50.0)
91 | val viewCondition = conf.getString("Options.View-Condition", Settings.INSTANCE.viewCondition).let {
92 | if (it == null || it.isBlank()) null
93 | else Condition(it)
94 | }
95 | val refreshCondition = conf.getLong("Options.Refresh-Condition", Settings.INSTANCE.refershCondition)
96 | val contents = conf.getStringList("Contents").ifEmpty { listOf("TrHologram") }
97 | val actions = mutableMapOf()
98 |
99 | val all = conf.get("Actions.All").toStringList()
100 | val left = conf.get("Actions.Left").toStringList()
101 | val shiftLeft = conf.get("Actions.Shift_Left").toStringList()
102 | val right = conf.get("Actions.Right").toStringList()
103 | val shiftRight = conf.get("Actions.Shift_Right").toStringList()
104 |
105 | if (all.isNotEmpty()) actions[Type.ALL] = Reaction(all)
106 | if (left.isNotEmpty()) actions[Type.LEFT] = Reaction(left)
107 | if (shiftLeft.isNotEmpty()) actions[Type.SHIFT_LEFT] = Reaction(shiftLeft)
108 | if (right.isNotEmpty()) actions[Type.RIGHT] = Reaction(right)
109 | if (shiftRight.isNotEmpty()) actions[Type.SHIFT_RIGHT] = Reaction(shiftRight)
110 |
111 | val holograms = mutableListOf()
112 | var position = Position.fromLocation(location).clone(y = -1.25)
113 |
114 | contents.forEach {
115 | val (line, options) = Property.from(it)
116 | val itemDisplay = options[Property.ITEM]
117 | val update = options[Property.UPDATE]?.toLongOrNull() ?: -1
118 | val offset = options[Property.OFFSET]?.toDoubleOrNull() ?: lineSpacing
119 | val isItem = itemDisplay != null
120 | val fix = if (isItem) 0.5 else 0.0
121 |
122 | position = position.clone(y = -(fix + offset))
123 |
124 | val hologram = when {
125 | isItem ->{
126 | val texture = if (!HookPlugin.getTrMenu().isHooked) Texture.createTexture(itemDisplay!!) else TrMenuTexture(itemDisplay!!)
127 | ItemHologram(texture, position, update)
128 | }
129 | line.isBlank() -> null
130 | else -> TextHologram(line, position, update)
131 | }
132 |
133 | if (hologram != null) holograms.add(hologram)
134 | }
135 |
136 | // Loaded & Add
137 | val hologram = Hologram(
138 | id,
139 | Position.fromLocation(location),
140 | viewDistance,
141 | viewCondition,
142 | refreshCondition,
143 | holograms,
144 | ClickReaction(actions),
145 | file.absolutePath
146 | )
147 | Hologram.holograms.add(hologram)
148 | return hologram
149 | }
150 |
151 | private fun filterHologramFiles(file: File): List {
152 | return mutableListOf().run {
153 | if (file.isDirectory) {
154 | file.listFiles()?.forEach {
155 | addAll(filterHologramFiles(it))
156 | }
157 | } else if (!file.name.startsWith("#") && file.extension.equals("yml", true)) {
158 | add(file)
159 | }
160 | this
161 | }
162 | }
163 |
164 | @Suppress("UNCHECKED_CAST")
165 | private fun Any?.toStringList(): List {
166 | return when (this) {
167 | is List<*> -> this as List
168 | else -> listOf(toString())
169 | }
170 | }
171 |
172 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/module/conf/Property.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.module.conf
2 |
3 | /**
4 | * @author Arasple
5 | * @date 2021/2/11 17:26
6 | */
7 | enum class Property(val regex: Regex, val group: Int) {
8 |
9 | OFFSET("\\{offset?[:=] ?([0-9.]+)}"),
10 |
11 | UPDATE("\\{(update|refresh)?[:=] ?([0-9]+)}", 2),
12 |
13 | ITEM("\\{items?[:=] ?(.+)}");
14 |
15 | constructor(regex: String, group: Int = 1) : this("(?i)$regex".toRegex(), group)
16 |
17 | companion object {
18 |
19 | fun from(string: String): Pair> {
20 | var content = string
21 | val map = values().mapNotNull {
22 | val value = it.regex.find(content)?.groupValues?.get(it.group)
23 | value?.let { v ->
24 | content = content.replace(it.regex, "")
25 | it to v
26 | }
27 | }.toMap()
28 |
29 | return content to map
30 | }
31 |
32 | }
33 |
34 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/module/display/Hologram.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.module.display
2 |
3 | import me.arasple.mc.trhologram.api.Position
4 | import me.arasple.mc.trhologram.api.TrHologramAPI
5 | import me.arasple.mc.trhologram.api.base.BaseCondition
6 | import me.arasple.mc.trhologram.api.base.ClickHandler
7 | import me.arasple.mc.trhologram.api.hologram.HologramBuilder
8 | import me.arasple.mc.trhologram.api.hologram.HologramComponent
9 | import me.arasple.mc.trhologram.api.hologram.ItemHologram
10 | import me.arasple.mc.trhologram.api.hologram.TextHologram
11 | import org.bukkit.Bukkit
12 | import org.bukkit.entity.Player
13 | import taboolib.common.platform.function.submit
14 | import taboolib.common.platform.service.PlatformExecutor
15 | import taboolib.common5.mirrorNow
16 |
17 | /**
18 | * @author Arasple
19 | * @date 2021/2/11 10:06
20 | */
21 | class Hologram(
22 | val id: String,
23 | val position: Position,
24 | val viewDistance: Double = 0.0,
25 | val viewCondition: BaseCondition? = null,
26 | val refreshCondition: Long = -1,
27 | val components: List,
28 | val reactions: ClickHandler,
29 | val loadedPath: String? = null
30 | ) {
31 |
32 | companion object {
33 |
34 | internal val externalHolograms = mutableSetOf()
35 | internal val holograms = mutableSetOf()
36 |
37 | fun findHologram(predicate: (Hologram) -> Boolean): Hologram? {
38 | return holograms.find(predicate) ?: externalHolograms.find(predicate)
39 | }
40 |
41 | fun refreshAll(player: Player) {
42 | mirrorNow("Hologram:Event:Refresh") {
43 | externalHolograms.filter { it.sameWorld(player) }.forEach {
44 | it.refreshVisibility(player)
45 | }
46 | holograms.filter { it.sameWorld(player) }.forEach {
47 | it.refreshVisibility(player)
48 | }
49 | }
50 | }
51 |
52 | fun clear() {
53 | holograms.removeIf {
54 | it.destroy()
55 | true
56 | }
57 | }
58 |
59 | fun destroyAll(player: Player) {
60 | holograms.forEach {
61 | it.destroy(player)
62 | }
63 | }
64 |
65 | }
66 |
67 | private val visibleByCondition = mutableMapOf()
68 | private val viewers = mutableSetOf()
69 | private var refreshTask: PlatformExecutor.PlatformTask? = null
70 |
71 | init {
72 | deployment()
73 | }
74 |
75 | private fun deployment() {
76 | refreshTask?.cancel()
77 | if (refreshCondition > 0) refreshTask =
78 | submit(delay = refreshCondition, period = refreshCondition, async = true) {
79 | viewers.removeIf {
80 | val player = Bukkit.getPlayerExact(it)
81 | player == null || !player.isOnline
82 | }
83 | Bukkit.getOnlinePlayers().filter { visibleByDistance(it) }.forEach {
84 | refreshCondition(it)
85 | refreshVisibility(it)
86 | }
87 | }
88 | }
89 |
90 | fun refreshVisibility(player: Player) {
91 | visible(player, visibleByDistance(player) && visibleByCondition(player))
92 | }
93 |
94 | fun sameWorld(player: Player): Boolean {
95 | return position.world == player.world
96 | }
97 |
98 | fun getTextLine(index: Int): TextHologram {
99 | return components[index] as TextHologram
100 | }
101 |
102 | fun getItemLine(index: Int): ItemHologram {
103 | return components[index] as ItemHologram
104 | }
105 |
106 | private fun visible(player: Player, visible: Boolean) {
107 | if (visible) {
108 | if (viewers.add(player.name)) components.forEach { it.spawn(player) }
109 | } else {
110 | if (viewers.remove(player.name)) components.forEach { it.destroy(player) }
111 | }
112 | }
113 |
114 | private fun visibleByDistance(player: Player): Boolean {
115 | return player.world == position.world && position.distance(player.location) <= viewDistance
116 | }
117 |
118 | private fun visibleByCondition(player: Player): Boolean {
119 | return if (visibleByCondition.containsKey(player.name)) {
120 | visibleByCondition[player.name] ?: true
121 | } else {
122 | refreshCondition(player)
123 | true
124 | }
125 | }
126 |
127 | private fun refreshCondition(player: Player) {
128 | viewCondition?.eval(player)?.thenApply {
129 | visibleByCondition[player.name] = it
130 | }
131 | }
132 |
133 | fun forViewers(viewer: (Player) -> Unit) {
134 | viewers.mapNotNull { Bukkit.getPlayerExact(it) }.forEach(viewer)
135 | }
136 |
137 | fun destroy(player: Player) {
138 | viewers.remove(player.name)
139 | components.forEach {
140 | it.viewers.remove(player.name)
141 | }
142 | }
143 |
144 | fun destroy() {
145 | refreshTask?.cancel()
146 | components.forEach(HologramComponent::destroy)
147 | externalHolograms.remove(this)
148 | }
149 |
150 | /**
151 | * WARNING: This will make all custom interspace invalid
152 | */
153 | fun rebuild(): HologramBuilder {
154 | destroy()
155 |
156 | return TrHologramAPI.builder(position.toLocation()).run {
157 | viewDistance(viewDistance)
158 | refreshCondition(refreshCondition)
159 | click(reactions)
160 |
161 | viewCondition?.let { viewCondition(it) }
162 | components.forEach {
163 | when (it) {
164 | is TextHologram -> append(it.text, it.period0, onTick = it.onTick)
165 | is ItemHologram -> append(it.display, it.period0, onTick = it.onTick)
166 | }
167 | }
168 |
169 | this
170 | }
171 | }
172 |
173 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/module/display/texture/Texture.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.module.display.texture
2 |
3 | import me.arasple.mc.trhologram.api.base.ItemTexture
4 | import me.arasple.mc.trhologram.util.Heads
5 | import me.arasple.mc.trhologram.util.ItemHelper
6 | import me.arasple.mc.trhologram.util.containsPlaceholder
7 | import me.arasple.mc.trhologram.util.parseString
8 | import org.bukkit.Color
9 | import org.bukkit.Material
10 | import org.bukkit.entity.Player
11 | import org.bukkit.inventory.ItemStack
12 | import org.bukkit.inventory.meta.LeatherArmorMeta
13 | import org.bukkit.inventory.meta.SkullMeta
14 | import taboolib.common.util.Strings.similarDegree
15 | import taboolib.library.xseries.XMaterial
16 | import taboolib.module.nms.MinecraftVersion
17 | import taboolib.platform.util.ItemBuilder
18 | import taboolib.platform.util.buildItem
19 | import kotlin.math.min
20 |
21 | /**
22 | * @author Arasple
23 | * @date 2021/1/24 11:50
24 | */
25 | class Texture(
26 | val raw: String,
27 | val type: TextureType,
28 | val texture: String,
29 | val dynamic: Boolean,
30 | val static: ItemStack?,
31 | val meta: Map
32 | ) : ItemTexture {
33 |
34 | override fun generate(player: Player): ItemStack {
35 | if (static != null) return static
36 | val temp = if (dynamic) player.parseString(texture) else texture
37 |
38 | val itemStack = when (type) {
39 | TextureType.NORMAL -> parseMaterial(temp)
40 | TextureType.HEAD -> Heads.getHead(temp)
41 | TextureType.RAW -> ItemHelper.fromJson(temp)
42 | }
43 |
44 | if (itemStack != null) {
45 | val itemMeta = itemStack.itemMeta
46 | meta.forEach { (meta, metaValue) ->
47 | val value = player.parseString(metaValue)
48 | when (meta) {
49 | TextureMeta.DATA_VALUE -> itemStack.durability = value.toShortOrNull() ?: 0
50 | TextureMeta.MODEL_DATA -> {
51 | itemMeta?.setCustomModelData(value.toInt()).also { itemStack.itemMeta = itemMeta }
52 | }
53 | TextureMeta.LEATHER_DYE -> if (itemMeta is LeatherArmorMeta) {
54 | itemMeta.setColor(serializeColor(value)).also { itemStack.itemMeta = itemMeta }
55 | }
56 | }
57 | }
58 | }
59 |
60 | return itemStack ?: FALL_BACK
61 | }
62 |
63 | companion object {
64 |
65 | val FALL_BACK = ItemStack(Material.BEDROCK)
66 |
67 | fun createTexture(itemStack: ItemStack): Texture {
68 | val material = itemStack.type.name.lowercase().replace("_", " ")
69 | val itemMeta = itemStack.itemMeta
70 |
71 | // Head Meta
72 | val texture = let {
73 | if (itemMeta is SkullMeta) {
74 | return@let if (itemMeta.hasOwner()) "head:${itemMeta.owningPlayer?.name}"
75 | else "head:${Heads.seekTexture(itemStack)}"
76 | }
77 | // Model Data
78 | if (MinecraftVersion.majorLegacy >= 11400 && itemMeta != null && itemMeta.hasCustomModelData()) {
79 | return@let "$material{model-data:${itemMeta.customModelData}}"
80 | }
81 | // Leather
82 | if (itemMeta is LeatherArmorMeta) {
83 | return@let "$material{dye:${deserializeColor(itemMeta.color)}}"
84 | }
85 | return@let material
86 | }
87 |
88 | return createTexture(texture)
89 | }
90 |
91 |
92 | fun createTexture(raw: String): Texture {
93 | var type = TextureType.NORMAL
94 | var static: ItemStack? = null
95 | var texture = raw
96 | val meta = mutableMapOf()
97 |
98 | TextureMeta.values().forEach {
99 | it.regex.find(raw)?.groupValues?.get(1)?.also { value ->
100 | meta[it] = value
101 | texture = texture.replace(it.regex, "")
102 | }
103 | }
104 |
105 | TextureType.values().filter { it.group != -1 }.forEach {
106 | it.regex.find(texture)?.groupValues?.get(it.group)?.also { value ->
107 | type = it
108 | texture = value
109 | }
110 | }
111 |
112 | val dynamic = texture.containsPlaceholder()
113 | if (type == TextureType.NORMAL) {
114 | if (texture.startsWith("{")) {
115 | type = TextureType.RAW
116 | if (!dynamic) static = ItemHelper.fromJson(texture)!!
117 | }
118 | }
119 | return Texture(raw, type, texture, dynamic, static, meta)
120 | }
121 |
122 | private fun parseMaterial(material: String): ItemStack {
123 | val split = material.split(":", limit = 2)
124 | val data = split.getOrNull(1)?.toIntOrNull() ?: 0
125 | val id = split[0].toIntOrNull() ?: split[0].uppercase().replace("[ _]".toRegex(), "_")
126 |
127 | return buildItem(XMaterial.matchXMaterial(FALL_BACK)) {
128 | var rawMaterial = id
129 |
130 | if (id is Int) {
131 | XMaterial.matchXMaterial(id, 0).let {
132 | if (it.isPresent) {
133 | this.material = it.get().parseMaterial()!!
134 | this.damage = data
135 | } else {
136 | XMaterial.STONE
137 | }
138 | }
139 |
140 | } else {
141 | val name = id.toString()
142 | try {
143 | this.material = XMaterial.valueOf(name).parseMaterial()!!
144 | } catch (e: Throwable) {
145 | val xMaterial =
146 | XMaterial.values().find { it.name.equals(name, true) }
147 | ?: XMaterial.values()
148 | .find { it -> it.legacy.any { it == name } }
149 | ?: XMaterial.values()
150 | .maxByOrNull { similarDegree(name, it.name) }
151 | xMaterial?.parseItem() ?: FALL_BACK
152 | }
153 | }
154 | }
155 | }
156 |
157 | fun serializeColor(color: String): Color {
158 | val rgb = color.split(",").toTypedArray()
159 | if (rgb.size == 3) {
160 | val r = min(rgb[0].toIntOrNull() ?: 0, 255)
161 | val g = min(rgb[1].toIntOrNull() ?: 0, 255)
162 | val b = min(rgb[2].toIntOrNull() ?: 0, 255)
163 | return Color.fromRGB(r, g, b)
164 | }
165 | return Color.BLACK
166 | }
167 |
168 | fun deserializeColor(color: Color): String {
169 | return "${color.red},${color.green},${color.blue}"
170 | }
171 |
172 | }
173 |
174 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/module/display/texture/TextureMeta.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.module.display.texture
2 |
3 | /**
4 | * @author Arasple
5 | * @date 2021/1/24 12:15
6 | *
7 | */
8 | enum class TextureMeta(val regex: Regex) {
9 |
10 | DATA_VALUE("(?i)[<{]data-?value[:=](\\d+?)[>}]"),
11 |
12 | MODEL_DATA("(?i)[<{]model-?data[:=](\\d+?)[>}]"),
13 |
14 | LEATHER_DYE("(?i)[<{]dye[:=](\\d{3},\\d{3},\\d{3})[>}]");
15 |
16 | constructor(regex: String) : this(regex.toRegex())
17 |
18 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/module/display/texture/TextureType.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.module.display.texture
2 |
3 | /**
4 | * @author Arasple
5 | * @date 2021/1/24 11:55
6 | */
7 | enum class TextureType(val regex: Regex, val group: Int) {
8 |
9 | /**
10 | * Red Stained Glass Pane
11 | * Wool:3 (Data-Value does not support variables here)
12 | */
13 | NORMAL,
14 |
15 | /**
16 | * {identifier}:{parsedArgument}
17 | *
18 | * e.g.
19 | * head:%player_name%
20 | * head:BlackSky
21 | *
22 | * if the server runs SkinsRestorer and in offline mode, the player head will automatically support
23 | * if argument's length is larger than 64, then identified as custom textued skull
24 | *
25 | * head:eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDRmNDUyZDk5OGVhYmFjNDY0MmM2YjBmZTVhOGY0ZTJlNjczZWRjYWUyYTZkZmQ5ZTZhMmU4NmU3ODZlZGFjMCJ9fX0=
26 | * head:44f452d998eabac4642c6b0fe5a8f4e2e673edcae2a6dfd9e6a2e86e786edac0
27 | */
28 | HEAD("[<{]?(player|custom|textured?)?-?(head|skull)[:=]([\\w.%{}]+)[>}]?", 3),
29 |
30 | /**
31 | * {json Content}
32 | *
33 | * Will be parsed if there contains placeholders
34 | */
35 | RAW;
36 |
37 | constructor(regex: String = "", group: Int = -1) : this(regex.toRegex(), group)
38 |
39 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/module/display/texture/TrMenuTexture.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.module.display.texture
2 |
3 | import me.arasple.mc.trhologram.api.base.ItemTexture
4 | import me.arasple.mc.trmenu.module.display.MenuSession
5 | import org.bukkit.entity.Player
6 | import org.bukkit.inventory.ItemStack
7 |
8 | /**
9 | * @author Arasple
10 | * @date 2021/2/12 21:11
11 | */
12 | class TrMenuTexture(texture: String) : ItemTexture {
13 |
14 | private val texture = me.arasple.mc.trmenu.module.display.texture.Texture.createTexture(texture)
15 |
16 | override fun generate(player: Player): ItemStack {
17 | return texture.generate(MenuSession.getSession(player))
18 | }
19 |
20 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/module/editor/Editor.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.module.editor
2 |
3 | import me.arasple.mc.trhologram.module.conf.HologramLoader
4 | import me.arasple.mc.trhologram.module.display.Hologram
5 | import org.bukkit.Bukkit
6 | import org.bukkit.configuration.file.YamlConfiguration
7 | import java.io.File
8 |
9 | /**
10 | * @author Arasple
11 | * @date 2021/2/12 17:09
12 | */
13 | object Editor {
14 |
15 | fun modify(hologram: Hologram, modify: (YamlConfiguration) -> Unit) {
16 | val file = File(hologram.loadedPath!!)
17 | val conf = YamlConfiguration.loadConfiguration(file)
18 | modify(conf)
19 | conf.save(file)
20 | hologram.destroy()
21 | Hologram.holograms.remove(hologram)
22 | HologramLoader.load(file).run {
23 | Bukkit.getOnlinePlayers().forEach(this::refreshVisibility)
24 | }
25 | }
26 |
27 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/module/hook/HookAbstract.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.module.hook
2 |
3 | import org.bukkit.Bukkit
4 | import org.bukkit.plugin.Plugin
5 | import taboolib.common.platform.function.console
6 | import taboolib.module.lang.sendLang
7 |
8 | /**
9 | * @author Mical
10 | * @date 2021/8/19 21:00
11 | */
12 | abstract class HookAbstract {
13 |
14 | open val name by lazy { getPluginName() }
15 |
16 | val plugin: Plugin? by lazy {
17 | Bukkit.getPluginManager().getPlugin(name)
18 | }
19 |
20 | open val isHooked by lazy {
21 | plugin != null && plugin!!.isEnabled
22 | }
23 |
24 | open fun getPluginName(): String {
25 | return javaClass.simpleName.substring(4)
26 | }
27 |
28 | open fun checkHooked(): Boolean {
29 | return if (isHooked) true else false.also { reportAbuse() }
30 | }
31 |
32 | fun reportAbuse() {
33 | console().sendLang("Plugin-Dependency-Abuse", name)
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/module/hook/HookPlugin.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.module.hook
2 |
3 | import me.arasple.mc.trhologram.module.hook.impl.HookSkinsRestorer
4 | import me.arasple.mc.trhologram.module.hook.impl.HookTrMenu
5 | import taboolib.common.LifeCycle
6 | import taboolib.common.platform.Awake
7 | import taboolib.common.platform.function.console
8 | import taboolib.module.lang.sendLang
9 |
10 | /**
11 | * @author Mical
12 | * @date 2021/8/19 21:00
13 | */
14 | object HookPlugin {
15 |
16 | @Awake(LifeCycle.ENABLE)
17 | fun printInfo() {
18 | registry.filter { it.isHooked }.forEach {
19 | console().sendLang("Plugin-Dependency-Hooked", it.name)
20 | }
21 | }
22 |
23 | private val registry: Array = arrayOf(
24 | HookSkinsRestorer(),
25 | HookTrMenu()
26 | )
27 |
28 | fun getSkinsRestorer(): HookSkinsRestorer {
29 | return registry[0] as HookSkinsRestorer
30 | }
31 |
32 | fun getTrMenu(): HookTrMenu {
33 | return registry[1] as HookTrMenu
34 | }
35 |
36 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/module/hook/impl/HookSkinsRestorer.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.module.hook.impl
2 |
3 | import com.mojang.authlib.properties.Property
4 | import me.arasple.mc.trhologram.module.hook.HookAbstract
5 | import net.skinsrestorer.api.SkinsRestorerAPI
6 | import org.bukkit.Bukkit
7 |
8 | /**
9 | * @author Mical
10 | * @date 2021/8/19 20:52
11 | */
12 | class HookSkinsRestorer : HookAbstract() {
13 |
14 | private val skinsRestorerAPI: SkinsRestorerAPI? =
15 | if (isHooked) {
16 | SkinsRestorerAPI.getApi()
17 | } else {
18 | null
19 | }
20 | get() {
21 | if (field == null) reportAbuse()
22 | return field
23 | }
24 |
25 | fun getPlayerSkinTexture(name: String): String? {
26 | skinsRestorerAPI?.let {
27 | val uuid = Bukkit.getPlayerExact(name)?.uniqueId?.toString() ?: return null
28 | if (it.getProfile(uuid) == null) {
29 | return null
30 | }
31 |
32 | val skinData = it.getSkinData(name)
33 | return (skinData as Property).value
34 | }
35 | return null
36 | }
37 |
38 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/module/hook/impl/HookTrMenu.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.module.hook.impl
2 |
3 | import me.arasple.mc.trhologram.module.hook.HookAbstract
4 |
5 | /**
6 | * @author Mical
7 | * @date 2021/8/19 21:05
8 | */
9 | class HookTrMenu : HookAbstract() {
10 |
11 | override val isHooked: Boolean
12 | get() = super.isHooked && plugin!!.description.version.startsWith("3")
13 |
14 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/module/listener/ListenerHologramInteract.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.module.listener
2 |
3 | import me.arasple.mc.trhologram.api.Settings
4 | import me.arasple.mc.trhologram.api.event.HologramInteractEvent
5 | import taboolib.common.platform.event.EventPriority
6 | import taboolib.common.platform.event.SubscribeEvent
7 | import taboolib.common.platform.function.submit
8 |
9 | /**
10 | * @author Arasple
11 | * @date 2021/2/11 16:41
12 | */
13 | object ListenerHologramInteract {
14 |
15 | @SubscribeEvent(priority = EventPriority.HIGHEST, ignoreCancelled = true)
16 | fun onInteract(e: HologramInteractEvent) {
17 | val player = e.player
18 |
19 | if (Settings.INSTANCE.interactDelay.hasNext(player.name)) {
20 | submit(delay = 1L, async = false) {
21 | e.hologram.reactions.eval(player, e.type)
22 | }
23 | }
24 | }
25 |
26 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/module/listener/ListenerJoin.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.module.listener
2 |
3 | import me.arasple.mc.trhologram.module.display.Hologram
4 | import org.bukkit.event.player.PlayerJoinEvent
5 | import taboolib.common.platform.event.SubscribeEvent
6 |
7 | /**
8 | * @author Arasple
9 | * @date 2021/2/12 13:55
10 | */
11 | object ListenerJoin {
12 |
13 | @SubscribeEvent
14 | fun onJoin(e: PlayerJoinEvent) {
15 | Hologram.refreshAll(e.player)
16 | }
17 |
18 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/module/listener/ListenerMovement.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.module.listener
2 |
3 | import me.arasple.mc.trhologram.module.display.Hologram
4 | import org.bukkit.event.player.PlayerMoveEvent
5 | import taboolib.common.platform.event.SubscribeEvent
6 | import taboolib.common5.Baffle
7 | import java.util.concurrent.TimeUnit
8 |
9 | /**
10 | * @author Arasple
11 | * @date 2021/2/11 9:58
12 | */
13 | object ListenerMovement {
14 |
15 | val cd = Baffle.of(1, TimeUnit.SECONDS)
16 |
17 | @SubscribeEvent
18 | fun onMove(e: PlayerMoveEvent) {
19 | val player = e.player
20 |
21 | if (!cd.hasNext(player.name)) {
22 | Hologram.refreshAll(player)
23 | }
24 | }
25 |
26 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/module/listener/ListenerQuit.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.module.listener
2 |
3 | import me.arasple.mc.trhologram.module.display.Hologram
4 | import org.bukkit.event.player.PlayerQuitEvent
5 | import taboolib.common.platform.event.SubscribeEvent
6 |
7 | /**
8 | * @author Arasple
9 | * @date 2021/2/12 13:55
10 | */
11 | object ListenerQuit {
12 |
13 | @SubscribeEvent
14 | fun onQuit(e: PlayerQuitEvent) {
15 | val player = e.player
16 |
17 | ListenerMovement.cd.reset(player.name)
18 | Hologram.destroyAll(player)
19 | }
20 |
21 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/module/listener/ListenerRespawn.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.module.listener
2 |
3 | import me.arasple.mc.trhologram.module.display.Hologram
4 | import org.bukkit.event.player.PlayerRespawnEvent
5 | import taboolib.common.platform.event.SubscribeEvent
6 | import taboolib.common.platform.function.submit
7 |
8 | /**
9 | * @author Arasple
10 | * @date 2021/2/12 13:58
11 | */
12 | object ListenerRespawn {
13 |
14 | @SubscribeEvent
15 | fun onRespawn(e: PlayerRespawnEvent) {
16 | submit(delay = 2, async = true) {
17 | Hologram.refreshAll(e.player)
18 | }
19 | }
20 |
21 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/module/listener/ListenerWorldChange.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.module.listener
2 |
3 | import me.arasple.mc.trhologram.module.display.Hologram
4 | import org.bukkit.event.player.PlayerChangedWorldEvent
5 | import taboolib.common.platform.event.SubscribeEvent
6 |
7 | /**
8 | * @author Arasple
9 | * @date 2021/2/12 13:58
10 | */
11 | object ListenerWorldChange {
12 |
13 | @SubscribeEvent
14 | fun onChange(e: PlayerChangedWorldEvent) {
15 | Hologram.destroyAll(e.player)
16 | Hologram.refreshAll(e.player)
17 | }
18 |
19 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/module/service/Metrics.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.module.service
2 |
3 | import me.arasple.mc.trhologram.module.display.Hologram
4 | import taboolib.common.LifeCycle
5 | import taboolib.common.platform.Awake
6 | import taboolib.common.platform.Platform
7 | import taboolib.common.platform.function.pluginVersion
8 | import taboolib.module.metrics.Metrics
9 | import taboolib.module.metrics.charts.SingleLineChart
10 |
11 | /**
12 | * @author Arasple
13 | * @date 2020/3/7 22:15
14 | */
15 | object Metrics {
16 |
17 | private val B_STATS by lazy { Metrics(6387, pluginVersion, Platform.BUKKIT) }
18 |
19 | @Awake(LifeCycle.INIT)
20 | fun initialization() {
21 | B_STATS.let {
22 | it.addCustomChart(SingleLineChart("holograms") {
23 | Hologram.holograms.size + Hologram.externalHolograms.size
24 | })
25 | }
26 | }
27 |
28 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/module/service/Updater.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.module.service
2 |
3 | import org.bukkit.event.player.PlayerJoinEvent
4 | import taboolib.common.platform.Schedule
5 | import taboolib.common.platform.event.EventPriority
6 | import taboolib.common.platform.event.SubscribeEvent
7 | import taboolib.common.platform.function.console
8 | import taboolib.common.platform.function.pluginVersion
9 | import taboolib.common.platform.function.submit
10 | import taboolib.module.lang.sendLang
11 | import taboolib.platform.util.sendLang
12 | import java.io.BufferedReader
13 | import java.io.InputStreamReader
14 | import java.net.HttpURLConnection
15 | import java.net.URL
16 | import java.nio.charset.StandardCharsets
17 | import java.util.*
18 |
19 | /**
20 | * @author Arasple
21 | * @date 2020/7/28 18:30
22 | */
23 | object Updater {
24 |
25 | // FIXME: 从某种意义上来说, 有没有可能网络延迟太大然后无法获取到版本号? 也许应该换一个地方存.
26 | private val url = URL("https://github.com/Micalhl/TrHologram/raw/master/Version.txt")
27 | private var LATEST_VERSION: String? = ""
28 | private var NOTIFIED = false
29 | private val NOTIFIED_PLAYER = mutableSetOf()
30 |
31 | fun init() {
32 | submit(delay = 20, period = (10 * 60 * 20), async = true) {
33 | grabInfo()
34 | }
35 | }
36 |
37 | @Schedule(true, 20, 20 * 60 * 10)
38 | private fun grabInfo() {
39 | LATEST_VERSION = getLatestVersion().also {
40 | if (it != null && !NOTIFIED && it != pluginVersion) {
41 | console().sendLang("Plugin-Update", it)
42 | NOTIFIED = true
43 | }
44 | }
45 | }
46 |
47 | private fun getLatestVersion(): String? {
48 | var connection: HttpURLConnection? = null
49 | try {
50 | connection = url.openConnection() as HttpURLConnection
51 | connection.connectTimeout = 5000
52 | val buffer = StringBuilder(255)
53 | BufferedReader(InputStreamReader(connection.inputStream, StandardCharsets.UTF_8)).use { reader ->
54 | val buffer0 = CharArray(255)
55 | while (true) {
56 | val length = reader.read(buffer0)
57 | if (length == -1) break
58 | buffer.append(buffer0, 0, length)
59 | }
60 | }
61 | return buffer.toString().trim()
62 | } catch (ignored: Throwable) {
63 | } finally {
64 | connection?.disconnect()
65 | }
66 | return null
67 | }
68 |
69 | @SubscribeEvent(EventPriority.HIGHEST)
70 | fun e(e: PlayerJoinEvent) {
71 | LATEST_VERSION?.let {
72 | if (e.player.isOp && LATEST_VERSION != pluginVersion && !NOTIFIED_PLAYER.contains(e.player.uniqueId)) {
73 | e.player.sendLang("Plugin-Update", it)
74 | NOTIFIED_PLAYER.add(e.player.uniqueId)
75 | }
76 | }
77 | }
78 |
79 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/util/Heads.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.util
2 |
3 | import com.google.gson.JsonObject
4 | import com.google.gson.JsonParser
5 | import com.mojang.authlib.GameProfile
6 | import com.mojang.authlib.properties.Property
7 | import me.arasple.mc.trhologram.api.nms.NMS
8 | import me.arasple.mc.trhologram.module.hook.HookPlugin
9 | import org.bukkit.Bukkit
10 | import org.bukkit.inventory.ItemStack
11 | import org.bukkit.inventory.meta.SkullMeta
12 | import taboolib.common.platform.function.console
13 | import taboolib.common.platform.function.submit
14 | import taboolib.library.xseries.XMaterial
15 | import java.net.URL
16 | import java.util.*
17 |
18 | /**
19 | * @author Arasple
20 | * @date 2021/1/27 14:05
21 | */
22 | object Heads {
23 |
24 | private val MOJANG_API = arrayOf(
25 | "https://api.mojang.com/users/profiles/minecraft/",
26 | "https://sessionserver.mojang.com/session/minecraft/profile/"
27 | )
28 |
29 | private val DEFAULT_HEAD = XMaterial.PLAYER_HEAD.parseItem()!!
30 | private val CACHED_PLAYER_TEXTURE = mutableMapOf()
31 | private val CACHED_SKULLS = mutableMapOf()
32 |
33 | fun getHead(id: String): ItemStack {
34 | return if (id.length > 20) getCustomTextureHead(id)
35 | else getPlayerHead(id)
36 | }
37 |
38 | fun getPlayerHead(name: String): ItemStack {
39 | if (CACHED_SKULLS.containsKey(name)) {
40 | return CACHED_SKULLS[name] ?: DEFAULT_HEAD
41 | } else {
42 | CACHED_SKULLS[name] = DEFAULT_HEAD.clone()
43 | .also { item -> playerTexture(name) { modifyTexture(it, item) } ?: return DEFAULT_HEAD }
44 | return CACHED_SKULLS[name] ?: DEFAULT_HEAD
45 | }
46 | }
47 |
48 | fun getCustomTextureHead(texture: String): ItemStack {
49 | return CACHED_SKULLS.computeIfAbsent(texture) {
50 | modifyTexture(texture, DEFAULT_HEAD.clone())
51 | }
52 | }
53 |
54 | fun seekTexture(itemStack: ItemStack): String? {
55 | val meta = itemStack.itemMeta ?: return null
56 |
57 | if (meta is SkullMeta) {
58 | meta.owningPlayer?.name?.let { return it }
59 | }
60 |
61 | val field = meta.javaClass.getDeclaredField("profile").also { it.isAccessible = true }
62 | (field.get(meta) as GameProfile?)?.properties?.values()?.forEach {
63 | if (it.name == "textures") return it.value
64 | }
65 | return null
66 | }
67 |
68 | /**
69 | * PRIVATE UTILS
70 | */
71 | @Suppress("DEPRECATION")
72 | private fun playerTexture(name: String, block: (String) -> Unit): Unit? {
73 | when {
74 | HookPlugin.getSkinsRestorer().isHooked -> {
75 | HookPlugin.getSkinsRestorer().getPlayerSkinTexture(name)?.also(block) ?: return null
76 | }
77 | Bukkit.getPlayer(name)?.isOnline == true -> {
78 | NMS.INSTANCE.getGameProfile(Bukkit.getPlayer(name)!!).properties["textures"]
79 | .find { it.value != null }?.value
80 | ?.also(block)
81 | ?: return null
82 | }
83 | else -> {
84 | submit(async = true) {
85 | val profile = JsonParser().parse(fromURL("${MOJANG_API[0]}$name")) as? JsonObject
86 | if (profile == null) {
87 | console().sendMessage("§7[§3Texture§7] Texture player $name not found.")
88 | return@submit
89 | }
90 | val uuid = profile["id"].asString
91 | (JsonParser.parseString(fromURL("${MOJANG_API[1]}$uuid")) as JsonObject).getAsJsonArray("properties")
92 | (JsonParser.parseString(fromURL("${MOJANG_API[1]}$uuid")) as JsonObject).getAsJsonArray("properties")
93 | .forEach {
94 | if ("textures" == it.asJsonObject["name"].asString) {
95 | CACHED_PLAYER_TEXTURE[name] = it.asJsonObject["value"].asString.also(block)
96 | }
97 | }
98 | }
99 | }
100 | }
101 | return Unit
102 | }
103 |
104 | private fun modifyTexture(input: String, itemStack: ItemStack): ItemStack {
105 | val meta = itemStack.itemMeta as SkullMeta
106 | val profile = GameProfile(UUID.randomUUID(), null)
107 | val field = meta.javaClass.getDeclaredField("profile")
108 | val texture = if (input.length in 60..100) encodeTexture(input) else input
109 |
110 | profile.properties.put("textures", Property("textures", texture, "TrMenu_TexturedSkull"))
111 | field.isAccessible = true
112 | field[meta] = profile
113 | itemStack.itemMeta = meta
114 | return itemStack
115 | }
116 |
117 | private fun encodeTexture(input: String): String {
118 | val encoder = Base64.getEncoder()
119 | return encoder.encodeToString("{\"textures\":{\"SKIN\":{\"url\":\"http://textures.minecraft.net/texture/$input\"}}}".toByteArray())
120 | }
121 |
122 | private fun fromURL(url: String): String {
123 | return try {
124 | String(URL(url).openStream().readBytes())
125 | } catch (t: Throwable) {
126 | ""
127 | }
128 | }
129 |
130 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/util/ItemHelper.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.util
2 |
3 | import com.google.gson.JsonObject
4 | import com.google.gson.JsonParser
5 | import org.bukkit.Material
6 | import org.bukkit.inventory.ItemStack
7 | import taboolib.library.xseries.XMaterial
8 | import taboolib.module.nms.ItemTag
9 | import taboolib.platform.util.buildItem
10 |
11 | /**
12 | * @author Arasple
13 | * @date 2021/2/4 9:56
14 | */
15 | object ItemHelper {
16 |
17 | fun isNull(item: ItemStack?): Boolean {
18 | return item == null || item.type == Material.AIR
19 | }
20 |
21 | fun fromJson(json: String): ItemStack? {
22 | try {
23 | val parse = JsonParser.parseString(json)
24 | if (parse is JsonObject) {
25 | val itemBuild = buildItem(parse["type"].let { it ?: XMaterial.STONE; XMaterial.valueOf(it.asString) }) {
26 | parse["data"].let {
27 | it ?: return@let
28 | damage = it.asInt
29 | }
30 | parse["amount"].let {
31 | it ?: return@let
32 | amount = it.asInt
33 | }
34 | }
35 | val meta = parse["meta"]
36 | return if (meta != null) itemBuild.also { ItemTag.fromJson(meta.toString()).saveTo(it) }
37 | else itemBuild
38 | }
39 | return null
40 | } catch (e: Throwable) {
41 | return null
42 | }
43 | }
44 |
45 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/util/Parser.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.util
2 |
3 | import org.bukkit.Bukkit
4 | import org.bukkit.Location
5 | import org.bukkit.entity.Player
6 | import taboolib.common.platform.function.adaptPlayer
7 | import taboolib.common5.Coerce
8 | import taboolib.module.chat.colored
9 | import taboolib.module.kether.KetherFunction
10 | import taboolib.platform.compat.replacePlaceholder
11 |
12 | /**
13 | * @author Arasple
14 | * @date 2021/2/10 21:15
15 | */
16 | private val PLACEHOLDER_API = "[%{](.+?)[%}]".toRegex()
17 |
18 | fun String.containsPlaceholder(): Boolean {
19 | return PLACEHOLDER_API.find(this) != null
20 | }
21 |
22 | fun String.parseLocation(): Location {
23 | val (world, loc) = split("~", limit = 2)
24 | val (x, y, z) = loc.split(",", limit = 3).map { it.toDouble() }
25 |
26 | return Location(Bukkit.getWorld(world), x, y, z)
27 | }
28 |
29 | fun Location.parseString(): String {
30 | val world = world?.name
31 | val x = Coerce.format(x)
32 | val y = Coerce.format(y)
33 | val z = Coerce.format(z)
34 |
35 | return "$world~$x,$y,$z"
36 | }
37 |
38 | fun Player.parseString(string: String): String {
39 | return KetherFunction.parse(string) { sender = adaptPlayer(this@parseString) }.colored().replacePlaceholder(this)
40 | }
--------------------------------------------------------------------------------
/src/main/kotlin/me/arasple/mc/trhologram/util/Variables.kt:
--------------------------------------------------------------------------------
1 | package me.arasple.mc.trhologram.util
2 |
3 | /**
4 | * @author Bkm016
5 | * @date 2021/1/31 17:21
6 | */
7 | class Variables(source: String, regex: Regex, group: (List) -> String = { it[1] }) {
8 |
9 | val element: List = source.toElements(regex, group)
10 |
11 | companion object {
12 |
13 | val cacheVariables = hashMapOf()
14 |
15 | private fun String.toElements(regex: Regex, group: (List) -> String): List {
16 | val list = mutableListOf()
17 | var index = 0
18 | regex.findAll(this).forEach {
19 | list.add(Element(substring(index, it.range.first)))
20 | list.add(Element(group.invoke(it.groupValues), true))
21 | index = it.range.last + 1
22 | }
23 | val last = Element(substring(index, length))
24 | if (last.value.isNotEmpty()) {
25 | list.add(last)
26 | }
27 | return list
28 | }
29 |
30 | }
31 |
32 | class Element(var value: String, var isVariable: Boolean = false)
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/resources/holograms/Demo.yml:
--------------------------------------------------------------------------------
1 | Location: world~0,83,24
2 |
3 | Options:
4 | Line-Spacing: 0.25
5 | View-Distance: 10
6 | View-Condition: 'perm **'
7 | Refresh-Condition: 20
8 |
9 | #
10 | # Options:
11 | # {update/refresh: } -> Hologram Tick Period
12 | # {item: } -> ItemHologram
13 | # {offset: }
14 | #
15 |
16 | Contents:
17 | - 'Hello &3Tr&bHologram'
18 | - '{item=head:%player_name%}'
19 | - ''
20 | - '&7Date: &f%server_time_HH:mm:ss%{update:20}'
21 | - '&7Name: &a%player_name%'
22 |
23 | Actions:
24 | Left: 'tell color *"&3Hello, you left-clicked this hologram!"'
25 | Shift_Left:
26 | - 'tell color *"&7What''s up?"'
27 | Right: [ '' ]
28 | Shift_Right: [ '' ]
29 |
--------------------------------------------------------------------------------
/src/main/resources/lang/RU_ru.yml:
--------------------------------------------------------------------------------
1 | Plugin-UnsupportedVersion: '&8[&2Tr&aHologram&8] &cERROR &8| &cПлагин не поддерживает версию сервера Minecraft!'
2 | Plugin-Loading:
3 | - '&r'
4 | - '&7Загрузка &2Tr&aHologram... &8{0}'
5 | - '&aПеревод подготовлен UnknownZuVEnO'
6 | - '&r'
7 | Plugin-Enabled: '&8[&2Tr&aHologram&8] &bINFO &8| &3Плагин запущен! Версия плагина &2{0}&7.'
8 | Plugin-Update:
9 | - '&8[&2Tr&aHologram&8] &bUPDATE &8| &3Обновление &a{0}&3 найдено! Скачайте его для исправления багов и новых фишек!'
10 | - '&bhttps://www.spigotmc.org/resources/75503/'
11 | Plugin-Dependency-Hooked: '&8[&2Tr&aHologram&8] &6HOOK &8| &7Дополнение &f{0} &7привязан.'
12 | Plugin-Dependency-Abuse: '&8[&2Tr&aHologram&8] &6HOOK &8| &4Попытка загрузить неустановленный требуемый плагин &c{0}'
13 |
14 | Hologram-Loaded: '&8[&2Tr&aHologram&8] &aFINE &8| &a{0} &3Голограммы загрузились за &8({1} ms)'
15 |
16 | Paster-Processing: '&8[&2Tr&aHologram&8] &7Вставляем содержимое...'
17 | Paster-Success: '&8[&2Tr&aHologram&8] &3Содержимое вставлено за &a{0}'
18 | Paster-Failed: '&8[&2Tr&aHologram&8] &cПроизошла ошибка при вставке содержимого. Проверьте консоль.'
19 |
20 | Command-Help-Type: 'Тип'
21 | Command-Help-Args: 'Параметры'
22 |
23 | COMMAND-CREATE-DESCRIPTION: 'Создание новой голограммы'
24 | COMMAND-DELETE-DESCRIPTION: 'Удалить существующую голограмму'
25 | COMMAND-LIST-DESCRIPTION: 'Список загруженных голограмм'
26 | COMMAND-MIRROR-DESCRIPTION: 'Мониторинг производительности'
27 | COMMAND-MOVEHERE-DESCRIPTION: 'Телепортируйте голограмму в свое местоположение'
28 | COMMAND-RELOAD-DESCRIPTION: 'Перезагрузить голограммы'
29 | COMMAND-TELEPORT-DESCRIPTION: 'Телепортироваться в местоположение голограммы'
30 |
31 | Command-Reload: '&8[&2Tr&aHologram&8] &aFINE &8| &3Голограммы перезагрузились. &a{0}'
32 | Command-List-Error: '&8[&2Tr&aHologram&8] &7NORM &8| &7Голограм не нашлось 0_0 &8(Filter: {0})'
33 | Command-List-Header:
34 | - ''
35 | - '&8[&2Tr&aHologram&8] &aFINE &8| &7Загружено &f{0} &7голограмм &8(Filter: {1}): '
36 | - ''
37 | Command-List-Format:
38 | - type: JSON
39 | text: '&8- [&3{0}] &7| &8(Lines: {1})&r'
40 | args:
41 | - hover: '&7Кликните, чтобы переместить'
42 | command: '/trhologram teleport {0}'
43 | Command-Not-Exists: '&8[&2Tr&aHologram&8] &7NORM &8| &7Голограмма &f{0} не существует.'
44 | Command-Deleted: '&8[&2Tr&aHologram&8] &aNORM &8| &3Удалена голограмма с именем &3{0}&3.'
45 | Command-Existed:
46 | - type: TITLE
47 | title: '&c&lОПЕРАЦИЯ ОТМЕНЕНА'
48 | subtitle: '&7&lДублирование ID голограммы'
49 | - type: SOUND
50 | sound: ITEM_SHIELD_BREAK
51 | volume: 1
52 | pitch: 2
53 | Command-Created:
54 | - type: TITLE
55 | title: '&a&lГОТОВО'
56 | subtitle: '&a&lТеперь настройте голограмму'
57 | - type: SOUND
58 | sound: ITEM_BOTTLE_FILL
59 | volume: 1
60 | pitch: 2
--------------------------------------------------------------------------------
/src/main/resources/lang/en_US.yml:
--------------------------------------------------------------------------------
1 | Plugin-UnsupportedVersion: '&8[&2Tr&aHologram&8] &cERROR &8| &cPlugin does not support this outdated Minecraft version'
2 | Plugin-Loading:
3 | - '&r'
4 | - '&7Loading &2Tr&aHologram... &8{0}'
5 | - '&r'
6 | Plugin-Enabled: '&8[&2Tr&aHologram&8] &bINFO &8| &3Plugin has been enabled. Currently running version &2{0}&7.'
7 | Plugin-Update:
8 | - '&8[&2Tr&aHologram&8] &bUPDATE &8| &3Update &a{0}&3 found ! Download it from the link below for new features and bug fix!'
9 | - '&bhttps://www.spigotmc.org/resources/75503/'
10 | Plugin-Dependency-Hooked: '&8[&2Tr&aHologram&8] &6HOOK &8| &7Soft-Dependency &f{0} &7is hooked.'
11 | Plugin-Dependency-Abuse: '&8[&2Tr&aHologram&8] &6HOOK &8| &4Attempted to use the non-installed soft-depend plugin &c{0}'
12 |
13 | Hologram-Loaded: '&8[&2Tr&aHologram&8] &aFINE &8| &a{0} &3holograms were loaded &8({1} ms)'
14 |
15 | Paster-Processing: '&8[&2Tr&aHologram&8] &7Pasting content ...'
16 | Paster-Success: '&8[&2Tr&aHologram&8] &3The content has been pasted to &a{0}'
17 | Paster-Failed: '&8[&2Tr&aHologram&8] &cAn error occurred while pasting the content. Please check out console.'
18 |
19 | Command-Help-Type: 'Type'
20 | Command-Help-Args: 'Parameters'
21 |
22 | COMMAND-CREATE-DESCRIPTION: 'Create a new hologram'
23 | COMMAND-DELETE-DESCRIPTION: 'Delete an existed hologram'
24 | COMMAND-LIST-DESCRIPTION: 'List loaded holograms'
25 | COMMAND-MIRROR-DESCRIPTION: 'Monitor performance'
26 | COMMAND-MOVEHERE-DESCRIPTION: 'Teleport hologram to your location'
27 | COMMAND-RELOAD-DESCRIPTION: 'Reload holograms'
28 | COMMAND-TELEPORT-DESCRIPTION: 'Teleport to the hologram''s location'
29 |
30 | Command-Reload: '&8[&2Tr&aHologram&8] &aFINE &8| &3Successfully reloaded the hologram &a{0}'
31 | Command-List-Error: '&8[&2Tr&aHologram&8] &7NORM &8| &7No holograms are found... &8(Filter: {0})'
32 | Command-List-Header:
33 | - ''
34 | - '&8[&2Tr&aHologram&8] &aFINE &8| &7Loaded &f{0} &7holograms &8(Filter: {1}): '
35 | - ''
36 | Command-List-Format:
37 | - type: JSON
38 | text: '&8- [&3{0}] &7| &8(Lines: {1})&r'
39 | args:
40 | - hover: '&7Click to teleport'
41 | command: '/trhologram teleport {0}'
42 | Command-Not-Exists: '&8[&2Tr&aHologram&8] &7NORM &8| &7Hologram &f{0} does not exist.'
43 | Command-Deleted: '&8[&2Tr&aHologram&8] &aNORM &8| &3Deleted hologram named &3{0}&3.'
44 | Command-Existed:
45 | - type: TITLE
46 | title: '&c&lOPERATION FAILED'
47 | subtitle: '&7&lDuplicate hologram ID'
48 | - type: SOUND
49 | sound: ITEM_SHIELD_BREAK
50 | volume: 1
51 | pitch: 2
52 | Command-Created:
53 | - type: TITLE
54 | title: '&a&lCREATED'
55 | subtitle: '&a&lEdit the hologram now'
56 | - type: SOUND
57 | sound: ITEM_BOTTLE_FILL
58 | volume: 1
59 | pitch: 2
--------------------------------------------------------------------------------
/src/main/resources/lang/zh_CN.yml:
--------------------------------------------------------------------------------
1 | Plugin-UnsupportedVersion: '&8[&2Tr&aHologram&8] &cERROR &8| &c插件不支持当前服务器版本'
2 | Plugin-Loading:
3 | - '&r'
4 | - '&7正在加载 &2Tr&aHologram... &8{0}'
5 | - '&r'
6 | Plugin-Enabled: '&8[&2Tr&aHologram&8] &bINFO &8| &3插件启用. 当前运行版本 &2{0}&7.'
7 | Plugin-Update:
8 | - '&8[&2Tr&aHologram&8] &bUPDATE &8| &3更新 &a{0}&3 找到, 通过下方链接下载!'
9 | - '&bhttps://github.com/Micalhl/TrHologram/actions'
10 | Plugin-Dependency-Hooked: '&8[&2Tr&aHologram&8] &6HOOK &8| &7软依赖 &f{0} &7已兼容.'
11 | Plugin-Dependency-Abuse: '&8[&2Tr&aHologram&8] &6HOOK &8| &4试图使用未挂钩插件 &c{0}'
12 |
13 | Configuration-Auto-Reload: '&8[&2Tr&aHologram&8] &aFINE &8| &3检测到配置文件更改, 已自动重新载入...'
14 |
15 | Hologram-Loaded: '&8[&2Tr&aHologram&8] &aFINE &8| &a{0} &3个全息图已加载 &8({1} ms)'
16 |
17 | Paster-Processing: '&8[&2Tr&aHologram&8] &7正在粘贴内容 ...'
18 | Paster-Success: '&8[&2Tr&aHologram&8] &3内容已被粘贴到 &a{0}'
19 | Paster-Failed: '&8[&2Tr&aHologram&8] &c粘贴内容时发现错误. 请检查控制台.'
20 |
21 | Command-Help-Type: '命令'
22 | Command-Help-Args: '参数'
23 |
24 | COMMAND-CREATE-DESCRIPTION: '创建一个新的全息图'
25 | COMMAND-DELETE-DESCRIPTION: '删除一个已经存在的全息图'
26 | COMMAND-LIST-DESCRIPTION: '列出已加载的全息图'
27 | COMMAND-MIRROR-DESCRIPTION: '监控性能'
28 | COMMAND-MOVEHERE-DESCRIPTION: '将全息图传送到你的位置'
29 | COMMAND-RELOAD-DESCRIPTION: '重新加载全息图'
30 | COMMAND-TELEPORT-DESCRIPTION: '传送到全息图的位置'
31 |
32 | Command-Reload: '&8[&2Tr&aHologram&8] &aFINE &8| &3成功重载全息图 &a{0}'
33 | Command-List-Error: '&8[&2Tr&aHologram&8] &7NORM &8| &7没有发现全息图... &8(Filter: {0})'
34 | Command-List-Header:
35 | - ''
36 | - '&8[&2Tr&aHologram&8] &aFINE &8| &7已加载 &f{0} &7个全息图 &8(Filter: {1}): '
37 | - ''
38 | Command-List-Format:
39 | - type: JSON
40 | text: '&8- [&3{0}] &7| &8(Lines: {1})&r'
41 | args:
42 | - hover: '&7点击传送'
43 | command: '/trhologram teleport {0}'
44 | Command-Not-Exists: '&8[&2Tr&aHologram&8] &7NORM &8| &7指定的全息图 &f{0} 不存在.'
45 | Command-Deleted: '&8[&2Tr&aHologram&8] &aNORM &8| &3已删除名为 &3{0} &3的全息图.'
46 | Command-Existed:
47 | - type: TITLE
48 | title: '&c&l操作失败'
49 | subtitle: '&7&l重复的全息图 ID'
50 | - type: SOUND
51 | sound: ITEM_SHIELD_BREAK
52 | volume: 1
53 | pitch: 2
54 | Command-Created:
55 | - type: TITLE
56 | title: '&a&l已创建'
57 | subtitle: '&a&l快来修改吧 :)'
58 | - type: SOUND
59 | sound: ITEM_BOTTLE_FILL
60 | volume: 1
61 | pitch: 2
--------------------------------------------------------------------------------
/src/main/resources/settings.yml:
--------------------------------------------------------------------------------
1 | Loader:
2 | Hologram-Files:
3 | - 'plugins/CustomHologramsFolder'
4 |
5 | Hologram:
6 | Interact-Min-Delay: 500
7 | Options:
8 | Default-Line-Spacing: 0.25
9 | Default-View-Distance: 20
10 | Default-View-Condition: ''
11 | Default-Refresh-Condition: -1
12 |
--------------------------------------------------------------------------------