├── .github └── workflows │ ├── publish-release.yml │ └── publish-snapshot.yml ├── .gitignore ├── LICENSE ├── README.md ├── build.gradle.kts ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── sayanvanish-api ├── build.gradle.kts └── src │ └── main │ └── kotlin │ └── org │ └── sayandev │ └── sayanvanish │ └── api │ ├── BasicUser.kt │ ├── Permission.kt │ ├── Platform.kt │ ├── SayanVanishAPI.kt │ ├── User.kt │ ├── VanishOptions.kt │ ├── database │ ├── Database.kt │ ├── DatabaseConfig.kt │ ├── DatabaseMethod.kt │ ├── redis │ │ ├── RedisConfig.kt │ │ └── RedisDatabase.kt │ └── sql │ │ ├── SQLConfig.kt │ │ └── SQLDatabase.kt │ ├── exception │ └── UnsupportedPlatformException.kt │ ├── feature │ ├── Configurable.kt │ ├── Feature.kt │ ├── Features.kt │ ├── RegisteredFeature.kt │ ├── RegisteredFeatureHandler.kt │ └── category │ │ ├── FeatureCategories.kt │ │ └── FeatureCategory.kt │ ├── health │ └── HealthCheckData.kt │ └── utils │ ├── DownloadUtils.kt │ ├── HangarUtils.kt │ └── Paste.kt ├── sayanvanish-bukkit ├── build.gradle.kts └── src │ └── main │ └── kotlin │ └── org │ └── sayandev │ └── sayanvanish │ └── bukkit │ ├── SayanVanish.kt │ ├── VanishManager.kt │ ├── api │ ├── BukkitUser.kt │ ├── Metrics.kt │ ├── SayanVanishBukkitAPI.kt │ └── event │ │ ├── BukkitUserUnVanishEvent.kt │ │ └── BukkitUserVanishEvent.kt │ ├── command │ └── SayanVanishCommand.kt │ ├── config │ ├── LanguageConfig.kt │ └── SettingsConfig.kt │ ├── feature │ ├── HookFeature.kt │ ├── ListenedFeature.kt │ └── features │ │ ├── FeatureActionbar.kt │ │ ├── FeatureEffect.kt │ │ ├── FeatureFakeMessage.kt │ │ ├── FeatureFly.kt │ │ ├── FeatureGameMode.kt │ │ ├── FeatureInventoryInspect.kt │ │ ├── FeatureInvulnerability.kt │ │ ├── FeatureLevel.kt │ │ ├── FeatureProxyVanishQueue.kt │ │ ├── FeatureRideEntity.kt │ │ ├── FeatureSilentContainer.kt │ │ ├── FeatureState.kt │ │ ├── FeatureUpdate.kt │ │ ├── hook │ │ ├── AdvancedServerListImpl.kt │ │ ├── FeatureHookAdvancedServerList.kt │ │ ├── FeatureHookCitizens.kt │ │ ├── FeatureHookDiscordSRV.kt │ │ ├── FeatureHookDynmap.kt │ │ ├── FeatureHookEssentials.kt │ │ ├── FeatureHookMiniPlaceholders.kt │ │ ├── FeatureHookPl3xMap.kt │ │ ├── FeatureHookPlaceholderAPI.kt │ │ ├── FeatureHookSquareMap.kt │ │ ├── FeatureHookTAB.kt │ │ └── FeatureLuckPermsHook.kt │ │ └── prevent │ │ ├── FeaturePreventAdvancementAnnounce.kt │ │ ├── FeaturePreventBlockBreak.kt │ │ ├── FeaturePreventBlockGrief.kt │ │ ├── FeaturePreventBlockPlace.kt │ │ ├── FeaturePreventChat.kt │ │ ├── FeaturePreventCreatureTarget.kt │ │ ├── FeaturePreventDamage.kt │ │ ├── FeaturePreventInteract.kt │ │ ├── FeaturePreventPickup.kt │ │ ├── FeaturePreventPush.kt │ │ ├── FeaturePreventRaidTrigger.kt │ │ ├── FeaturePreventSculk.kt │ │ ├── FeaturePreventServerPing.kt │ │ ├── FeaturePreventSpawnerSpawn.kt │ │ ├── FeaturePreventTabComplete.kt │ │ └── PreventFoodLevelChange.kt │ ├── health │ ├── HealthCache.kt │ ├── HealthCheckRequestPublisher.kt │ └── ServerInfoPublisher.kt │ └── utils │ ├── PlayerUtils.kt │ └── ServerUtils.kt ├── sayanvanish-proxy ├── build.gradle.kts ├── sayanvanish-proxy-bungeecord │ ├── build.gradle.kts │ └── src │ │ └── main │ │ ├── kotlin │ │ └── org │ │ │ └── sayandev │ │ │ └── sayanvanish │ │ │ └── bungeecord │ │ │ ├── SayanVanish.kt │ │ │ ├── VanishManager.kt │ │ │ ├── api │ │ │ ├── BungeeUser.kt │ │ │ └── SayanVanishBungeeAPI.kt │ │ │ ├── event │ │ │ ├── BungeeUserUnVanishEvent.kt │ │ │ └── BungeeUserVanishEvent.kt │ │ │ ├── feature │ │ │ ├── HookFeature.kt │ │ │ ├── ListenedFeature.kt │ │ │ └── features │ │ │ │ ├── FeatureSyncEvents.kt │ │ │ │ └── prevent │ │ │ │ └── FeaturePreventTabComplete.kt │ │ │ └── utils │ │ │ └── PlayerUtils.kt │ │ └── resources │ │ └── plugin.yml ├── sayanvanish-proxy-velocity │ ├── build.gradle.kts │ └── src │ │ └── main │ │ ├── kotlin │ │ └── org │ │ │ └── sayandev │ │ │ └── sayanvanish │ │ │ └── velocity │ │ │ ├── SayanVanish.kt │ │ │ ├── VanishManager.kt │ │ │ ├── api │ │ │ ├── SayanVanishVelocityAPI.kt │ │ │ └── VelocityUser.kt │ │ │ ├── command │ │ │ └── SayanVanishProxyCommandVelocity.kt │ │ │ ├── event │ │ │ ├── VelocityUserUnVanishEvent.kt │ │ │ └── VelocityUserVanishEvent.kt │ │ │ ├── feature │ │ │ ├── HookFeature.kt │ │ │ ├── ListenedFeature.kt │ │ │ └── features │ │ │ │ ├── FeatureSyncEvents.kt │ │ │ │ ├── FeatureUpdate.kt │ │ │ │ ├── FeatureUpdatePing.kt │ │ │ │ ├── hook │ │ │ │ ├── FeatureHookAdvancedServerList.kt │ │ │ │ ├── FeatureHookEnhancedVelocity.kt │ │ │ │ ├── FeatureHookMiniPlaceholders.kt │ │ │ │ ├── FeatureHookTAB.kt │ │ │ │ ├── FeatureHookVelocitab.kt │ │ │ │ └── FeatureLuckPermsHook.kt │ │ │ │ └── prevent │ │ │ │ └── FeaturePreventTabComplete.kt │ │ │ ├── health │ │ │ ├── HealthCheckMessageSubscriber.kt │ │ │ └── ServerInfoPublisher.kt │ │ │ └── utils │ │ │ └── PlayerUtils.kt │ │ └── resources │ │ └── velocity-plugin.json └── src │ └── main │ └── kotlin │ └── org │ └── sayandev │ └── sayanvanish │ └── proxy │ ├── command │ └── SayanVanishProxyCommand.kt │ └── config │ ├── LanguageConfig.kt │ └── SettingsConfig.kt └── settings.gradle.kts /.github/workflows/publish-release.yml: -------------------------------------------------------------------------------- 1 | name: Publish Releases 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | 8 | jobs: 9 | build: 10 | name: Build artifacts job 11 | runs-on: ubuntu-22.04 12 | steps: 13 | - name: Checkout code 14 | uses: actions/checkout@v2 15 | with: 16 | fetch-depth: 0 17 | tags: true 18 | - name: Checkout Repository 19 | uses: actions/checkout@v4 20 | - name: Validate Gradle Wrapper 21 | uses: gradle/wrapper-validation-action@v1 22 | - name: Set up JDK 17 23 | uses: actions/setup-java@v4 24 | with: 25 | distribution: 'temurin' 26 | java-version: 17 27 | - name: Change wrapper permissions 28 | run: chmod +x ./gradlew 29 | - name: Cache Gradle dependencies 30 | uses: actions/cache@v3 31 | with: 32 | path: ~/.gradle/caches 33 | key: ${{ runner.OS }}-gradle-${{ hashFiles('**/*.gradle') }} 34 | restore-keys: | 35 | ${{ runner.OS }}-gradle- 36 | - name: Build artifacts 37 | env: 38 | REPO_SAYAN_USER: ${{ secrets.REPO_SAYAN_USER }} 39 | REPO_SAYAN_TOKEN: ${{ secrets.REPO_SAYAN_TOKEN }} 40 | run: ./gradlew build 41 | - name: Publish to Hangar 42 | env: 43 | HANGAR_API_TOKEN: ${{ secrets.HANGAR_API_TOKEN }} 44 | HANGAR_BUILD_CHANNEL: Release 45 | # HANGAR_CHANGELOG: ${{ github.event.release.body }} 46 | REPO_SAYAN_USER: ${{ secrets.REPO_SAYAN_USER }} 47 | REPO_SAYAN_TOKEN: ${{ secrets.REPO_SAYAN_TOKEN }} 48 | run: ./gradlew build publishPluginPublicationToHangar --stacktrace 49 | - name: Publish to Modrinth 50 | run: ./gradlew modrinth 51 | env: 52 | MODRINTH_BUILD_CHANNEL: release 53 | MODRINTH_API_TOKEN: ${{ secrets.MODRINTH_API_TOKEN }} 54 | # MODRINTH_CHANGELOG: ${{ github.event.release.body }} 55 | REPO_SAYAN_USER: ${{ secrets.REPO_SAYAN_USER }} 56 | REPO_SAYAN_TOKEN: ${{ secrets.REPO_SAYAN_TOKEN }} 57 | - name: Publish to SayanDevelopment snapshot repo 58 | run: ./gradlew publish 59 | env: 60 | REPO_SAYAN_USER: ${{ secrets.REPO_SAYAN_USER }} 61 | REPO_SAYAN_TOKEN: ${{ secrets.REPO_SAYAN_TOKEN }} 62 | 63 | -------------------------------------------------------------------------------- /.github/workflows/publish-snapshot.yml: -------------------------------------------------------------------------------- 1 | name: Build artifacts 2 | on: 3 | push: 4 | branches: 5 | - master 6 | 7 | jobs: 8 | build: 9 | name: Build artifacts job 10 | runs-on: ubuntu-22.04 11 | steps: 12 | - name: Checkout Repository 13 | uses: actions/checkout@v4 14 | with: 15 | fetch-depth: 0 16 | - name: Validate Gradle Wrapper 17 | uses: gradle/wrapper-validation-action@v1 18 | - name: Set up JDK 17 19 | uses: actions/setup-java@v4 20 | with: 21 | distribution: 'temurin' 22 | java-version: 17 23 | - name: Change wrapper permissions 24 | run: chmod +x ./gradlew 25 | - name: Cache Gradle dependencies 26 | uses: actions/cache@v3 27 | with: 28 | path: ~/.gradle/caches 29 | key: ${{ runner.OS }}-gradle-${{ hashFiles('**/*.gradle') }} 30 | restore-keys: | 31 | ${{ runner.OS }}-gradle- 32 | - name: Build artifacts 33 | env: 34 | REPO_SAYAN_USER: ${{ secrets.REPO_SAYAN_USER }} 35 | REPO_SAYAN_TOKEN: ${{ secrets.REPO_SAYAN_TOKEN }} 36 | run: ./gradlew build 37 | - name: Publish to Hangar 38 | env: 39 | HANGAR_API_TOKEN: ${{ secrets.HANGAR_API_TOKEN }} 40 | HANGAR_BUILD_CHANNEL: Snapshot 41 | # HANGAR_CHANGELOG: ${{ github.event.release.body }} 42 | REPO_SAYAN_USER: ${{ secrets.REPO_SAYAN_USER }} 43 | REPO_SAYAN_TOKEN: ${{ secrets.REPO_SAYAN_TOKEN }} 44 | run: ./gradlew build publishPluginPublicationToHangar --stacktrace 45 | - name: Publish to Modrinth 46 | run: ./gradlew modrinth 47 | env: 48 | MODRINTH_BUILD_CHANNEL: beta 49 | MODRINTH_API_TOKEN: ${{ secrets.MODRINTH_API_TOKEN }} 50 | MODRINTH_CHANGELOG: ${{ github.event.release.body }} 51 | REPO_SAYAN_USER: ${{ secrets.REPO_SAYAN_USER }} 52 | REPO_SAYAN_TOKEN: ${{ secrets.REPO_SAYAN_TOKEN }} 53 | - name: Publish to SayanDevelopment snapshot repo 54 | run: ./gradlew publish 55 | env: 56 | REPO_SAYAN_USER: ${{ secrets.REPO_SAYAN_USER }} 57 | REPO_SAYAN_TOKEN: ${{ secrets.REPO_SAYAN_TOKEN }} 58 | 59 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | !**/src/main/**/build/ 5 | !**/src/test/**/build/ 6 | 7 | ### IntelliJ IDEA ### 8 | .idea/modules.xml 9 | .idea/jarRepositories.xml 10 | .idea/compiler.xml 11 | .idea/libraries/ 12 | *.iws 13 | *.iml 14 | *.ipr 15 | out/ 16 | !**/src/main/**/out/ 17 | !**/src/test/**/out/ 18 | 19 | ### Eclipse ### 20 | .apt_generated 21 | .classpath 22 | .factorypath 23 | .project 24 | .settings 25 | .springBeans 26 | .sts4-cache 27 | bin/ 28 | !**/src/main/**/bin/ 29 | !**/src/test/**/bin/ 30 | 31 | ### NetBeans ### 32 | /nbproject/private/ 33 | /nbbuild/ 34 | /dist/ 35 | /nbdist/ 36 | /.nb-gradle/ 37 | 38 | ### VS Code ### 39 | .vscode/ 40 | 41 | ### Mac OS ### 42 | .DS_Store 43 | 44 | .idea 45 | **/run 46 | .kotlin -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official 2 | org.gradle.caching=true 3 | org.gradle.parallel=true 4 | org.gradle.vfs.watch=true 5 | systemProp.file.encoding=utf-8 6 | 7 | paperVersion=1.8-1.21.4 8 | foliaVersion=1.19.4-1.21.4 9 | velocityVersion=3.0-3.4 10 | waterfallVersion=1.11-1.21 11 | 12 | modrinthProjectID=hkzyeLcD 13 | modrinthMinecraftVersions=1.8.9,\ 14 | 1.9,1.9.1,1.9.2,1.9.3,1.9.4,\ 15 | 1.10,1.10.1,1.10.2,\ 16 | 1.11,1.11.1,1.11.2,\ 17 | 1.12,1.12.1,1.12.2,\ 18 | 1.13,1.13.1,1.13.2,\ 19 | 1.14,1.14.1,1.14.2,1.14.3,1.14.4,\ 20 | 1.15,1.15.1,1.15.2,\ 21 | 1.16,1.16.1,1.16.2,1.16.3,1.16.4,1.16.5,\ 22 | 1.17,1.17.1,\ 23 | 1.18,1.18.1,1.18.2,\ 24 | 1.19,1.19.1,1.19.2,1.19.3,1.19.4,\ 25 | 1.20,1.20.1,1.20.2,1.20.3,1.20.4,1.20.5,1.20.6,\ 26 | 1.21,1.21.1,1.21.2,1.21.3,1.21.4 27 | modrinthLoaders=paper,purpur,spigot,velocity,bungeecord,waterfall,folia 28 | 29 | group=org.sayandev 30 | slug=sayanvanish 31 | name=SayanVanish 32 | description=A modular vanish system for Minecraft servers 33 | website=https://sayandev.org 34 | author=Syrent 35 | 36 | version=1.7.0-SNAPSHOT -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | # api 3 | discordsrv = "1.27.0" 4 | luckperms-api = "5.4" 5 | miniplaceholders-api = "2.2.3" 6 | advancedserverlist = "3.3.0" 7 | tab-api = "5.0.2" 8 | 9 | # bukkit 10 | paper-api = "1.20.6-R0.1-SNAPSHOT" 11 | folia-api = "1.20.4-R0.1-SNAPSHOT" 12 | placeholderapi = "2.11.6" 13 | essentialsx = "2.20.1" 14 | citizens-api = "2.0.28-SNAPSHOT" 15 | squaremap-api = "1.2.3" 16 | pl3xmap-api = "1.21.4-512" 17 | dynmap = "3.7-beta-6" 18 | 19 | # proxy 20 | # proxy - velocity 21 | velocity-api = "3.3.0-SNAPSHOT" 22 | velocitab = "1.7.4" 23 | enhancedvelocity = "1.3.4" 24 | # proxy - bungeecord 25 | bungeecord-api = "1.19-R0.1-SNAPSHOT" 26 | 27 | [libraries] 28 | # api 29 | discordsrv = { group = "com.discordsrv", name="discordsrv", version.ref="discordsrv" } 30 | luckperms-api = { group = "net.luckperms", name="api", version.ref="luckperms-api" } 31 | miniplaceholders-api = { group = "io.github.miniplaceholders", name="miniplaceholders-api", version.ref="miniplaceholders-api" } 32 | advancedserverlist = { group = "ch.andre601.asl-api", name="api", version.ref="advancedserverlist" } 33 | tab-api = { group = "com.github.NEZNAMY", name="TAB-API", version.ref="tab-api" } 34 | 35 | # bukkit 36 | paper-api = { group = "io.papermc.paper", name="paper-api", version.ref="paper-api" } 37 | folia-api = { group = "dev.folia", name="folia-api", version.ref="folia-api" } 38 | placeholderapi = { group = "me.clip", name="placeholderapi", version.ref="placeholderapi" } 39 | essentialsx = { group = "net.essentialsx", name="EssentialsX", version.ref="essentialsx" } 40 | citizens-api = { group = "net.citizensnpcs", name="citizensapi", version.ref="citizens-api" } 41 | squaremap-api = { group = "xyz.jpenilla", name="squaremap-api", version.ref="squaremap-api" } 42 | pl3xmap-api = { group = "maven.modrinth", name="pl3xmap", version.ref="pl3xmap-api" } 43 | dynmap-core-api = { group = "us.dynmap", name="DynmapCoreAPI", version.ref="dynmap" } 44 | dynmap-api = { group = "us.dynmap", name="dynmap-api", version.ref="dynmap" } 45 | 46 | # proxy 47 | # proxy - velocity 48 | velocity-api = { group = "com.velocitypowered", name="velocity-api", version.ref="velocity-api" } 49 | velocitab = { group = "net.william278", name="velocitab", version.ref="velocitab" } 50 | enhancedvelocity = { group = "ir.syrent", name="EnhancedVelocity", version.ref="enhancedvelocity" } 51 | # proxy - bungeecord 52 | bungeecord-api = { group = "net.md-5", name="bungeecord-api", version.ref="bungeecord-api" } 53 | 54 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syrent/SayanVanish/5e3e2cbee8b3d7d56589dfbaeaa4b838c2b61abc/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-8.12-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /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 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /sayanvanish-api/build.gradle.kts: -------------------------------------------------------------------------------- 1 | dependencies { 2 | compileOnlyApi(libs.discordsrv) 3 | compileOnlyApi(libs.luckperms.api) 4 | compileOnlyApi(libs.miniplaceholders.api) 5 | compileOnlyApi(libs.advancedserverlist) 6 | compileOnlyApi(libs.tab.api) 7 | } -------------------------------------------------------------------------------- /sayanvanish-api/src/main/kotlin/org/sayandev/sayanvanish/api/BasicUser.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.api 2 | 3 | import com.google.gson.Gson 4 | import com.google.gson.JsonObject 5 | import com.google.gson.JsonParser 6 | import org.sayandev.sayanvanish.api.exception.UnsupportedPlatformException 7 | import java.util.* 8 | 9 | interface BasicUser { 10 | 11 | val uniqueId: UUID 12 | var username: String 13 | var serverId: String 14 | 15 | fun hasPermission(permission: String): Boolean { 16 | throw UnsupportedPlatformException("hasPermission") 17 | } 18 | 19 | fun hasPermission(permission: Permission): Boolean { 20 | return hasPermission(permission.permission()) 21 | } 22 | 23 | fun save() { 24 | SayanVanishAPI.getInstance().database.addBasicUser(this) 25 | } 26 | 27 | fun toJson(): String { 28 | val json = JsonObject() 29 | json.addProperty("unique-id", uniqueId.toString()) 30 | json.addProperty("username", username) 31 | json.addProperty("server-id", serverId) 32 | return Gson().toJson(json) 33 | } 34 | 35 | companion object { 36 | @JvmStatic 37 | fun fromJson(serialized: String): BasicUser { 38 | val json = JsonParser.parseString(serialized).asJsonObject 39 | val uniqueId = json.get("unique-id").asString 40 | val username = json.get("username").asString 41 | val serverId = json.get("server-id").asString 42 | return create(UUID.fromString(uniqueId), username, serverId) 43 | } 44 | 45 | @JvmStatic 46 | fun create(uniqueId: UUID, username: String, serverId: String?): BasicUser { 47 | return object : BasicUser { 48 | override val uniqueId: UUID = uniqueId 49 | override var username: String = username 50 | override var serverId: String = serverId ?: Platform.get().serverId 51 | } 52 | } 53 | } 54 | 55 | } -------------------------------------------------------------------------------- /sayanvanish-api/src/main/kotlin/org/sayandev/sayanvanish/api/Permission.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.api 2 | 3 | enum class Permission(val value: String) { 4 | VANISH("vanish.use"), 5 | VANISH_OTHERS("commands.vanish.others"), 6 | VANISH_ON_JOIN("action.vanish.onjoin"), 7 | FEATURE_PLAYER_TOGGLE("commands.feature.toggleplayer.other"), 8 | INVULNERABLE("action.vanish.invulnerable"), 9 | FLY_KEEP_AFTER_REAPPEAR("action.vanish.fly.keep_after_reappear"), 10 | FLY("action.vanish.fly"); 11 | 12 | fun permission(): String { 13 | return "sayanvanish.${value}" 14 | } 15 | } -------------------------------------------------------------------------------- /sayanvanish-api/src/main/kotlin/org/sayandev/sayanvanish/api/Platform.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.api 2 | 3 | import org.sayandev.sayanvanish.api.feature.RegisteredFeatureHandler 4 | import java.io.File 5 | import java.util.logging.Logger 6 | 7 | data class Platform( 8 | val id: String, 9 | val logger: Logger, 10 | var rootDirectory: File, 11 | var serverId: String, 12 | ) { 13 | 14 | companion object { 15 | private var currentPlatform = Platform("default", Logger.getGlobal(), File("."), "unknown") 16 | 17 | @JvmStatic 18 | fun setPlatform(platform: Platform) { 19 | currentPlatform = platform 20 | } 21 | 22 | @JvmStatic 23 | fun register() { 24 | RegisteredFeatureHandler.process() 25 | } 26 | 27 | @JvmStatic 28 | fun setAndRegister(platform: Platform): Boolean { 29 | setPlatform(platform) 30 | 31 | if (!SayanVanishAPI.getInstance().databaseConnected) { 32 | return false 33 | } 34 | 35 | register() 36 | return true 37 | } 38 | 39 | @JvmStatic 40 | fun get() = currentPlatform 41 | } 42 | 43 | 44 | 45 | } -------------------------------------------------------------------------------- /sayanvanish-api/src/main/kotlin/org/sayandev/sayanvanish/api/SayanVanishAPI.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.api 2 | 3 | import org.sayandev.sayanvanish.api.database.DatabaseMethod 4 | import org.sayandev.sayanvanish.api.database.databaseConfig 5 | import org.sayandev.sayanvanish.api.database.redis.RedisDatabase 6 | import org.sayandev.sayanvanish.api.database.sql.SQLDatabase 7 | import java.util.* 8 | 9 | open class SayanVanishAPI(val type: Class) { 10 | constructor(): this(User::class.java) 11 | 12 | var databaseConnected: Boolean = true 13 | 14 | val database = when (databaseConfig.method) { 15 | DatabaseMethod.SQL -> { 16 | try { 17 | SQLDatabase(databaseConfig.sql, type, databaseConfig.useCacheWhenAvailable).apply { 18 | this.connect() 19 | this.initialize() 20 | } 21 | } catch (e: Exception) { 22 | databaseConnected = false 23 | logDatabaseError() 24 | throw e 25 | } 26 | } 27 | DatabaseMethod.REDIS -> { 28 | try { 29 | RedisDatabase(databaseConfig.redis, type, databaseConfig.useCacheWhenAvailable).apply { 30 | this.initialize() 31 | this.connect() 32 | } 33 | } catch (e: Exception) { 34 | databaseConnected = false 35 | logDatabaseError() 36 | throw e 37 | } 38 | } 39 | } 40 | 41 | init { 42 | for (user in database.getUsers().filter { user -> user.serverId == Platform.get().serverId }) { 43 | user.isOnline = false 44 | user.save() 45 | } 46 | database.purgeBasic(Platform.get().serverId) 47 | } 48 | 49 | fun getPlatform(): Platform { 50 | return Platform.get() 51 | } 52 | 53 | fun isVanished(uniqueId: UUID, useCache: Boolean = true): Boolean { 54 | return database.getUser(uniqueId, useCache)?.isVanished == true 55 | } 56 | 57 | fun isVanished(uniqueId: UUID): Boolean { 58 | return database.getUser(uniqueId, true)?.isVanished == true 59 | } 60 | 61 | fun canSee(user: U?, target: U): Boolean { 62 | if (!target.isVanished) return true 63 | val vanishLevel = user?.vanishLevel ?: -1 64 | return vanishLevel >= target.vanishLevel 65 | } 66 | 67 | fun getUser(uniqueId: UUID, useCache: Boolean = true): U? { 68 | return database.getUser(uniqueId, useCache) 69 | } 70 | 71 | fun getUser(uniqueId: UUID): U? { 72 | return getUser(uniqueId, true) 73 | } 74 | 75 | fun getOnlineUsers(): List { 76 | return database.getUsers().filter { it.isOnline } 77 | } 78 | 79 | fun getVanishedUsers(): List { 80 | return database.getUsers().filter { it.isVanished } 81 | } 82 | 83 | private fun logDatabaseError() { 84 | Platform.get().logger.severe("Database connection failed. Disabling the plugin.") 85 | Platform.get().logger.severe("Please check the following:") 86 | Platform.get().logger.severe("- Make sure your database server is not misconfigured.") 87 | Platform.get().logger.severe("- Make sure your database server is running.") 88 | Platform.get().logger.severe("Here's the full error trace:") 89 | } 90 | 91 | companion object { 92 | private val defaultInstance = SayanVanishAPI() 93 | 94 | @JvmStatic 95 | fun getInstance(): SayanVanishAPI { 96 | return defaultInstance 97 | } 98 | 99 | @JvmStatic 100 | fun UUID.user(): User? { 101 | return getInstance().getUser(this) 102 | } 103 | 104 | /*fun UUID.asyncUser(result: (User?) -> Unit) { 105 | getInstance().getUserAsync(this, result) 106 | }*/ 107 | } 108 | 109 | } -------------------------------------------------------------------------------- /sayanvanish-api/src/main/kotlin/org/sayandev/sayanvanish/api/User.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.api 2 | 3 | import com.google.gson.Gson 4 | import com.google.gson.JsonObject 5 | import com.google.gson.JsonParser 6 | import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver 7 | import org.sayandev.sayanvanish.api.exception.UnsupportedPlatformException 8 | import java.util.* 9 | 10 | interface User : BasicUser { 11 | 12 | var currentOptions: VanishOptions 13 | var isVanished: Boolean 14 | var isOnline: Boolean 15 | var vanishLevel: Int 16 | 17 | fun vanish(options: VanishOptions) { 18 | isVanished = true 19 | save() 20 | } 21 | 22 | fun vanish() { 23 | vanish(VanishOptions.defaultOptions()) 24 | } 25 | 26 | fun unVanish(options: VanishOptions) { 27 | isVanished = false 28 | save() 29 | } 30 | 31 | fun unVanish() { 32 | unVanish(VanishOptions.defaultOptions()) 33 | } 34 | 35 | fun toggleVanish(options: VanishOptions) { 36 | if (isVanished) unVanish(options) else vanish(options) 37 | } 38 | 39 | fun toggleVanish() { 40 | toggleVanish(VanishOptions.defaultOptions()) 41 | } 42 | 43 | fun sendComponent(content: String, vararg placeholder: TagResolver) { 44 | throw UnsupportedPlatformException("sendMessage") 45 | } 46 | 47 | fun sendActionbar(content: String, vararg placeholder: TagResolver) { 48 | throw UnsupportedPlatformException("sendActionbar") 49 | } 50 | 51 | /** 52 | * @param otherUser The user to check if this user can see 53 | * */ 54 | fun canSee(otherUser: User): Boolean { 55 | if (!otherUser.isVanished) return true 56 | if (this.uniqueId == otherUser.uniqueId) return true 57 | val canSee = vanishLevel >= otherUser.vanishLevel 58 | return canSee 59 | } 60 | 61 | override fun save() { 62 | serverId = Platform.get().serverId 63 | SayanVanishAPI.getInstance().database.addUser(this) 64 | } 65 | 66 | fun delete() { 67 | SayanVanishAPI.getInstance().database.removeUser(uniqueId) 68 | } 69 | 70 | override fun toJson(): String { 71 | val json = JsonObject() 72 | json.addProperty("unique-id", uniqueId.toString()) 73 | json.addProperty("username", username) 74 | json.addProperty("is-vanished", isVanished) 75 | json.addProperty("is-online", isOnline) 76 | json.addProperty("vanish-level", vanishLevel) 77 | json.addProperty("current-options", currentOptions.toJson()) 78 | return Gson().toJson(json) 79 | } 80 | 81 | companion object { 82 | @JvmStatic 83 | fun fromJson(serialized: String): User { 84 | val json = JsonParser.parseString(serialized).asJsonObject 85 | val uniqueId = json.get("unique-id").asString 86 | val username = json.get("username").asString 87 | val isVanished = json.get("is-vanished").asBoolean 88 | val isOnline = json.get("is-online").asBoolean 89 | val vanishLevel = json.get("vanish-level").asInt 90 | val currentOptions = VanishOptions.fromJson(json.get("current-options").asString) 91 | return object : User { 92 | override val uniqueId = UUID.fromString(uniqueId) 93 | override var username = username 94 | override var isVanished = isVanished 95 | override var isOnline = isOnline 96 | override var vanishLevel = vanishLevel 97 | override var currentOptions = currentOptions 98 | override var serverId = Platform.get().id 99 | } 100 | } 101 | 102 | fun User.convert(to: Class): Any { 103 | val instance = to.getDeclaredMethod("fromUser", User::class.java).invoke(null, this) 104 | return instance 105 | } 106 | } 107 | 108 | } -------------------------------------------------------------------------------- /sayanvanish-api/src/main/kotlin/org/sayandev/sayanvanish/api/VanishOptions.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.api 2 | 3 | import com.google.gson.Gson 4 | import com.google.gson.JsonObject 5 | import com.google.gson.JsonParser 6 | 7 | data class VanishOptions( 8 | var sendMessage: Boolean = true, 9 | var notifyStatusChangeToOthers: Boolean = true, 10 | var notifyJoinQuitVanished: Boolean = true, 11 | var isOnJoin: Boolean = false, 12 | var isOnQuit: Boolean = false, 13 | ) { 14 | 15 | class Builder { 16 | private var sendMessage = true 17 | private var notifyStatusChangeToOthers = true 18 | private var notifyJoinQuitVanished = true 19 | private var isOnJoin = false 20 | private var isOnQuit = false 21 | 22 | fun sendMessage(sendMessage: Boolean): Builder { 23 | this.sendMessage = sendMessage 24 | return this 25 | } 26 | 27 | fun notifyStatusChangeToOthers(notifyStatusChangeToOthers: Boolean): Builder { 28 | this.notifyStatusChangeToOthers = notifyStatusChangeToOthers 29 | return this 30 | } 31 | 32 | fun notifyJoinQuitVanished(notifyJoinQuitVanished: Boolean): Builder { 33 | this.notifyJoinQuitVanished = notifyJoinQuitVanished 34 | return this 35 | } 36 | 37 | fun isOnJoin(isOnJoin: Boolean): Builder { 38 | this.isOnJoin = isOnJoin 39 | return this 40 | } 41 | 42 | fun isOnQuit(isOnQuit: Boolean): Builder { 43 | this.isOnQuit = isOnQuit 44 | return this 45 | } 46 | 47 | fun build(): VanishOptions { 48 | return VanishOptions(sendMessage, notifyStatusChangeToOthers, notifyJoinQuitVanished, isOnJoin) 49 | } 50 | } 51 | 52 | fun toJson(): String { 53 | val json = JsonObject() 54 | json.addProperty("send-message", sendMessage) 55 | json.addProperty("notify-status-change-to-others", notifyStatusChangeToOthers) 56 | json.addProperty("notify-join-quit-vanished", notifyJoinQuitVanished) 57 | json.addProperty("is-on-join", isOnJoin) 58 | json.addProperty("is-on-quit", isOnQuit) 59 | return Gson().toJson(json) 60 | } 61 | 62 | companion object { 63 | @JvmStatic 64 | fun fromJson(serialized: String): VanishOptions { 65 | val json = JsonParser.parseString(serialized).asJsonObject 66 | return VanishOptions( 67 | json.get("send-message").asBoolean, 68 | json.get("notify-status-change-to-others").asBoolean, 69 | json.get("notify-join-quit-vanished").asBoolean, 70 | json.get("is-on-join").asBoolean 71 | ) 72 | } 73 | 74 | @JvmStatic 75 | fun defaultOptions(): VanishOptions { 76 | return VanishOptions() 77 | } 78 | } 79 | } -------------------------------------------------------------------------------- /sayanvanish-api/src/main/kotlin/org/sayandev/sayanvanish/api/database/Database.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.api.database 2 | 3 | import org.sayandev.sayanvanish.api.BasicUser 4 | import org.sayandev.sayanvanish.api.User 5 | import java.util.* 6 | 7 | interface Database { 8 | 9 | var cache: MutableMap 10 | var useCache: Boolean 11 | 12 | fun initialize() 13 | 14 | fun connect() 15 | 16 | fun disconnect() 17 | 18 | fun addUser(user: U) 19 | 20 | fun hasUser(uniqueId: UUID): Boolean 21 | 22 | fun updateUser(user: U) 23 | 24 | fun removeUser(uniqueId: UUID) 25 | 26 | fun getUser(uniqueId: UUID, useCache: Boolean = true): U? 27 | 28 | fun getUser(uniqueId: UUID): U? { 29 | return getUser(uniqueId, true) 30 | } 31 | 32 | fun getUsers(): List 33 | fun getUsersAsync(result: (List) -> Unit) 34 | 35 | fun getBasicUsers(useCache: Boolean): List 36 | fun getBasicUsersAsync(result: (List) -> Unit) 37 | 38 | fun addBasicUser(user: BasicUser) 39 | fun hasBasicUser(uniqueId: UUID, useCache: Boolean): Boolean 40 | fun updateBasicUser(user: BasicUser) 41 | fun removeBasicUser(uniqueId: UUID) 42 | 43 | fun isInQueue(uniqueId: UUID, result: (Boolean) -> Unit) 44 | fun addToQueue(uniqueId: UUID, vanished: Boolean) 45 | fun getFromQueue(uniqueId: UUID, result: (Boolean) -> Unit) 46 | fun removeFromQueue(uniqueId: UUID) 47 | 48 | fun purge() 49 | fun purgeCache() 50 | fun purgeBasic() 51 | fun purgeBasic(serverId: String) 52 | 53 | } -------------------------------------------------------------------------------- /sayanvanish-api/src/main/kotlin/org/sayandev/sayanvanish/api/database/DatabaseConfig.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.api.database 2 | 3 | import org.sayandev.sayanvanish.api.Platform 4 | import org.sayandev.sayanvanish.api.database.redis.RedisConfig 5 | import org.sayandev.sayanvanish.api.database.sql.SQLConfig 6 | import org.sayandev.stickynote.core.configuration.Config 7 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 8 | import org.spongepowered.configurate.objectmapping.meta.Comment 9 | import java.io.File 10 | 11 | public var databaseConfig = DatabaseConfig.fromConfig() ?: DatabaseConfig.defaultConfig() 12 | 13 | @ConfigSerializable 14 | class DatabaseConfig( 15 | @Comment("Configuration for the database, including method, SQL, Redis, and caching options.") 16 | val method: DatabaseMethod = DatabaseMethod.SQL, 17 | @Comment("Configuration for SQL database") 18 | val sql: SQLConfig = SQLConfig(), 19 | @Comment("Configuration for Redis database") 20 | val redis: RedisConfig = RedisConfig(), 21 | @Comment("Whether to use cache when available") 22 | val useCacheWhenAvailable: Boolean = true, 23 | ) : Config(Platform.get().rootDirectory, fileName) { 24 | companion object { 25 | private val fileName = "database.yml" 26 | 27 | @JvmStatic 28 | fun defaultConfig(): DatabaseConfig { 29 | return DatabaseConfig().also { it.save() } 30 | } 31 | 32 | @JvmStatic 33 | fun fromConfig(): DatabaseConfig? { 34 | return fromConfig(File(Platform.get().rootDirectory, fileName)) 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /sayanvanish-api/src/main/kotlin/org/sayandev/sayanvanish/api/database/DatabaseMethod.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.api.database 2 | 3 | enum class DatabaseMethod { 4 | SQL, 5 | REDIS, 6 | } -------------------------------------------------------------------------------- /sayanvanish-api/src/main/kotlin/org/sayandev/sayanvanish/api/database/redis/RedisConfig.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.api.database.redis 2 | 3 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 4 | import org.spongepowered.configurate.objectmapping.meta.Comment 5 | 6 | @ConfigSerializable 7 | class RedisConfig( 8 | @Comment("The type of Redis configuration. Available types: STANDALONE") 9 | val type: RedisType = RedisType.STANDALONE, 10 | @Comment("Configuration for standalone Redis setup") 11 | val standalone: Standalone = Standalone(), 12 | ) { 13 | 14 | @ConfigSerializable 15 | data class Standalone( 16 | @Comment("The host address of the Redis database. If it's an IP address (x.x.x.x), ensure it is enclosed in double quotes (`\"`).") 17 | val host: String = "127.0.0.1", 18 | @Comment("The port number of the Redis server") 19 | val port: Int = 6379, 20 | @Comment("The username for accessing the Redis server") 21 | val user: String = "", 22 | @Comment("The password for accessing the Redis server") 23 | val password: String = "", 24 | @Comment("Whether to use SSL for the connection") 25 | val ssl: Boolean = false 26 | ) 27 | 28 | enum class RedisType { 29 | STANDALONE 30 | } 31 | } -------------------------------------------------------------------------------- /sayanvanish-api/src/main/kotlin/org/sayandev/sayanvanish/api/database/sql/SQLConfig.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.api.database.sql 2 | 3 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 4 | import org.spongepowered.configurate.objectmapping.meta.Comment 5 | import org.spongepowered.configurate.objectmapping.meta.Setting 6 | 7 | @ConfigSerializable 8 | class SQLConfig( 9 | @Comment("The method to use for the database. Available methods: MYSQL, SQLITE") 10 | val method: SQLMethod = SQLMethod.SQLITE, 11 | @Comment("The host address of the SQL database. If it's an IP address (x.x.x.x), ensure it is enclosed in double quotes (`\"`).") 12 | val host: String = "127.0.0.1", 13 | @Comment("The port number of the SQL database") 14 | val port: Int = 3306, 15 | @Comment("The name of the SQL database") 16 | val database: String = "sayanvanish", 17 | @Comment("The username for accessing the SQL database") 18 | val username: String = "minecraft", 19 | @Comment("The password for accessing the SQL database") 20 | val password: String = "", 21 | @Comment("Properties for the connection pool") 22 | val poolProperties: PoolProperties = PoolProperties(), 23 | @Comment("The prefix for the table names in the SQL database") 24 | val tablePrefix: String = "sayanvanish_", 25 | ) { 26 | @ConfigSerializable 27 | data class PoolProperties( 28 | @Comment("The maximum number of connections in the pool") 29 | val maximumPoolSize: Int = 5, 30 | @Comment("The minimum number of idle connections in the pool") 31 | val minimumIdle: Int = 5, 32 | @Comment("The maximum lifetime of a connection in the pool (in milliseconds)") 33 | val maximumLifetime: Long = 1800000, 34 | @Comment("The keepalive time for a connection in the pool (in milliseconds)") 35 | val keepaliveTime: Long = 0, 36 | @Comment("The connection timeout (in milliseconds)") 37 | val connectionTimeout: Long = 5000, 38 | @Comment("Whether to use SSL for the connection") 39 | @Setting("use-ssl") val useSSL: Boolean = false, 40 | @Comment("Whether to verify the server certificate") 41 | val verifyServerCertificate: Boolean = false, 42 | @Comment("Whether to allow public key retrieval") 43 | val allowPublicKeyRetrieval: Boolean = true, 44 | ) 45 | 46 | enum class SQLMethod { 47 | MYSQL, 48 | SQLITE 49 | } 50 | } -------------------------------------------------------------------------------- /sayanvanish-api/src/main/kotlin/org/sayandev/sayanvanish/api/exception/UnsupportedPlatformException.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.api.exception 2 | 3 | import org.sayandev.sayanvanish.api.Platform 4 | 5 | class UnsupportedPlatformException(action: String?, message: String) : Exception(message) { 6 | constructor(action: String) : this(action, "This action is not supported on this platform yet. (platform: ${Platform.get().id}, action: ${action})") 7 | constructor() : this(null, "This platform is not supported yet. (platform: ${Platform.get().id})") 8 | } -------------------------------------------------------------------------------- /sayanvanish-api/src/main/kotlin/org/sayandev/sayanvanish/api/feature/Configurable.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.api.feature 2 | 3 | @Target(AnnotationTarget.FIELD) 4 | @Retention(AnnotationRetention.RUNTIME) 5 | annotation class Configurable -------------------------------------------------------------------------------- /sayanvanish-api/src/main/kotlin/org/sayandev/sayanvanish/api/feature/Feature.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.api.feature 2 | 3 | import org.sayandev.sayanvanish.api.BasicUser 4 | import org.sayandev.sayanvanish.api.Platform 5 | import org.sayandev.sayanvanish.api.feature.category.FeatureCategories 6 | import org.sayandev.stickynote.core.configuration.Config 7 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 8 | import org.spongepowered.configurate.serialize.TypeSerializerCollection 9 | import java.io.File 10 | 11 | @ConfigSerializable 12 | abstract class Feature( 13 | val id: String, 14 | open var enabled: Boolean = true, 15 | @Transient val category: FeatureCategories = FeatureCategories.DEFAULT, 16 | @Transient val additionalSerializers: TypeSerializerCollection = TypeSerializerCollection.defaults(), 17 | @Transient val critical: Boolean = false 18 | ) : Config( 19 | directory(category), 20 | "${id}.yml", 21 | additionalSerializers 22 | ) { 23 | @Transient open var condition: Boolean = true 24 | 25 | open fun isActive(): Boolean { 26 | return enabled && condition 27 | } 28 | 29 | open fun isActive(user: BasicUser): Boolean { 30 | return !user.hasPermission("sayanvanish.feature.disable.${id}") && Features.userFeatures(user).find { it.id == this.id }?.enabled != false && isActive() 31 | } 32 | 33 | open fun enable() { 34 | enabled = true 35 | } 36 | 37 | fun disable() { 38 | disable(false) 39 | } 40 | 41 | open fun disable(reload: Boolean) { 42 | if (critical) { 43 | onCriticalDisabled(reload) 44 | } 45 | enabled = false 46 | } 47 | 48 | fun toggle() { 49 | if (enabled && condition) { 50 | disable() 51 | } else { 52 | enable() 53 | } 54 | } 55 | 56 | fun onCriticalDisabled() { 57 | onCriticalDisabled(false) 58 | } 59 | 60 | fun onCriticalDisabled(reload: Boolean) { 61 | if (!reload) { 62 | Platform.get().logger.warning("the feature '$id' is critical and currently disabled. We strongly recommend re-enabling it to avoid potential unexpected behavior. (path: ${directory(category).path}/${id}.yml)") 63 | } 64 | } 65 | 66 | fun loadAndRegister() { 67 | loadAndRegister(this) 68 | } 69 | 70 | companion object { 71 | fun directory(category: FeatureCategories) = 72 | when (category.directory) { 73 | null -> { 74 | File(Platform.get().rootDirectory, "features") 75 | } 76 | else -> { 77 | File(File(Platform.get().rootDirectory, "features"), category.directory) 78 | } 79 | } 80 | 81 | fun createFromConfig(type: Class): Feature { 82 | val freshInstance = type.getDeclaredConstructor().newInstance() 83 | return createFromInstance(freshInstance) 84 | } 85 | 86 | fun createFromInstance(feature: Feature): Feature { 87 | val category = feature.category 88 | val instance = getConfigFromFile(File( 89 | if (category.directory == null) { 90 | File(Platform.get().rootDirectory, "features") 91 | } else { 92 | File(File(Platform.get().rootDirectory, "features"), category.directory) 93 | }, "${feature.id}.yml"), 94 | feature.additionalSerializers)?.get(feature::class.java) ?: feature 95 | if (instance.enabled) { 96 | instance.enable() 97 | } else { 98 | instance.disable() 99 | } 100 | return instance 101 | } 102 | 103 | @JvmStatic 104 | fun loadAndRegister(feature: Feature) { 105 | createFromInstance(feature) 106 | feature.save() 107 | Features.addFeature(feature) 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /sayanvanish-api/src/main/kotlin/org/sayandev/sayanvanish/api/feature/Features.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.api.feature 2 | 3 | import org.sayandev.sayanvanish.api.BasicUser 4 | import java.util.UUID 5 | 6 | object Features { 7 | val features = mutableListOf() 8 | val userFeatures = mutableMapOf>() 9 | 10 | @JvmStatic 11 | inline fun getFeature(): T { 12 | return features.find { it is T } as T 13 | } 14 | 15 | @JvmStatic 16 | inline fun getUserFeature(uniqueId: UUID): T { 17 | return userFeatures[uniqueId] as T 18 | } 19 | 20 | @JvmStatic 21 | inline fun getUserFeature(user: BasicUser): T { 22 | return getUserFeature(user.uniqueId) 23 | } 24 | 25 | @JvmStatic 26 | fun addFeature(feature: Feature) { 27 | features.add(feature) 28 | } 29 | 30 | @JvmStatic 31 | fun features(): List { 32 | return features 33 | } 34 | 35 | fun userFeatures(user: BasicUser): List { 36 | return userFeatures.getOrPut(user.uniqueId) { features() } 37 | } 38 | 39 | } -------------------------------------------------------------------------------- /sayanvanish-api/src/main/kotlin/org/sayandev/sayanvanish/api/feature/RegisteredFeature.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.api.feature 2 | 3 | @Target(AnnotationTarget.CLASS) 4 | @Retention(AnnotationRetention.RUNTIME) 5 | annotation class RegisteredFeature -------------------------------------------------------------------------------- /sayanvanish-api/src/main/kotlin/org/sayandev/sayanvanish/api/feature/RegisteredFeatureHandler.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.api.feature 2 | 3 | import org.reflections.Reflections 4 | import org.sayandev.sayanvanish.api.Platform 5 | import java.io.IOException 6 | import java.net.URL 7 | import java.security.CodeSource 8 | import java.util.* 9 | import java.util.jar.JarEntry 10 | import java.util.jar.JarFile 11 | import kotlin.collections.ArrayList 12 | 13 | 14 | object RegisteredFeatureHandler { 15 | 16 | fun process() { 17 | val reflections = Reflections("org.sayandev.sayanvanish") 18 | val annotatedClasses = if (reflections.getTypesAnnotatedWith(RegisteredFeature::class.java).isEmpty()) { 19 | Platform.get().logger.warning("Couldn't load plugin features in your current server software, trying alternative method...") 20 | getClassesInPackage(Platform.get(), "org.sayandev.sayanvanish") 21 | } else { 22 | reflections.getTypesAnnotatedWith(RegisteredFeature::class.java) 23 | } 24 | 25 | Platform.get().logger.info("Found ${annotatedClasses.size} features.") 26 | for (annotatedClass in annotatedClasses) { 27 | createNewInstance(annotatedClass) 28 | } 29 | Platform.get().logger.info("Enabled ${Features.features.filter { it.isActive() }.size} features.") 30 | } 31 | 32 | private fun createNewInstance(clazz: Class<*>) { 33 | try { 34 | if (Features.features.map { it.javaClass }.contains(clazz)) return 35 | val instance = Feature.createFromConfig(clazz as Class) 36 | instance.save() 37 | Features.addFeature(instance) 38 | } catch (e: NoClassDefFoundError) { 39 | Platform.get().logger.warning("Couldn't enable feature ${clazz.simpleName} on your server software/version.") 40 | } catch (e: Exception) { 41 | e.printStackTrace() 42 | } 43 | } 44 | 45 | fun getClassesInPackage(plugin: Any, packageName: String): Collection> { 46 | val classes: MutableCollection> = ArrayList() 47 | val codeSource: CodeSource = plugin.javaClass.protectionDomain.codeSource 48 | val resource: URL = codeSource.location 49 | val relPath = packageName.replace('.', '/') 50 | val resPath = resource.path.replace("%20", " ") 51 | val jarPath = resPath.replaceFirst("[.]jar[!].*".toRegex(), ".jar").replaceFirst("file:", "") 52 | 53 | val jarFile: JarFile = try { 54 | JarFile(jarPath) 55 | } catch (e: IOException) { 56 | Platform.get().logger.severe("Tried to find plugin jar file to load features, but couldn't find it. Your server software doesn't support this behavior.") 57 | return emptyList() 58 | } 59 | 60 | val entries: Enumeration = jarFile.entries() 61 | 62 | while (entries.hasMoreElements()) { 63 | val entry: JarEntry = entries.nextElement() 64 | val entryName: String = entry.name 65 | if (entryName.endsWith(".class") && entryName.startsWith(relPath) && entryName.length > relPath.length + 1) { 66 | val className = entryName.replace('/', '.').replace('\\', '.').replace(".class", "") 67 | try { 68 | val clazz = plugin.javaClass.classLoader.loadClass(className) 69 | if (clazz.isAnnotationPresent(RegisteredFeature::class.java)) { 70 | classes.add(clazz) 71 | } 72 | } catch (_: NoClassDefFoundError) { 73 | } catch (_: ClassNotFoundException) { } 74 | } 75 | } 76 | 77 | try { 78 | jarFile.close() 79 | } catch (e: IOException) { 80 | e.printStackTrace() 81 | } 82 | 83 | return classes 84 | } 85 | 86 | } -------------------------------------------------------------------------------- /sayanvanish-api/src/main/kotlin/org/sayandev/sayanvanish/api/feature/category/FeatureCategories.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.api.feature.category 2 | 3 | enum class FeatureCategories(override val directory: String?) : FeatureCategory { 4 | HOOK("hooks"), 5 | PREVENTION("preventions"), 6 | PROXY("proxy"), 7 | DEFAULT(null), 8 | CUSTOM("custom") 9 | } -------------------------------------------------------------------------------- /sayanvanish-api/src/main/kotlin/org/sayandev/sayanvanish/api/feature/category/FeatureCategory.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.api.feature.category 2 | 3 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 4 | 5 | @ConfigSerializable 6 | interface FeatureCategory { 7 | 8 | val directory: String? 9 | } -------------------------------------------------------------------------------- /sayanvanish-api/src/main/kotlin/org/sayandev/sayanvanish/api/health/HealthCheckData.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.api.health 2 | 3 | import org.sayandev.sayanvanish.api.database.DatabaseMethod 4 | import org.sayandev.sayanvanish.api.database.sql.SQLConfig 5 | 6 | data class HealthCheckData( 7 | val proxy: ProxyInfo, 8 | val servers: Servers, 9 | ) { 10 | 11 | data class ProxyInfo( 12 | val databaseMethod: DatabaseMethod, 13 | val sqlMethod: SQLConfig.SQLMethod, 14 | ) 15 | 16 | data class ServerInfo( 17 | val id: String?, 18 | var name: String?, 19 | val enabledProxyMode: Boolean, 20 | val databaseMethod: DatabaseMethod, 21 | val sqlMethod: SQLConfig.SQLMethod, 22 | val timestamp: Long 23 | ) 24 | 25 | data class Servers( 26 | val servers: MutableList 27 | ) 28 | 29 | } -------------------------------------------------------------------------------- /sayanvanish-api/src/main/kotlin/org/sayandev/sayanvanish/api/utils/DownloadUtils.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.api.utils 2 | 3 | import java.io.BufferedInputStream 4 | import java.io.File 5 | import java.io.FileOutputStream 6 | import java.net.HttpURLConnection 7 | import java.net.URL 8 | import java.util.concurrent.CompletableFuture 9 | 10 | object DownloadUtils { 11 | 12 | fun download(url: String, destination: File): CompletableFuture { 13 | val future = CompletableFuture() 14 | 15 | val url = URL(url) 16 | val connection = url.openConnection() as HttpURLConnection 17 | connection.requestMethod = "GET" 18 | connection.connectTimeout = 5000 19 | connection.readTimeout = 5000 20 | 21 | if (connection.responseCode == HttpURLConnection.HTTP_OK) { 22 | val inputStream = BufferedInputStream(connection.inputStream) 23 | val outputStream = FileOutputStream(destination) 24 | 25 | val buffer = ByteArray(1024) 26 | var bytesRead = inputStream.read(buffer) 27 | 28 | while (bytesRead != -1) { 29 | outputStream.write(buffer, 0, bytesRead) 30 | bytesRead = inputStream.read(buffer) 31 | } 32 | 33 | outputStream.close() 34 | inputStream.close() 35 | 36 | future.complete(true) 37 | } else { 38 | future.complete(false) 39 | } 40 | connection.disconnect() 41 | 42 | return future 43 | } 44 | } -------------------------------------------------------------------------------- /sayanvanish-api/src/main/kotlin/org/sayandev/sayanvanish/api/utils/HangarUtils.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.api.utils 2 | 3 | import com.google.gson.Gson 4 | import com.google.gson.annotations.SerializedName 5 | import java.io.BufferedReader 6 | import java.io.InputStreamReader 7 | import java.net.HttpURLConnection 8 | import java.net.URL 9 | import java.util.concurrent.CompletableFuture 10 | 11 | object HangarUtils { 12 | private val gson = Gson() 13 | 14 | fun getLatestRelease(): CompletableFuture { 15 | return get("https://hangar.papermc.io/api/v1/projects/sayanvanish/latestrelease") 16 | } 17 | 18 | fun getLatestSnapshot(): CompletableFuture { 19 | return get("https://hangar.papermc.io/api/v1/projects/sayanvanish/latest?channel=Snapshot") 20 | } 21 | 22 | private fun get(urlString: String): CompletableFuture { 23 | val future = CompletableFuture() 24 | 25 | CompletableFuture.runAsync { 26 | val url = URL(urlString) 27 | val connection = url.openConnection() as HttpURLConnection 28 | connection.requestMethod = "GET" 29 | connection.setRequestProperty("User-Agent", USER_AGENT) 30 | connection.setRequestProperty("Accept", "text/plain") 31 | 32 | val version = BufferedReader(InputStreamReader(connection.inputStream)).use { it.readLine() } 33 | 34 | val versionUrl = URL("$HANGAR_API_URL/$version") 35 | val versionConnection = versionUrl.openConnection() as HttpURLConnection 36 | versionConnection.requestMethod = "GET" 37 | versionConnection.setRequestProperty("User-Agent", USER_AGENT) 38 | versionConnection.setRequestProperty("Accept", "application/json") 39 | 40 | val versionInfo = BufferedReader(InputStreamReader(versionConnection.inputStream)).use { reader -> 41 | gson.fromJson(reader, VersionInfo::class.java)!! 42 | } 43 | 44 | 45 | future.complete(versionInfo) 46 | } 47 | 48 | return future 49 | } 50 | 51 | private const val HANGAR_API_URL = "https://hangar.papermc.io/api/v1/projects/sayanvanish/versions" 52 | private const val USER_AGENT: String = "Mozilla/5.0" 53 | } 54 | 55 | data class VersionInfo( 56 | val name: String, 57 | val description: String, 58 | val downloads: Downloads 59 | ) 60 | 61 | data class Downloads( 62 | @SerializedName("PAPER") val PAPER: PlatformInfo?, 63 | @SerializedName("WATERFALL") val WATERFALL: PlatformInfo?, 64 | @SerializedName("VELOCITY") val VELOCITY: PlatformInfo? 65 | ) 66 | 67 | data class PlatformInfo( 68 | val externalUrl: String?, 69 | @SerializedName("downloadUrl") val downloadUrl: String? 70 | ) { 71 | fun downloadUrl(): String { 72 | return externalUrl ?: downloadUrl ?: "" 73 | } 74 | } -------------------------------------------------------------------------------- /sayanvanish-api/src/main/kotlin/org/sayandev/sayanvanish/api/utils/Paste.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.api.utils 2 | 3 | import com.google.gson.JsonParser 4 | import java.io.BufferedReader 5 | import java.io.IOException 6 | import java.io.InputStreamReader 7 | import java.net.HttpURLConnection 8 | import java.net.URL 9 | import java.nio.charset.StandardCharsets 10 | import java.util.concurrent.CompletableFuture 11 | 12 | 13 | class Paste( 14 | val language: String, 15 | val content: List, 16 | ) { 17 | 18 | fun post(): CompletableFuture { 19 | val future = CompletableFuture() 20 | 21 | val url = URL(POST_URL) 22 | val connection = url.openConnection() as HttpURLConnection 23 | connection.setRequestMethod("POST") 24 | connection.setRequestProperty("User-Agent", USER_AGENT) 25 | connection.setRequestProperty("Content-Type", "text/$language") 26 | connection.setDoOutput(true) 27 | connection.connectTimeout = 10000 28 | 29 | connection.outputStream.use { os -> 30 | val input: ByteArray = content.joinToString("\n").byteInputStream(StandardCharsets.UTF_8).readAllBytes() 31 | os.write(input, 0, input.size) 32 | } 33 | 34 | val responseCode: Int = connection.getResponseCode() 35 | if (responseCode == HttpURLConnection.HTTP_OK || responseCode == HttpURLConnection.HTTP_CREATED) { 36 | BufferedReader(InputStreamReader(connection.inputStream, StandardCharsets.UTF_8)).use { br -> 37 | var inputLine: String? 38 | val response = StringBuilder() 39 | 40 | while ((br.readLine().also { inputLine = it }) != null) { 41 | response.append(inputLine) 42 | } 43 | 44 | val key = JsonParser.parseString(response.toString()).asJsonObject.get("key").asString 45 | 46 | future.complete(key) 47 | return future 48 | } 49 | } else { 50 | throw IOException("Failed to upload content, HTTP response code: $responseCode") 51 | } 52 | } 53 | 54 | companion object { 55 | private const val BASE_URL = "https://api.pastes.dev" 56 | private const val POST_URL = "$BASE_URL/post" 57 | private const val USER_AGENT: String = "Mozilla/5.0" 58 | 59 | const val PASTE_URL = "https://pastes.dev" 60 | } 61 | 62 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/SayanVanish.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit 2 | 3 | import org.bukkit.Bukkit 4 | import org.bukkit.plugin.java.JavaPlugin 5 | import org.sayandev.sayanvanish.api.Platform 6 | import org.sayandev.sayanvanish.api.SayanVanishAPI 7 | import org.sayandev.sayanvanish.api.database.DatabaseMethod 8 | import org.sayandev.sayanvanish.api.database.databaseConfig 9 | import org.sayandev.sayanvanish.api.database.sql.SQLConfig 10 | import org.sayandev.sayanvanish.api.database.sql.SQLDatabase 11 | import org.sayandev.sayanvanish.bukkit.api.Metrics 12 | import org.sayandev.sayanvanish.bukkit.api.SayanVanishBukkitAPI 13 | import org.sayandev.sayanvanish.bukkit.command.SayanVanishCommand 14 | import org.sayandev.sayanvanish.bukkit.config.language 15 | import org.sayandev.sayanvanish.bukkit.config.settings 16 | import org.sayandev.sayanvanish.bukkit.health.HealthCheckRequestPublisher 17 | import org.sayandev.sayanvanish.bukkit.health.ServerInfoPublisher 18 | import org.sayandev.stickynote.bukkit.StickyNote 19 | import org.sayandev.stickynote.bukkit.error 20 | import org.sayandev.stickynote.bukkit.pluginDirectory 21 | import org.sayandev.stickynote.bukkit.runAsync 22 | import org.sayandev.stickynote.loader.bukkit.StickyNoteBukkitLoader 23 | import java.io.File 24 | 25 | public lateinit var sayanvanish: SayanVanish 26 | 27 | open class SayanVanish : JavaPlugin() { 28 | 29 | override fun onEnable() { 30 | StickyNoteBukkitLoader(this) 31 | sayanvanish = this 32 | 33 | if (!Platform.setAndRegister(Platform("bukkit", logger, pluginDirectory, settings.general.serverId))) { 34 | Bukkit.getPluginManager().disablePlugin(this) 35 | return 36 | } 37 | 38 | SayanVanishBukkitAPI() 39 | 40 | HealthCheckRequestPublisher 41 | ServerInfoPublisher 42 | 43 | settings 44 | if (settings.general.proxyMode && databaseConfig.method == DatabaseMethod.SQL && databaseConfig.sql.method == SQLConfig.SQLMethod.SQLITE) { 45 | error("The `proxy-mode` is enabled, but the database method is set to SQLite, which might lead to unexpected results. If you're using proxies such as Velocity or BungeeCord, make sure to use a different database method, such as MySQL or Redis.") 46 | } 47 | 48 | language 49 | 50 | VanishManager 51 | 52 | SayanVanishCommand() 53 | 54 | runAsync({ 55 | SayanVanishBukkitAPI.getInstance().database.getUsersAsync { users -> 56 | SayanVanishBukkitAPI.getInstance().database.cache = users.associateBy { it.uniqueId }.toMutableMap() 57 | SayanVanishAPI.getInstance().database.cache = users.associateBy { it.uniqueId }.toMutableMap() 58 | } 59 | }, 0, settings.general.cacheUpdatePeriodTicks) 60 | 61 | runAsync({ 62 | if (databaseConfig.method == DatabaseMethod.SQL) { 63 | SayanVanishBukkitAPI.getInstance().database.getBasicUsersAsync { users -> 64 | (SayanVanishBukkitAPI.getInstance().database as SQLDatabase).basicCache = users.associateBy { it.uniqueId }.toMutableMap() 65 | (SayanVanishAPI.getInstance().database as SQLDatabase).basicCache = users.associateBy { it.uniqueId }.toMutableMap() 66 | } 67 | } 68 | }, 0, settings.general.basicCacheUpdatePeriodTicks) 69 | 70 | if (settings.general.bstats) { 71 | Metrics(this, 23914).apply { 72 | this.addCustomChart(Metrics.SingleLineChart("vanished") { 73 | SayanVanishBukkitAPI.getInstance().getVanishedUsers().count() 74 | }) 75 | this.addCustomChart(Metrics.SimplePie("proxied") { 76 | if (settings.general.proxyMode) "On Proxy" else "No Proxy" 77 | }) 78 | this.addCustomChart(Metrics.SimplePie("database_method") { 79 | databaseConfig.method.name 80 | }) 81 | } 82 | } 83 | } 84 | 85 | override fun onDisable() { 86 | SayanVanishBukkitAPI.getInstance().database.disconnect() 87 | StickyNote.shutdown() 88 | } 89 | 90 | fun pluginFile(): File { 91 | return this.file 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/VanishManager.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit 2 | 3 | import org.bukkit.event.EventHandler 4 | import org.bukkit.event.EventPriority 5 | import org.bukkit.event.Listener 6 | import org.bukkit.event.player.PlayerJoinEvent 7 | import org.bukkit.event.player.PlayerQuitEvent 8 | import org.sayandev.sayanvanish.api.BasicUser 9 | import org.sayandev.sayanvanish.api.SayanVanishAPI 10 | import org.sayandev.sayanvanish.bukkit.api.SayanVanishBukkitAPI 11 | import org.sayandev.sayanvanish.bukkit.config.settings 12 | import org.sayandev.stickynote.bukkit.registerListener 13 | 14 | object VanishManager : Listener { 15 | 16 | init { 17 | registerListener(this) 18 | } 19 | 20 | @EventHandler 21 | private fun addBasicUserOnJoin(event: PlayerJoinEvent) { 22 | if (settings.general.proxyMode) return 23 | 24 | val player = event.player 25 | SayanVanishAPI.getInstance().database.addBasicUser(BasicUser.create(player.uniqueId, player.name, null)) 26 | } 27 | 28 | @EventHandler 29 | private fun removeBasicUserOnQuit(event: PlayerQuitEvent) { 30 | if (settings.general.proxyMode) return 31 | 32 | val player = event.player 33 | SayanVanishAPI.getInstance().database.cache.remove(player.uniqueId) 34 | SayanVanishAPI.getInstance().database.removeBasicUser(player.uniqueId) 35 | } 36 | 37 | @EventHandler(priority = EventPriority.LOWEST) 38 | private fun hideVanishedPlayersOnJoin(event: PlayerJoinEvent) { 39 | for (user in SayanVanishBukkitAPI.getInstance().database.getUsers().filter { it.isVanished && it.player() != null }) { 40 | user.hideUser(event.player) 41 | } 42 | } 43 | 44 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/api/SayanVanishBukkitAPI.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.api 2 | 3 | import org.bukkit.OfflinePlayer 4 | import org.bukkit.entity.Player 5 | import org.sayandev.sayanvanish.api.SayanVanishAPI 6 | import org.sayandev.sayanvanish.api.database.databaseConfig 7 | import org.sayandev.sayanvanish.bukkit.config.settings 8 | import java.util.* 9 | 10 | class SayanVanishBukkitAPI : SayanVanishAPI(BukkitUser::class.java) { 11 | 12 | fun canSee(player: Player?, otherPlayer: Player): Boolean { 13 | val vanishLevel = player?.getOrCreateUser()?.vanishLevel ?: -1 14 | return vanishLevel >= (otherPlayer.user()?.vanishLevel ?: -1) 15 | } 16 | 17 | companion object { 18 | private val defaultInstance = SayanVanishBukkitAPI() 19 | 20 | @JvmStatic 21 | fun getInstance(): SayanVanishAPI { 22 | return defaultInstance 23 | } 24 | 25 | @JvmStatic 26 | fun UUID.bukkitUser(): BukkitUser? { 27 | return getInstance().getUser(this) 28 | } 29 | 30 | @JvmStatic 31 | fun OfflinePlayer.user(): BukkitUser? { 32 | return getInstance().database.getUser(this.uniqueId) 33 | } 34 | 35 | @JvmStatic 36 | fun OfflinePlayer.getOrCreateUser(): BukkitUser { 37 | return getInstance().getUser(this.uniqueId) ?: BukkitUser(this.uniqueId, this.name ?: "N/A") 38 | } 39 | 40 | @JvmStatic 41 | fun OfflinePlayer.getOrAddUser(): BukkitUser { 42 | return getInstance().getUser(this.uniqueId) ?: let { 43 | val newUser = BukkitUser(this.uniqueId, this.name ?: "N/A") 44 | getInstance().database.addUser(newUser) 45 | newUser 46 | } 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/api/event/BukkitUserUnVanishEvent.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.api.event 2 | 3 | import org.bukkit.event.Cancellable 4 | import org.bukkit.event.Event 5 | import org.bukkit.event.HandlerList 6 | import org.sayandev.sayanvanish.api.VanishOptions 7 | import org.sayandev.sayanvanish.bukkit.api.BukkitUser 8 | 9 | class BukkitUserUnVanishEvent( 10 | val user: BukkitUser, 11 | val options: VanishOptions, 12 | ) : Event(), Cancellable { 13 | 14 | private var cancelled = false 15 | 16 | override fun getHandlers(): HandlerList { 17 | return HANDLERS 18 | } 19 | 20 | companion object { 21 | @JvmStatic 22 | private val HANDLERS: HandlerList = HandlerList() 23 | 24 | @JvmStatic 25 | fun getHandlerList(): HandlerList { 26 | return HANDLERS 27 | } 28 | } 29 | 30 | override fun isCancelled(): Boolean { 31 | return cancelled 32 | } 33 | 34 | override fun setCancelled(cancel: Boolean) { 35 | this.cancelled = cancel 36 | } 37 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/api/event/BukkitUserVanishEvent.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.api.event 2 | 3 | import org.bukkit.event.Cancellable 4 | import org.bukkit.event.Event 5 | import org.bukkit.event.HandlerList 6 | import org.sayandev.sayanvanish.api.VanishOptions 7 | import org.sayandev.sayanvanish.bukkit.api.BukkitUser 8 | 9 | class BukkitUserVanishEvent( 10 | val user: BukkitUser, 11 | val options: VanishOptions, 12 | ) : Event(), Cancellable { 13 | 14 | private var cancelled = false 15 | 16 | override fun getHandlers(): HandlerList { 17 | return HANDLERS 18 | } 19 | 20 | companion object { 21 | @JvmStatic 22 | private val HANDLERS: HandlerList = HandlerList() 23 | 24 | @JvmStatic 25 | fun getHandlerList(): HandlerList { 26 | return HANDLERS 27 | } 28 | } 29 | 30 | override fun isCancelled(): Boolean { 31 | return cancelled 32 | } 33 | 34 | override fun setCancelled(cancel: Boolean) { 35 | this.cancelled = cancel 36 | } 37 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/config/SettingsConfig.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.config 2 | 3 | import org.sayandev.sayanvanish.api.Platform 4 | import org.sayandev.stickynote.bukkit.pluginDirectory 5 | import org.sayandev.stickynote.core.configuration.Config 6 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 7 | import org.spongepowered.configurate.objectmapping.meta.Comment 8 | import java.io.File 9 | import java.util.* 10 | 11 | public var settings: SettingsConfig = SettingsConfig.fromConfig() ?: SettingsConfig.defaultConfig() 12 | 13 | @ConfigSerializable 14 | class SettingsConfig( 15 | @Comment(""" 16 | Do NOT copy and paste the SayanVanish directory across multiple servers. 17 | The server-id is generated during the plugin's first startup. 18 | Duplicating this file could lead to synchronization issues. 19 | 20 | General settings for the plugin 21 | """) 22 | val general: General = General(), 23 | @Comment("Command settings for the plugin") 24 | val vanishCommand: Command = Command(), 25 | ) : Config( 26 | pluginDirectory, 27 | fileName, 28 | ) { 29 | 30 | @ConfigSerializable 31 | data class General( 32 | @Comment("Unique server identifier. used for server identification if proxy mode is not enabled!") 33 | val serverId: String = "${Platform.get().id}-${UUID.randomUUID()}", 34 | @Comment(""" 35 | Language name 36 | Note: By default, it only includes the `en_US` language. 37 | However, you can create and add your own custom languages. 38 | """) 39 | val language: String = LanguageConfig.Language.EN_US.id, 40 | @Comment("Whether to include prefix in messages, can be found in the language file.") 41 | val includePrefixInMessages: Boolean = true, 42 | @Comment("Enable or disable bStats metrics") 43 | val bstats: Boolean = true, 44 | @Comment(""" 45 | If you want to synchronize the vanish status of players across multiple servers, enable this. 46 | You will also need to install the SayanVanish proxy plugin on your proxy server. 47 | WARNING: You need to use MySQL or Redis as the database for this feature to work properly. 48 | """) 49 | val proxyMode: Boolean = false, 50 | @Comment("Cache update period in ticks. low values may cause performance issues.") 51 | val cacheUpdatePeriodTicks: Long = 20, 52 | @Comment("Basic cache update period in ticks. low values may cause performance issues.") 53 | val basicCacheUpdatePeriodTicks: Long = 20, 54 | ) 55 | 56 | @ConfigSerializable 57 | data class Command( 58 | @Comment("Name of the main command") 59 | val name: String = "vanish", 60 | @Comment("Aliases for the main command") 61 | val aliases: List = listOf( 62 | "v", 63 | "sayanvanish", 64 | "sv" 65 | ) 66 | ) 67 | 68 | companion object { 69 | private val fileName = "settings.yml" 70 | val settingsFile = File(pluginDirectory, fileName) 71 | 72 | @JvmStatic 73 | fun defaultConfig(): SettingsConfig { 74 | return SettingsConfig().also { it.save() } 75 | } 76 | 77 | @JvmStatic 78 | fun fromConfig(): SettingsConfig? { 79 | return fromConfig(settingsFile) 80 | } 81 | } 82 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/feature/HookFeature.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.feature 2 | 3 | import org.sayandev.sayanvanish.api.BasicUser 4 | import org.sayandev.sayanvanish.api.feature.category.FeatureCategories 5 | import org.sayandev.stickynote.bukkit.hasPlugin 6 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 7 | 8 | @ConfigSerializable 9 | abstract class HookFeature( 10 | id: String, 11 | @Transient val plugin: String, 12 | enabled: Boolean = true, 13 | category: FeatureCategories = FeatureCategories.HOOK, 14 | ) : ListenedFeature(id, enabled, category) { 15 | 16 | fun hasPlugin(): Boolean { 17 | return hasPlugin(plugin) 18 | } 19 | 20 | override fun isActive(): Boolean { 21 | return super.isActive() && hasPlugin() 22 | } 23 | 24 | override fun isActive(user: BasicUser): Boolean { 25 | return super.isActive(user) && hasPlugin() 26 | } 27 | 28 | 29 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/feature/ListenedFeature.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.feature 2 | 3 | import org.bukkit.event.Listener 4 | import org.sayandev.sayanvanish.api.feature.Feature 5 | import org.sayandev.sayanvanish.api.feature.category.FeatureCategories 6 | import org.sayandev.stickynote.bukkit.registerListener 7 | import org.sayandev.stickynote.bukkit.unregisterListener 8 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 9 | import org.spongepowered.configurate.serialize.TypeSerializerCollection 10 | 11 | @ConfigSerializable 12 | abstract class ListenedFeature( 13 | id: String, 14 | enabled: Boolean = true, 15 | category: FeatureCategories = FeatureCategories.DEFAULT, 16 | additionalSerializers: TypeSerializerCollection = TypeSerializerCollection.defaults(), 17 | critical: Boolean = false 18 | ) : Feature(id, enabled, category, additionalSerializers, critical), Listener { 19 | 20 | override fun enable() { 21 | if (!condition) return 22 | registerListener(this) 23 | super.enable() 24 | } 25 | 26 | override fun disable(reload: Boolean) { 27 | unregisterListener(this) 28 | super.disable(reload) 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/feature/features/FeatureActionbar.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.feature.features 2 | 3 | import org.bukkit.event.EventHandler 4 | import org.sayandev.sayanvanish.api.Permission 5 | import org.sayandev.sayanvanish.api.feature.Configurable 6 | import org.sayandev.sayanvanish.api.feature.RegisteredFeature 7 | import org.sayandev.sayanvanish.bukkit.api.SayanVanishBukkitAPI.Companion.user 8 | import org.sayandev.sayanvanish.bukkit.api.event.BukkitUserUnVanishEvent 9 | import org.sayandev.sayanvanish.bukkit.api.event.BukkitUserVanishEvent 10 | import org.sayandev.sayanvanish.bukkit.feature.ListenedFeature 11 | import org.sayandev.stickynote.bukkit.StickyNote.runSync 12 | import org.sayandev.stickynote.bukkit.onlinePlayers 13 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 14 | import org.spongepowered.configurate.objectmapping.meta.Comment 15 | 16 | @RegisteredFeature 17 | @ConfigSerializable 18 | class FeatureActionbar( 19 | @Comment("The content of the actionbar message.") 20 | @Configurable val content: String = "You are currently vanished!", 21 | @Comment("The delay before the actionbar message is sent. doesn't really matter.") 22 | @Configurable val delay: Long = 20, 23 | @Comment("The period between each actionbar message. values higher than 40 will make it not always visible.") 24 | @Configurable val period: Long = 20, 25 | ) : ListenedFeature("actionbar") { 26 | 27 | @EventHandler 28 | private fun onVanish(event: BukkitUserVanishEvent) { 29 | val user = event.user 30 | if (!isActive(user)) return 31 | user.sendActionbar(content) 32 | } 33 | 34 | @EventHandler 35 | private fun onUnVanish(event: BukkitUserUnVanishEvent) { 36 | val user = event.user 37 | if (!isActive(user)) return 38 | user.sendActionbar("") 39 | } 40 | 41 | override fun enable() { 42 | runSync({ 43 | for (user in onlinePlayers.filter { it.hasPermission(Permission.VANISH.permission()) }.mapNotNull { it.user() }.filter { it.isVanished }) { 44 | if (!isActive(user)) continue 45 | user.sendActionbar(content) 46 | } 47 | }, delay, period) 48 | super.enable() 49 | } 50 | 51 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/feature/features/FeatureEffect.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.feature.features 2 | 3 | import org.bukkit.event.EventHandler 4 | import org.bukkit.potion.PotionEffect 5 | import org.bukkit.potion.PotionEffectType 6 | import org.sayandev.sayanvanish.api.feature.RegisteredFeature 7 | import org.sayandev.sayanvanish.bukkit.api.event.BukkitUserUnVanishEvent 8 | import org.sayandev.sayanvanish.bukkit.api.event.BukkitUserVanishEvent 9 | import org.sayandev.sayanvanish.bukkit.feature.ListenedFeature 10 | import org.sayandev.stickynote.bukkit.nms.NMSUtils.sendPacket 11 | import org.sayandev.stickynote.bukkit.nms.PacketUtils 12 | import org.sayandev.stickynote.bukkit.utils.ServerVersion 13 | import org.spongepowered.configurate.ConfigurationNode 14 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 15 | import org.spongepowered.configurate.objectmapping.meta.Comment 16 | import org.spongepowered.configurate.serialize.TypeSerializer 17 | import org.spongepowered.configurate.serialize.TypeSerializerCollection 18 | import java.lang.reflect.Type 19 | 20 | @RegisteredFeature 21 | @ConfigSerializable 22 | @Suppress("DEPRECATION") 23 | data class FeatureEffect( 24 | @Comment(""" 25 | All effects will being sent using packets to prevent conflict with other plugins or desyncs. 26 | List of effects to apply when a player vanishes 27 | """) 28 | val effects: List = listOf( 29 | PotionEffectData( 30 | ServerVersion.supports(9), 31 | false, 32 | PotionEffectType.NIGHT_VISION.name, 33 | Int.MAX_VALUE, 34 | 0, 35 | false, 36 | false, 37 | ), 38 | PotionEffectData( 39 | false, 40 | false, 41 | PotionEffectType.WATER_BREATHING.name, 42 | Int.MAX_VALUE, 43 | 0, 44 | false, 45 | false, 46 | ), 47 | PotionEffectData( 48 | false, 49 | false, 50 | PotionEffectType.FIRE_RESISTANCE.name, 51 | Int.MAX_VALUE, 52 | 0, 53 | false, 54 | false, 55 | ) 56 | ) 57 | ) : ListenedFeature("effect", additionalSerializers = TypeSerializerCollection.builder().register(PotionEffectType::class.java, PotionEffectTypeSerializer()).build()) { 58 | 59 | @EventHandler 60 | private fun onVanish(event: BukkitUserVanishEvent) { 61 | val user = event.user 62 | if (!isActive(user)) return 63 | val player = user.player() ?: return 64 | for (effect in effects) { 65 | if (effect.usePacket) { 66 | player.sendPacket(PacketUtils.getUpdateMobEffectPacket(player, effect.toPotionEffect())) 67 | } else { 68 | player.addPotionEffect(effect.toPotionEffect()) 69 | } 70 | } 71 | } 72 | 73 | @EventHandler 74 | private fun onUnVanish(event: BukkitUserUnVanishEvent) { 75 | val user = event.user 76 | if (!isActive(user)) return 77 | val player = user.player() ?: return 78 | for (effect in effects.filter { !it.keepAfterAppear }) { 79 | if (effect.usePacket) { 80 | player.sendPacket(PacketUtils.getRemoveMobEffectPacket(player, PotionEffectType.getByName(effect.type)!!)) 81 | } else { 82 | if (player.activePotionEffects.find { it.type.name == effect.type && it.amplifier == effect.amplifier && it.isAmbient == effect.ambient && it.hasParticles() == effect.particles } != null) { 83 | player.removePotionEffect(PotionEffectType.getByName(effect.type)!!) 84 | } 85 | } 86 | } 87 | } 88 | 89 | } 90 | 91 | @ConfigSerializable 92 | class PotionEffectData( 93 | val usePacket: Boolean = true, 94 | val keepAfterAppear: Boolean = false, 95 | val type: String = PotionEffectType.NIGHT_VISION.name, 96 | val duration: Int = Int.MAX_VALUE, 97 | val amplifier: Int = 0, 98 | val ambient: Boolean = false, 99 | val particles: Boolean = false, 100 | ) { 101 | fun toPotionEffect() = PotionEffect(PotionEffectType.getByName(type)!!, if (ServerVersion.supports(19) && duration == Int.MAX_VALUE) -1 else duration, amplifier, ambient, particles) 102 | } 103 | 104 | class PotionEffectTypeSerializer : TypeSerializer { 105 | override fun deserialize(type: Type, node: ConfigurationNode): PotionEffectType { 106 | return PotionEffectType.getByName(node.string!!)!! 107 | } 108 | 109 | override fun serialize(type: Type, effectType: PotionEffectType?, node: ConfigurationNode) { 110 | node.set(effectType?.name) 111 | } 112 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/feature/features/FeatureFly.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.feature.features 2 | 3 | import org.bukkit.event.EventHandler 4 | import org.sayandev.sayanvanish.api.Permission 5 | import org.sayandev.sayanvanish.api.feature.Configurable 6 | import org.sayandev.sayanvanish.api.feature.RegisteredFeature 7 | import org.sayandev.sayanvanish.bukkit.api.event.BukkitUserUnVanishEvent 8 | import org.sayandev.sayanvanish.bukkit.api.event.BukkitUserVanishEvent 9 | import org.sayandev.sayanvanish.bukkit.config.language 10 | import org.sayandev.sayanvanish.bukkit.feature.ListenedFeature 11 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 12 | import org.spongepowered.configurate.objectmapping.meta.Comment 13 | 14 | @RegisteredFeature 15 | @ConfigSerializable 16 | class FeatureFly( 17 | @Comment("Disable fly when player reappears and don't have keep fly permission.") 18 | @Configurable val disableOnReappear: Boolean = true 19 | ) : ListenedFeature("fly") { 20 | 21 | @EventHandler 22 | private fun onVanish(event: BukkitUserVanishEvent) { 23 | val user = event.user 24 | if (!isActive(user)) return 25 | if (user.hasPermission(Permission.FLY)) { 26 | user.player()?.allowFlight = true 27 | user.player()?.isFlying = true 28 | } 29 | } 30 | 31 | @EventHandler 32 | private fun onUnVanish(event: BukkitUserUnVanishEvent) { 33 | val user = event.user 34 | if (!isActive(user)) return 35 | if (!user.hasPermission(Permission.FLY_KEEP_AFTER_REAPPEAR) && disableOnReappear) { 36 | user.sendComponent(language.feature.flyDisabled) 37 | user.player()?.allowFlight = false 38 | user.player()?.isFlying = false 39 | } 40 | } 41 | 42 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/feature/features/FeatureGameMode.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.feature.features 2 | 3 | import org.bukkit.GameMode 4 | import org.bukkit.entity.Player 5 | import org.bukkit.event.EventHandler 6 | import org.bukkit.event.player.PlayerGameModeChangeEvent 7 | import org.bukkit.event.player.PlayerToggleSneakEvent 8 | import org.sayandev.sayanvanish.api.feature.RegisteredFeature 9 | import org.sayandev.sayanvanish.bukkit.feature.ListenedFeature 10 | import org.sayandev.stickynote.bukkit.StickyNote.runSync 11 | import org.sayandev.sayanvanish.bukkit.api.SayanVanishBukkitAPI.Companion.user 12 | import org.sayandev.sayanvanish.bukkit.api.event.BukkitUserUnVanishEvent 13 | import org.sayandev.sayanvanish.bukkit.api.event.BukkitUserVanishEvent 14 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 15 | import org.spongepowered.configurate.objectmapping.meta.Comment 16 | 17 | @RegisteredFeature 18 | @ConfigSerializable 19 | class FeatureGameMode( 20 | @Comment("The fallback gamemode when the player is not vanished and doesn't have in-memory gamemode.") 21 | val fallbackMode: GameMode = GameMode.SURVIVAL, 22 | @Comment("Update gamemode history on gamemode change event.") 23 | val checkGameModeChange: Boolean = false, 24 | @Comment("Change gamemode to spectator on double-sneak") 25 | val checkToggleSneak: Boolean = true, 26 | val timeWindowTicks: Long = 8 27 | ): ListenedFeature("gamemode") { 28 | 29 | @Transient val sneakMap = mutableMapOf() 30 | @Transient val sneakList = mutableListOf() 31 | 32 | @EventHandler 33 | private fun onToggleSneak(event: PlayerToggleSneakEvent) { 34 | if (!checkToggleSneak) return 35 | val player = event.player 36 | val user = player.user() ?: return 37 | if (!player.isSneaking || !user.isVanished || !isActive(user)) return 38 | if (sneakList.contains(player)) { 39 | if (player.gameMode == GameMode.SPECTATOR) { 40 | val allowFlight = player.allowFlight 41 | val isFlying = player.isFlying 42 | player.gameMode = sneakMap[player] ?: fallbackMode 43 | player.allowFlight = allowFlight 44 | player.isFlying = isFlying 45 | } else { 46 | sneakMap[player] = player.gameMode 47 | player.gameMode = GameMode.SPECTATOR 48 | } 49 | } else { 50 | if (player.gameMode != GameMode.SPECTATOR) { 51 | sneakMap[player] = player.gameMode 52 | } 53 | sneakList.add(player) 54 | runSync({ 55 | sneakList.remove(player) 56 | }, timeWindowTicks) 57 | } 58 | } 59 | 60 | @EventHandler 61 | private fun onGameModeChange(event: PlayerGameModeChangeEvent) { 62 | if (!checkGameModeChange) return 63 | val player = event.player 64 | if (player.user()?.isVanished != false) return 65 | sneakMap[player] = event.newGameMode 66 | } 67 | 68 | @EventHandler 69 | fun onVanish(event: BukkitUserVanishEvent) { 70 | val user = event.user 71 | val player = user.player() ?: return 72 | if (player.gameMode == GameMode.SPECTATOR) return 73 | val allowFlight = player.allowFlight 74 | val isFlying = player.isFlying 75 | sneakMap[player] = player.gameMode 76 | player.allowFlight = allowFlight 77 | player.isFlying = isFlying 78 | } 79 | 80 | @EventHandler 81 | private fun onUnVanish(event: BukkitUserUnVanishEvent) { 82 | val user = event.user 83 | val player = user.player() ?: return 84 | player.gameMode = sneakMap[player] ?: fallbackMode 85 | if (player.gameMode == GameMode.SPECTATOR || sneakMap[player] == GameMode.SPECTATOR) return 86 | val isFlying = player.isFlying 87 | val allowFlight = player.allowFlight 88 | player.allowFlight = allowFlight 89 | player.isFlying = isFlying 90 | } 91 | 92 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/feature/features/FeatureInventoryInspect.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.feature.features 2 | 3 | import org.bukkit.entity.Player 4 | import org.bukkit.event.EventHandler 5 | import org.bukkit.event.EventPriority 6 | import org.bukkit.event.inventory.InventoryClickEvent 7 | import org.bukkit.event.inventory.InventoryCloseEvent 8 | import org.bukkit.event.inventory.InventoryType 9 | import org.bukkit.event.player.PlayerInteractAtEntityEvent 10 | import org.sayandev.sayanvanish.api.feature.Configurable 11 | import org.sayandev.sayanvanish.api.feature.RegisteredFeature 12 | import org.sayandev.sayanvanish.bukkit.api.SayanVanishBukkitAPI.Companion.user 13 | import org.sayandev.sayanvanish.bukkit.feature.ListenedFeature 14 | import org.sayandev.stickynote.bukkit.plugin 15 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 16 | import java.util.* 17 | 18 | @RegisteredFeature 19 | @ConfigSerializable 20 | class FeatureInventoryInspect( 21 | @Configurable val modificationPermission: String = "${plugin.name}.features.inventory_inspect.modify" 22 | ): ListenedFeature("inventory_inspect") { 23 | 24 | val playerInventoryMap = mutableListOf() 25 | 26 | @EventHandler(priority = EventPriority.HIGHEST) 27 | private fun onInteractPlayer(event: PlayerInteractAtEntityEvent) { 28 | val player = event.player 29 | val user = player.user() ?: return 30 | if (!isActive(user)) return 31 | if (player.openInventory.type != InventoryType.CRAFTING) return 32 | if (!user.isVanished) return 33 | val target = event.rightClicked as? Player ?: return 34 | 35 | player.openInventory(target.inventory) 36 | playerInventoryMap.add(player.uniqueId) 37 | } 38 | 39 | @EventHandler 40 | private fun onClickPlayerInventory(event: InventoryClickEvent) { 41 | val player = event.whoClicked as? Player ?: return 42 | val user = player.user() ?: return 43 | if (!isActive(user)) return 44 | if (!playerInventoryMap.contains(player.uniqueId)) return 45 | if (player.hasPermission(modificationPermission)) return 46 | event.isCancelled = true 47 | } 48 | 49 | @EventHandler 50 | private fun onInventoryClose(event: InventoryCloseEvent) { 51 | val player = event.player as? Player ?: return 52 | playerInventoryMap.remove(player.uniqueId) 53 | } 54 | 55 | 56 | 57 | 58 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/feature/features/FeatureInvulnerability.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.feature.features 2 | 3 | import org.bukkit.event.EventHandler 4 | import org.sayandev.sayanvanish.api.Permission 5 | import org.sayandev.sayanvanish.api.feature.Configurable 6 | import org.sayandev.sayanvanish.api.feature.RegisteredFeature 7 | import org.sayandev.sayanvanish.bukkit.api.event.BukkitUserUnVanishEvent 8 | import org.sayandev.sayanvanish.bukkit.api.event.BukkitUserVanishEvent 9 | import org.sayandev.sayanvanish.bukkit.feature.ListenedFeature 10 | import org.sayandev.stickynote.bukkit.utils.ServerVersion 11 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 12 | import org.spongepowered.configurate.objectmapping.meta.Comment 13 | 14 | @RegisteredFeature 15 | @ConfigSerializable 16 | class FeatureInvulnerability( 17 | @Comment("Disable invulnerability when player reappears.") 18 | @Configurable val disableOnReappear: Boolean = true 19 | ) : ListenedFeature("invulnerability") { 20 | 21 | override var condition: Boolean = ServerVersion.supports(9) 22 | 23 | @EventHandler 24 | private fun onVanish(event: BukkitUserVanishEvent) { 25 | val user = event.user 26 | if (!isActive(user)) return 27 | if (user.hasPermission(Permission.INVULNERABLE)) { 28 | user.player()?.isInvulnerable = true 29 | } 30 | } 31 | 32 | @EventHandler 33 | private fun onUnVanish(event: BukkitUserUnVanishEvent) { 34 | val user = event.user 35 | if (!user.hasPermission(Permission.INVULNERABLE) || disableOnReappear) { 36 | user.player()?.isInvulnerable = false 37 | } 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/feature/features/FeatureProxyVanishQueue.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.feature.features 2 | 3 | import org.sayandev.sayanvanish.api.feature.Configurable 4 | import org.sayandev.sayanvanish.api.feature.Feature 5 | import org.sayandev.sayanvanish.api.feature.RegisteredFeature 6 | import org.sayandev.sayanvanish.api.feature.category.FeatureCategories 7 | import org.sayandev.sayanvanish.bukkit.api.SayanVanishBukkitAPI 8 | import org.sayandev.sayanvanish.bukkit.api.SayanVanishBukkitAPI.Companion.getOrAddUser 9 | import org.sayandev.sayanvanish.bukkit.config.language 10 | import org.sayandev.stickynote.bukkit.onlinePlayers 11 | import org.sayandev.stickynote.bukkit.runSync 12 | import org.sayandev.stickynote.bukkit.utils.AdventureUtils.component 13 | import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder 14 | import org.sayandev.sayanvanish.bukkit.api.SayanVanishBukkitAPI.Companion.user 15 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 16 | import org.spongepowered.configurate.objectmapping.meta.Comment 17 | 18 | @RegisteredFeature 19 | @ConfigSerializable 20 | class FeatureProxyVanishQueue( 21 | @Comment("The time in milliseconds between each check for players in the queue. low values may cause performance issues.") 22 | @Configurable val checkEvery: Long = 100 23 | ) : Feature("proxy_vanish_queue", category = FeatureCategories.PROXY) { 24 | 25 | override fun enable() { 26 | runSync({ 27 | for (player in onlinePlayers) { 28 | val user = player.user() 29 | if (((user != null && !isActive(user)) || !isActive())) return@runSync 30 | SayanVanishBukkitAPI.getInstance().database.isInQueue(player.uniqueId) { inQueue -> 31 | if (inQueue) { 32 | SayanVanishBukkitAPI.getInstance().database.getFromQueue(player.uniqueId) { isVanished -> 33 | SayanVanishBukkitAPI.getInstance().database.removeFromQueue(player.uniqueId) 34 | runSync { 35 | val user = player.getOrAddUser() 36 | user.sendComponent(language.vanish.vanishFromQueue, Placeholder.parsed("state", user.stateText(isVanished))) 37 | val options = user.currentOptions.apply { 38 | this.sendMessage = false 39 | } 40 | if (isVanished) { 41 | user.vanish(options) 42 | } else { 43 | user.unVanish(options) 44 | } 45 | } 46 | } 47 | } 48 | } 49 | } 50 | }, checkEvery, checkEvery) 51 | super.enable() 52 | } 53 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/feature/features/FeatureRideEntity.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.feature.features 2 | 3 | import org.bukkit.entity.Player 4 | import org.bukkit.event.EventHandler 5 | import org.bukkit.event.EventPriority 6 | import org.bukkit.event.player.PlayerInteractEntityEvent 7 | import org.sayandev.sayanvanish.api.feature.RegisteredFeature 8 | import org.sayandev.sayanvanish.bukkit.api.SayanVanishBukkitAPI.Companion.user 9 | import org.sayandev.sayanvanish.bukkit.feature.ListenedFeature 10 | import org.sayandev.sayanvanish.bukkit.utils.PlayerUtils.sendComponent 11 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 12 | 13 | @RegisteredFeature 14 | @ConfigSerializable 15 | class FeatureRideEntity( 16 | val leaveVehicleWhenOthersEnter: Boolean = true, 17 | val exitMessage: String = "You have been removed from the vehicle. because someone else entered it.", 18 | ): ListenedFeature("ride_entity") { 19 | 20 | @EventHandler(priority = EventPriority.HIGHEST) 21 | private fun removeVanishedPassengerOnEnter(event: PlayerInteractEntityEvent) { 22 | if (!leaveVehicleWhenOthersEnter) return 23 | val targetEntity = event.rightClicked 24 | val vanishedPassengers = targetEntity.passengers 25 | .filterIsInstance() 26 | .mapNotNull { player -> player.user() } 27 | .filter { it.isVanished } 28 | .mapNotNull { it.player() } 29 | for (vanishedPassenger in vanishedPassengers) { 30 | vanishedPassenger.leaveVehicle() 31 | vanishedPassenger.sendComponent(exitMessage) 32 | } 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/feature/features/hook/AdvancedServerListImpl.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.feature.features.hook 2 | 3 | import ch.andre601.advancedserverlist.api.objects.GenericPlayer 4 | import ch.andre601.advancedserverlist.api.objects.GenericServer 5 | import org.sayandev.sayanvanish.api.SayanVanishAPI 6 | import org.sayandev.sayanvanish.api.SayanVanishAPI.Companion.user 7 | import org.sayandev.sayanvanish.bukkit.api.SayanVanishBukkitAPI 8 | import org.sayandev.sayanvanish.bukkit.config.language 9 | import org.sayandev.sayanvanish.bukkit.config.settings 10 | import org.sayandev.stickynote.bukkit.onlinePlayers 11 | import ch.andre601.advancedserverlist.api.PlaceholderProvider 12 | import ch.andre601.advancedserverlist.api.AdvancedServerListAPI 13 | import ch.andre601.advancedserverlist.api.exceptions.InvalidPlaceholderProviderException 14 | import kotlin.text.get 15 | 16 | class AdvancedServerListImpl : PlaceholderProvider("sayanvanish") { 17 | fun register() { 18 | try { 19 | AdvancedServerListAPI.get() 20 | .addPlaceholderProvider(this) 21 | } catch (_: InvalidPlaceholderProviderException) { } 22 | } 23 | 24 | override fun parsePlaceholder( 25 | placeholder: String, 26 | player: GenericPlayer?, 27 | server: GenericServer? 28 | ): String? { 29 | if (placeholder.equals("vanished", true)) { 30 | if (player == null) return "false" 31 | return if (SayanVanishBukkitAPI.getInstance().getVanishedUsers().map { it.username }.contains(player.name)) "true" else "false" 32 | } 33 | 34 | if (placeholder.equals("level", true)) { 35 | if (player == null) return "0" 36 | return SayanVanishBukkitAPI.getInstance().getUser(player.uuid)?.vanishLevel?.toString() ?: "0" 37 | } 38 | 39 | if (placeholder.equals("count", true)) { 40 | return SayanVanishBukkitAPI.getInstance().database.getUsers().filter { user -> user.isOnline && user.isVanished }.size.toString() 41 | } 42 | 43 | if (placeholder.equals("vanish_prefix", true)) { 44 | return if (player?.uuid?.user()?.isVanished == true) language.vanish.placeholderPrefix else "" 45 | } 46 | 47 | if (placeholder.equals("vanish_suffix", true)) { 48 | return if (player?.uuid?.user()?.isVanished == true) language.vanish.placeholderSuffix else "" 49 | } 50 | 51 | if (placeholder.startsWith("online_")) { 52 | val type = placeholder.substring(7) 53 | val vanishedOnlineUsers = SayanVanishBukkitAPI.getInstance().database.getUsers().filter { user -> user.isVanished && user.isOnline } 54 | 55 | return if (type.equals("here", true)) { 56 | onlinePlayers.filter { onlinePlayer -> !vanishedOnlineUsers.map { vanishedOnlineUser -> vanishedOnlineUser.username }.contains(onlinePlayer.name) }.size.toString() 57 | } else if (type.equals("total", true)) { 58 | if (!settings.general.proxyMode) { 59 | return "PROXY_MODE IS NOT ENABLED!" 60 | } 61 | SayanVanishAPI.getInstance().database.getBasicUsers(false).filter { !vanishedOnlineUsers.map { vanishUser -> vanishUser.username }.contains(it.username) }.size.toString() 62 | } else { 63 | if (!settings.general.proxyMode) { 64 | return "PROXY_MODE IS NOT ENABLED!" 65 | } 66 | SayanVanishAPI.getInstance().database.getBasicUsers(false).filter { it.serverId == type && !vanishedOnlineUsers.map { vanishUser -> vanishUser.username }.contains(it.username) }.size.toString() 67 | } 68 | } 69 | 70 | return null 71 | } 72 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/feature/features/hook/FeatureHookAdvancedServerList.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.feature.features.hook 2 | 3 | import org.sayandev.sayanvanish.api.feature.RegisteredFeature 4 | import org.sayandev.sayanvanish.bukkit.feature.HookFeature 5 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 6 | 7 | @RegisteredFeature 8 | @ConfigSerializable 9 | class FeatureHookAdvancedServerList : HookFeature("hook_advanced_server_list", "AdvancedServerList") { 10 | override fun enable() { 11 | if (hasPlugin()) { 12 | AdvancedServerListImpl().register() 13 | } 14 | super.enable() 15 | } 16 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/feature/features/hook/FeatureHookCitizens.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.feature.features.hook 2 | 3 | import net.citizensnpcs.api.ai.speech.SpeechContext 4 | import net.citizensnpcs.api.ai.speech.event.NPCSpeechEvent 5 | import net.citizensnpcs.api.ai.speech.event.SpeechEvent 6 | import org.bukkit.entity.Player 7 | import org.bukkit.event.EventHandler 8 | import org.bukkit.event.Listener 9 | import org.sayandev.sayanvanish.api.feature.RegisteredFeature 10 | import org.sayandev.sayanvanish.bukkit.api.SayanVanishBukkitAPI 11 | import org.sayandev.sayanvanish.bukkit.feature.HookFeature 12 | import org.sayandev.sayanvanish.bukkit.api.SayanVanishBukkitAPI.Companion.user 13 | import org.sayandev.stickynote.bukkit.registerListener 14 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 15 | import org.spongepowered.configurate.objectmapping.meta.Comment 16 | 17 | @RegisteredFeature 18 | @ConfigSerializable 19 | class FeatureHookCitizens( 20 | @Comment("Will cancel npc speech event if context of speech contains a vanished player") 21 | val checkSpeech: Boolean = true, 22 | ): HookFeature("hook_citizens", "Citizens") { 23 | 24 | override fun enable() { 25 | if (hasPlugin()) { 26 | CitizensHookImpl(this) 27 | } 28 | super.enable() 29 | } 30 | 31 | } 32 | 33 | private class CitizensHookImpl(val feature: FeatureHookCitizens): Listener { 34 | 35 | init { 36 | registerListener(this) 37 | } 38 | 39 | @EventHandler 40 | private fun onNPCSpeech(event: NPCSpeechEvent) { 41 | if (!feature.checkSpeech) return 42 | if (!feature.isActive()) return 43 | val hasContext = checkContext(event.context) 44 | if (!hasContext) event.isCancelled = true 45 | } 46 | 47 | @EventHandler 48 | private fun onSpeech(event: SpeechEvent) { 49 | if (!feature.checkSpeech) return 50 | if (!feature.isActive()) return 51 | val hasContext = checkContext(event.context) 52 | if (!hasContext) event.isCancelled = true 53 | } 54 | 55 | private fun checkContext(context: SpeechContext): Boolean { 56 | val contains = SayanVanishBukkitAPI.getInstance().getVanishedUsers().filter { it.isOnline }.mapNotNull { it.player() }.any { context.message.contains(it.name) } 57 | if (contains) return false 58 | 59 | val iterator = context.iterator() 60 | while (iterator.hasNext()) { 61 | val recipient = iterator.next() 62 | val player = recipient.getEntity() as? Player ?: continue 63 | val user = player.user() ?: continue 64 | if (user.isVanished) { 65 | iterator.remove(); 66 | } 67 | } 68 | 69 | return context.hasRecipients(); 70 | } 71 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/feature/features/hook/FeatureHookDiscordSRV.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.feature.features.hook 2 | 3 | import github.scarsz.discordsrv.DiscordSRV 4 | import org.bukkit.event.EventHandler 5 | import org.bukkit.event.Listener 6 | import org.sayandev.sayanvanish.api.feature.Configurable 7 | import org.sayandev.sayanvanish.api.feature.RegisteredFeature 8 | import org.sayandev.sayanvanish.bukkit.api.event.BukkitUserUnVanishEvent 9 | import org.sayandev.sayanvanish.bukkit.api.event.BukkitUserVanishEvent 10 | import org.sayandev.sayanvanish.bukkit.feature.HookFeature 11 | import org.sayandev.stickynote.bukkit.registerListener 12 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 13 | import org.spongepowered.configurate.objectmapping.meta.Comment 14 | 15 | @RegisteredFeature 16 | @ConfigSerializable 17 | class FeatureHookDiscordSRV( 18 | @Comment("Send quit message on vanish") 19 | @Configurable val sendQuitMessageOnVanish: Boolean = true, 20 | @Comment("Send join message on unvanish") 21 | @Configurable val sendJoinMessageOnUnvanish: Boolean = true, 22 | @Comment("Quit message format") 23 | @Configurable val quitMessage: String = "%player% left the server", 24 | @Comment("Join message format") 25 | @Configurable val joinMessage: String = "%player% joined the server", 26 | ): HookFeature("hook_discordsrv", "DiscordSRV") { 27 | 28 | override fun enable() { 29 | if (hasPlugin()) { 30 | DiscordSRVImpl(this) 31 | } 32 | super.enable() 33 | } 34 | } 35 | 36 | private class DiscordSRVImpl(val feature: FeatureHookDiscordSRV): Listener { 37 | 38 | init { 39 | registerListener(this) 40 | } 41 | 42 | @EventHandler 43 | private fun onUnVanish(event: BukkitUserUnVanishEvent) { 44 | val user = event.user 45 | if (!feature.isActive(user)) return 46 | if (!feature.sendJoinMessageOnUnvanish) return 47 | val player = user.player() ?: return 48 | if (event.options.isOnJoin || event.options.isOnQuit) return 49 | DiscordSRV.getPlugin().sendJoinMessage(player, feature.joinMessage) 50 | } 51 | 52 | @EventHandler 53 | private fun onVanish(event: BukkitUserVanishEvent) { 54 | val user = event.user 55 | if (!feature.isActive(user)) return 56 | if (!feature.sendQuitMessageOnVanish) return 57 | val player = user.player() ?: return 58 | if (event.options.isOnJoin || event.options.isOnQuit) return 59 | DiscordSRV.getPlugin().sendLeaveMessage(player, feature.quitMessage) 60 | } 61 | 62 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/feature/features/hook/FeatureHookDynmap.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.feature.features.hook 2 | 3 | import org.bukkit.Bukkit 4 | import org.bukkit.event.EventHandler 5 | import org.bukkit.event.Listener 6 | import org.dynmap.DynmapAPI 7 | import org.sayandev.sayanvanish.api.feature.RegisteredFeature 8 | import org.sayandev.sayanvanish.bukkit.api.event.BukkitUserUnVanishEvent 9 | import org.sayandev.sayanvanish.bukkit.api.event.BukkitUserVanishEvent 10 | import org.sayandev.sayanvanish.bukkit.feature.HookFeature 11 | import org.sayandev.stickynote.bukkit.registerListener 12 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 13 | 14 | @RegisteredFeature 15 | @ConfigSerializable 16 | class FeatureHookDynmap: HookFeature("hook_dynmap", "dynmap") { 17 | 18 | override fun enable() { 19 | if (hasPlugin()) { 20 | DynmapHookImpl(this) 21 | } 22 | super.enable() 23 | } 24 | } 25 | 26 | private class DynmapHookImpl(val feature: FeatureHookDynmap): Listener { 27 | val api = Bukkit.getPluginManager().getPlugin("dynmap") as DynmapAPI 28 | 29 | init { 30 | registerListener(this) 31 | } 32 | 33 | @EventHandler 34 | private fun onVanish(event: BukkitUserVanishEvent) { 35 | val user = event.user 36 | if (!feature.isActive(user)) return 37 | api.setPlayerVisiblity(user.username, false) 38 | } 39 | 40 | @EventHandler 41 | private fun onUnVanish(event: BukkitUserUnVanishEvent) { 42 | val user = event.user 43 | if (!feature.isActive(user)) return 44 | api.setPlayerVisiblity(user.username, true) 45 | } 46 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/feature/features/hook/FeatureHookEssentials.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.feature.features.hook 2 | 3 | import net.ess3.api.events.AfkStatusChangeEvent 4 | import net.ess3.api.events.PrivateMessagePreSendEvent 5 | import org.bukkit.event.EventHandler 6 | import org.bukkit.event.Listener 7 | import org.sayandev.sayanvanish.api.SayanVanishAPI.Companion.user 8 | import org.sayandev.sayanvanish.api.feature.Configurable 9 | import org.sayandev.sayanvanish.api.feature.RegisteredFeature 10 | import org.sayandev.sayanvanish.bukkit.feature.HookFeature 11 | import org.sayandev.stickynote.bukkit.registerListener 12 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 13 | import org.spongepowered.configurate.objectmapping.meta.Comment 14 | import java.util.* 15 | 16 | @RegisteredFeature 17 | @ConfigSerializable 18 | class FeatureHookEssentials( 19 | @Comment("Prevent Essentials from changing the AFK status of vanished players") 20 | @Configurable val preventAfkStatusChange: Boolean = true, 21 | @Comment("Prevent players to send private messages to vanished players using Essentials") 22 | @Configurable val preventPrivateMessage: Boolean = true, 23 | ) : HookFeature("hook_essentials", "Essentials") { 24 | 25 | override fun enable() { 26 | if (hasPlugin()) { 27 | EssentialsHookImpl(this) 28 | } 29 | super.enable() 30 | } 31 | 32 | } 33 | 34 | private class EssentialsHookImpl(val feature: FeatureHookEssentials): Listener { 35 | 36 | val lastAfkValue = mutableMapOf() 37 | 38 | init { 39 | registerListener(this) 40 | } 41 | 42 | @EventHandler 43 | private fun preventAfkStatusChange(event: AfkStatusChangeEvent) { 44 | if (!feature.preventAfkStatusChange) return 45 | val user = event.affected.uuid?.user() ?: return 46 | if (!feature.isActive(user)) return 47 | if (!user.isVanished) return 48 | event.isCancelled = true 49 | } 50 | 51 | @EventHandler 52 | private fun preventPrivateMessage(event: PrivateMessagePreSendEvent) { 53 | if (!feature.preventPrivateMessage) return 54 | val user = event.recipient.uuid?.user() ?: return 55 | if (!feature.isActive(user)) return 56 | if (user.isVanished) { 57 | event.sender.sendMessage(com.earth2me.essentials.I18n.tl("errorWithMessage", com.earth2me.essentials.I18n.tl("playerNotFound"))) 58 | event.isCancelled = true 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/feature/features/hook/FeatureHookPl3xMap.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.feature.features.hook 2 | 3 | import net.pl3x.map.core.Pl3xMap 4 | import org.bukkit.event.EventHandler 5 | import org.bukkit.event.Listener 6 | import org.sayandev.sayanvanish.api.feature.RegisteredFeature 7 | import org.sayandev.sayanvanish.bukkit.api.event.BukkitUserUnVanishEvent 8 | import org.sayandev.sayanvanish.bukkit.api.event.BukkitUserVanishEvent 9 | import org.sayandev.sayanvanish.bukkit.feature.HookFeature 10 | import org.sayandev.stickynote.bukkit.registerListener 11 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 12 | 13 | @RegisteredFeature 14 | @ConfigSerializable 15 | class FeatureHookPl3xMap: HookFeature("hook_pl3xmap", "Pl3xMap") { 16 | 17 | override fun enable() { 18 | if (hasPlugin()) { 19 | Pl3xMapHookImpl(this) 20 | } 21 | super.enable() 22 | } 23 | } 24 | 25 | private class Pl3xMapHookImpl(val feature: FeatureHookPl3xMap): Listener { 26 | 27 | init { 28 | registerListener(this) 29 | } 30 | 31 | @EventHandler 32 | private fun onVanish(event: BukkitUserVanishEvent) { 33 | val user = event.user 34 | if (!feature.isActive(user)) return 35 | Pl3xMap.api().playerRegistry.get(user.uniqueId)?.setHidden(true, false) 36 | } 37 | 38 | @EventHandler 39 | private fun onUnVanish(event: BukkitUserUnVanishEvent) { 40 | val user = event.user 41 | if (!feature.isActive(user)) return 42 | Pl3xMap.api().playerRegistry.get(user.uniqueId)?.setHidden(false, false) 43 | } 44 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/feature/features/hook/FeatureHookSquareMap.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.feature.features.hook 2 | 3 | import org.bukkit.event.EventHandler 4 | import org.bukkit.event.Listener 5 | import org.sayandev.sayanvanish.api.feature.RegisteredFeature 6 | import org.sayandev.sayanvanish.bukkit.api.event.BukkitUserUnVanishEvent 7 | import org.sayandev.sayanvanish.bukkit.api.event.BukkitUserVanishEvent 8 | import org.sayandev.sayanvanish.bukkit.feature.HookFeature 9 | import org.sayandev.stickynote.bukkit.registerListener 10 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 11 | import xyz.jpenilla.squaremap.api.SquaremapProvider 12 | 13 | @RegisteredFeature 14 | @ConfigSerializable 15 | class FeatureHookSquareMap: HookFeature("hook_squaremap", "squaremap") { 16 | 17 | override fun enable() { 18 | if (hasPlugin()) { 19 | SquaremapHookImpl(this) 20 | } 21 | super.enable() 22 | } 23 | } 24 | 25 | private class SquaremapHookImpl(val feature: FeatureHookSquareMap): Listener { 26 | 27 | init { 28 | registerListener(this) 29 | } 30 | 31 | @EventHandler 32 | private fun onVanish(event: BukkitUserVanishEvent) { 33 | val user = event.user 34 | if (!feature.isActive(user)) return 35 | user.player()?.uniqueId?.let { SquaremapProvider.get().playerManager().hide(it, true) } 36 | } 37 | 38 | @EventHandler 39 | private fun onUnVanish(event: BukkitUserUnVanishEvent) { 40 | val user = event.user 41 | if (!feature.isActive(user)) return 42 | user.player()?.uniqueId?.let { SquaremapProvider.get().playerManager().show(it, true) } 43 | } 44 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/feature/features/hook/FeatureHookTAB.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.feature.features.hook 2 | 3 | import me.neznamy.tab.api.TabPlayer 4 | import me.neznamy.tab.api.integration.VanishIntegration 5 | import org.sayandev.sayanvanish.api.feature.RegisteredFeature 6 | import org.sayandev.sayanvanish.bukkit.api.BukkitUser 7 | import org.sayandev.sayanvanish.bukkit.api.SayanVanishBukkitAPI 8 | import org.sayandev.sayanvanish.bukkit.feature.HookFeature 9 | import org.sayandev.stickynote.bukkit.plugin 10 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 11 | import org.spongepowered.configurate.objectmapping.meta.Comment 12 | 13 | @RegisteredFeature 14 | @ConfigSerializable 15 | class FeatureHookTAB( 16 | @Comment("Whether to use cache data for vanish status. This will improve performance but may cause a small delay in tablist removal after join.") 17 | val useCacheData: Boolean = true 18 | ): HookFeature("hook_tab", "TAB") { 19 | override fun enable() { 20 | if (hasPlugin()) { 21 | VanishIntegrationTAB(this).register() 22 | } 23 | super.enable() 24 | } 25 | 26 | override fun disable(reload: Boolean) { 27 | if (hasPlugin()) { 28 | VanishIntegrationTAB(this).unregister() 29 | } 30 | super.disable(reload) 31 | } 32 | } 33 | 34 | private class VanishIntegrationTAB(val feature: FeatureHookTAB): VanishIntegration(plugin.name) { 35 | override fun isVanished(player: TabPlayer): Boolean { 36 | return SayanVanishBukkitAPI.getInstance().isVanished(player.uniqueId, feature.useCacheData) 37 | } 38 | 39 | override fun canSee(viewer: TabPlayer, target: TabPlayer): Boolean { 40 | if (viewer.uniqueId == target.uniqueId) return true 41 | val viewerUser = SayanVanishBukkitAPI.getInstance().getUser(viewer.uniqueId, feature.useCacheData) ?: BukkitUser(viewer.uniqueId, viewer.name) 42 | val targetUser = SayanVanishBukkitAPI.getInstance().getUser(target.uniqueId, feature.useCacheData) ?: return true 43 | return SayanVanishBukkitAPI.getInstance().canSee(viewerUser, targetUser) 44 | } 45 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/feature/features/hook/FeatureLuckPermsHook.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.feature.features.hook 2 | 3 | import net.luckperms.api.LuckPermsProvider 4 | import net.luckperms.api.context.ContextCalculator 5 | import net.luckperms.api.context.ContextConsumer 6 | import net.luckperms.api.context.ContextSet 7 | import net.luckperms.api.context.ImmutableContextSet 8 | import net.luckperms.api.node.NodeEqualityPredicate 9 | import net.luckperms.api.node.types.PermissionNode 10 | import net.luckperms.api.query.QueryOptions 11 | import org.bukkit.Bukkit 12 | import org.bukkit.entity.Player 13 | import org.sayandev.sayanvanish.api.feature.Configurable 14 | import org.sayandev.sayanvanish.api.feature.RegisteredFeature 15 | import org.sayandev.sayanvanish.bukkit.api.SayanVanishBukkitAPI.Companion.getOrCreateUser 16 | import org.sayandev.sayanvanish.bukkit.feature.HookFeature 17 | import org.sayandev.stickynote.bukkit.warn 18 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 19 | import org.spongepowered.configurate.objectmapping.meta.Comment 20 | import java.util.* 21 | 22 | 23 | @RegisteredFeature 24 | @ConfigSerializable 25 | class FeatureLuckPermsHook( 26 | @Comment(""" 27 | Whether to register custom context for vanished players. 28 | This will allow you to check if a player is vanished using the context "vanished". 29 | This is useful for checking permissions based on the player's vanish status. 30 | """) 31 | @Configurable val registerCustomContext: Boolean = true, 32 | @Comment("Whether to check permission using LuckPerms. If false, it will fallback to bukkit permission check.") 33 | @Configurable val checkPermissionViaLuckPerms: Boolean = false, 34 | @Configurable val checkPermissionViaLuckPermsFeatures: Boolean = true 35 | ): HookFeature("hook_luckperms", "LuckPerms") { 36 | 37 | @Transient var vanishContext: VanishedContext? = null 38 | 39 | override fun enable() { 40 | if (hasPlugin()) { 41 | if (registerCustomContext) { 42 | vanishContext = VanishedContext() 43 | LuckPermsProvider.get().contextManager.registerCalculator(vanishContext!!) 44 | } 45 | } 46 | super.enable() 47 | } 48 | 49 | override fun disable(reload: Boolean) { 50 | vanishContext?.let { LuckPermsProvider.get().contextManager.unregisterCalculator(it) } 51 | super.disable(reload) 52 | } 53 | 54 | fun hasPermission(uniqueId: UUID, permission: String): Boolean { 55 | // Can't check permissions on a per-player basis to prevent stackoverflow 56 | if (!isActive()) { 57 | warn("tried to check permission using LuckPerms, but the `${this.id}` feature is not active, fallback to bukkit permission check.") 58 | return Bukkit.getPlayer(uniqueId)?.hasPermission(permission) == true 59 | } 60 | val user = LuckPermsProvider.get().userManager.getUser(uniqueId) ?: return false 61 | val permissionNode = PermissionNode.builder(permission).value(true).build() 62 | val userPermission = user 63 | .data() 64 | .contains(permissionNode, NodeEqualityPredicate.IGNORE_EXPIRY_TIME) 65 | .asBoolean() 66 | if (!userPermission) { 67 | return user.getInheritedGroups(QueryOptions.nonContextual()).any { it.data().contains(permissionNode, NodeEqualityPredicate.IGNORE_EXPIRY_TIME).asBoolean() } 68 | } 69 | return userPermission 70 | } 71 | } 72 | 73 | class VanishedContext: ContextCalculator { 74 | 75 | override fun calculate(target: Player, contextConsumer: ContextConsumer) { 76 | contextConsumer.accept("vanished", target.getOrCreateUser().isVanished.toString()) 77 | } 78 | 79 | override fun estimatePotentialContexts(): ContextSet { 80 | val builder = ImmutableContextSet.builder() 81 | builder.add("vanished", "true") 82 | builder.add("vanished", "false") 83 | return builder.build() 84 | } 85 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/feature/features/prevent/FeaturePreventAdvancementAnnounce.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.feature.features.prevent 2 | 3 | import org.bukkit.event.EventHandler 4 | import org.bukkit.event.player.PlayerAdvancementDoneEvent 5 | import org.sayandev.sayanvanish.api.feature.Configurable 6 | import org.sayandev.sayanvanish.api.feature.RegisteredFeature 7 | import org.sayandev.sayanvanish.api.feature.category.FeatureCategories 8 | import org.sayandev.sayanvanish.bukkit.api.SayanVanishBukkitAPI.Companion.user 9 | import org.sayandev.sayanvanish.bukkit.feature.ListenedFeature 10 | import org.sayandev.stickynote.bukkit.StickyNote 11 | import org.sayandev.stickynote.bukkit.utils.ServerVersion 12 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 13 | import org.spongepowered.configurate.objectmapping.meta.Comment 14 | 15 | @RegisteredFeature 16 | @ConfigSerializable 17 | class FeaturePreventAdvancementAnnounce( 18 | @Comment("Whether to disable the advancement announce message when the player is vanished.") 19 | @Configurable val disableMessage: Boolean = true, 20 | @Comment("Whether to revoke the criteria when the player is vanished.") 21 | @Configurable val revokeCriteria: Boolean = false 22 | ): ListenedFeature("prevent_advancement_announce", category = FeatureCategories.PREVENTION) { 23 | 24 | @Transient 25 | override var condition: Boolean = StickyNote.isPaper && ServerVersion.supports(13) 26 | 27 | @EventHandler 28 | private fun onAdvancementDone(event: PlayerAdvancementDoneEvent) { 29 | val user = event.player.user() ?: return 30 | if (!isActive(user)) return 31 | if (user.isVanished) { 32 | if (disableMessage) { 33 | event.message(null) 34 | } 35 | if (revokeCriteria) { 36 | for (criteria in event.advancement.criteria) { 37 | event.player.getAdvancementProgress(event.advancement).revokeCriteria(criteria) 38 | } 39 | } 40 | } 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/feature/features/prevent/FeaturePreventBlockBreak.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.feature.features.prevent 2 | 3 | import org.bukkit.event.EventHandler 4 | import org.bukkit.event.EventPriority 5 | import org.bukkit.event.block.BlockBreakEvent 6 | import org.sayandev.sayanvanish.api.feature.RegisteredFeature 7 | import org.sayandev.sayanvanish.api.feature.category.FeatureCategories 8 | import org.sayandev.sayanvanish.bukkit.api.SayanVanishBukkitAPI.Companion.user 9 | import org.sayandev.sayanvanish.bukkit.feature.ListenedFeature 10 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 11 | 12 | @RegisteredFeature 13 | @ConfigSerializable 14 | class FeaturePreventBlockBreak: ListenedFeature("prevent_block_break", false, category = FeatureCategories.PREVENTION) { 15 | 16 | @EventHandler(priority = EventPriority.HIGHEST) 17 | private fun onBlockBreak(event: BlockBreakEvent) { 18 | val user = event.player.user() ?: return 19 | if (!isActive(user)) return 20 | if (user.isVanished) { 21 | event.isCancelled = true 22 | } 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/feature/features/prevent/FeaturePreventBlockGrief.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.feature.features.prevent 2 | 3 | import org.bukkit.entity.Player 4 | import org.bukkit.event.EventHandler 5 | import org.bukkit.event.entity.EntityChangeBlockEvent 6 | import org.sayandev.sayanvanish.api.feature.RegisteredFeature 7 | import org.sayandev.sayanvanish.api.feature.category.FeatureCategories 8 | import org.sayandev.sayanvanish.bukkit.api.SayanVanishBukkitAPI.Companion.user 9 | import org.sayandev.sayanvanish.bukkit.feature.ListenedFeature 10 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 11 | 12 | @RegisteredFeature 13 | @ConfigSerializable 14 | class FeaturePreventBlockGrief: ListenedFeature("prevent_block_grief", category = FeatureCategories.PREVENTION) { 15 | 16 | @EventHandler 17 | private fun onChangeBlock(event: EntityChangeBlockEvent) { 18 | val user = (event.entity as? Player)?.user() ?: return 19 | if (!isActive(user)) return 20 | if (user.isVanished) { 21 | event.isCancelled = true 22 | } 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/feature/features/prevent/FeaturePreventBlockPlace.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.feature.features.prevent 2 | 3 | import org.bukkit.event.EventHandler 4 | import org.bukkit.event.EventPriority 5 | import org.bukkit.event.block.BlockPlaceEvent 6 | import org.sayandev.sayanvanish.api.feature.RegisteredFeature 7 | import org.sayandev.sayanvanish.api.feature.category.FeatureCategories 8 | import org.sayandev.sayanvanish.bukkit.api.SayanVanishBukkitAPI.Companion.user 9 | import org.sayandev.sayanvanish.bukkit.feature.ListenedFeature 10 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 11 | 12 | @RegisteredFeature 13 | @ConfigSerializable 14 | class FeaturePreventBlockPlace: ListenedFeature("prevent_block_place", false, category = FeatureCategories.PREVENTION) { 15 | 16 | @EventHandler(priority = EventPriority.HIGHEST) 17 | private fun onBlockPlace(event: BlockPlaceEvent) { 18 | val user = event.player.user() ?: return 19 | if (!isActive(user)) return 20 | if (user.isVanished) { 21 | event.isCancelled = true 22 | } 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/feature/features/prevent/FeaturePreventChat.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.feature.features.prevent 2 | 3 | import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder 4 | import org.bukkit.Bukkit 5 | import org.bukkit.event.Event 6 | import org.bukkit.event.EventPriority 7 | import org.bukkit.event.Listener 8 | import org.bukkit.event.player.AsyncPlayerChatEvent 9 | import org.sayandev.sayanvanish.api.feature.Configurable 10 | import org.sayandev.sayanvanish.api.feature.RegisteredFeature 11 | import org.sayandev.sayanvanish.api.feature.category.FeatureCategories 12 | import org.sayandev.sayanvanish.bukkit.api.SayanVanishBukkitAPI.Companion.user 13 | import org.sayandev.sayanvanish.bukkit.config.language 14 | import org.sayandev.sayanvanish.bukkit.feature.ListenedFeature 15 | import org.sayandev.stickynote.bukkit.StickyNote 16 | import org.sayandev.stickynote.bukkit.plugin 17 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 18 | import org.spongepowered.configurate.objectmapping.meta.Comment 19 | 20 | @RegisteredFeature 21 | @ConfigSerializable 22 | class FeaturePreventChat( 23 | @Comment("The character that vanished players can use to bypass the chat prevention.") 24 | @Configurable val bypassChar: String = "!", 25 | @Comment("Requires server restart to apply.") 26 | val priority: EventPriority = EventPriority.HIGH, 27 | ): ListenedFeature("prevent_chat", category = FeatureCategories.PREVENTION) { 28 | 29 | override fun enable() { 30 | Bukkit.getPluginManager().registerEvent( 31 | AsyncPlayerChatEvent::class.java, 32 | this, 33 | priority, 34 | { listener: Listener, event: Event -> 35 | if (event !is AsyncPlayerChatEvent) return@registerEvent 36 | if (event.isCancelled) return@registerEvent 37 | val user = event.player.user() ?: return@registerEvent 38 | if (!isActive(user)) return@registerEvent 39 | if (!user.isVanished) return@registerEvent 40 | val message = event.message 41 | if (message.startsWith(bypassChar)) { 42 | event.message = message.removePrefix(bypassChar) 43 | } else { 44 | user.sendComponent(language.vanish.cantChatWhileVanished, Placeholder.unparsed("char", bypassChar)) 45 | event.isCancelled = true 46 | } 47 | }, 48 | plugin, 49 | false 50 | ) 51 | super.enable() 52 | } 53 | 54 | override fun disable(reload: Boolean) { 55 | StickyNote.unregisterListener(this) 56 | super.disable(reload) 57 | } 58 | 59 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/feature/features/prevent/FeaturePreventCreatureTarget.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.feature.features.prevent 2 | 3 | import org.bukkit.entity.Mob 4 | import org.bukkit.entity.Player 5 | import org.bukkit.event.EventHandler 6 | import org.bukkit.event.entity.EntityTargetEvent 7 | import org.sayandev.sayanvanish.api.feature.RegisteredFeature 8 | import org.sayandev.sayanvanish.api.feature.category.FeatureCategories 9 | import org.sayandev.sayanvanish.bukkit.api.SayanVanishBukkitAPI.Companion.user 10 | import org.sayandev.sayanvanish.bukkit.api.event.BukkitUserVanishEvent 11 | import org.sayandev.sayanvanish.bukkit.feature.ListenedFeature 12 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 13 | 14 | @RegisteredFeature 15 | @ConfigSerializable 16 | class FeaturePreventCreatureTarget: ListenedFeature("prevent_creature_target", category = FeatureCategories.PREVENTION) { 17 | 18 | @EventHandler 19 | private fun preventEntityTargetOnVanish(event: BukkitUserVanishEvent) { 20 | val user = event.user 21 | val player = user.player() ?: return 22 | for (entity in player.world.entities.filterIsInstance()) { 23 | if (entity.target != player) continue 24 | entity.target = null 25 | } 26 | } 27 | 28 | @EventHandler 29 | private fun onEntityTarget(event: EntityTargetEvent) { 30 | val target = event.target as? Player ?: return 31 | val user = target.user() ?: return 32 | if (!isActive(user)) return 33 | if (!user.isVanished) return 34 | event.isCancelled = true 35 | } 36 | 37 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/feature/features/prevent/FeaturePreventDamage.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.feature.features.prevent 2 | 3 | import org.bukkit.entity.Player 4 | import org.bukkit.event.EventHandler 5 | import org.bukkit.event.entity.EntityDamageByEntityEvent 6 | import org.sayandev.sayanvanish.api.feature.RegisteredFeature 7 | import org.sayandev.sayanvanish.api.feature.category.FeatureCategories 8 | import org.sayandev.sayanvanish.bukkit.api.SayanVanishBukkitAPI.Companion.user 9 | import org.sayandev.sayanvanish.bukkit.feature.ListenedFeature 10 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 11 | 12 | @RegisteredFeature 13 | @ConfigSerializable 14 | class FeaturePreventDamage: ListenedFeature("prevent_damage", category = FeatureCategories.PREVENTION) { 15 | 16 | @EventHandler 17 | private fun onEntityDamage(event: EntityDamageByEntityEvent) { 18 | val user = (event.entity as? Player)?.user() ?: return 19 | if (!isActive(user)) return 20 | if (user.isVanished) { 21 | event.isCancelled = true 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/feature/features/prevent/FeaturePreventInteract.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.feature.features.prevent 2 | 3 | import com.cryptomorin.xseries.XMaterial 4 | import org.bukkit.block.Container 5 | import org.bukkit.event.EventHandler 6 | import org.bukkit.event.EventPriority 7 | import org.bukkit.event.block.Action 8 | import org.bukkit.event.player.PlayerInteractEvent 9 | import org.sayandev.sayanvanish.api.feature.Configurable 10 | import org.sayandev.sayanvanish.api.feature.RegisteredFeature 11 | import org.sayandev.sayanvanish.api.feature.category.FeatureCategories 12 | import org.sayandev.sayanvanish.bukkit.api.SayanVanishBukkitAPI.Companion.user 13 | import org.sayandev.sayanvanish.bukkit.feature.ListenedFeature 14 | import org.sayandev.stickynote.bukkit.utils.ServerVersion 15 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 16 | import org.spongepowered.configurate.objectmapping.meta.Comment 17 | 18 | @RegisteredFeature 19 | @ConfigSerializable 20 | class FeaturePreventInteract( 21 | @Comment("Prevent players from activating pressure plates while vanished") 22 | @Configurable val pressurePlateTrigger: Boolean = true, 23 | @Comment("Prevent players from interacting with big dripleaf while vanished") 24 | @Configurable val dripLeaf: Boolean = true, 25 | @Comment("Prevent players from interacting") 26 | @Configurable val interact: Boolean = false, 27 | @Configurable val tripwire: Boolean = true, 28 | @Configurable val button: Boolean = true, 29 | ) : ListenedFeature("prevent_interact_event", category = FeatureCategories.PREVENTION) { 30 | 31 | @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) 32 | private fun cancelInteract(event: PlayerInteractEvent) { 33 | if (ServerVersion.supports(13)) { 34 | if (event.clickedBlock?.state is Container) return 35 | } 36 | val user = event.player.user() ?: return 37 | if (!isActive(user)) return 38 | if (user.isVanished) { 39 | val isPressurePlate = pressurePlateTrigger && event.action == Action.PHYSICAL && event.clickedBlock?.type?.name?.contains("PLATE") == true 40 | val isDripLeaf = dripLeaf && event.action == Action.PHYSICAL && event.clickedBlock?.type?.name?.equals("BIG_DRIPLEAF") == true 41 | if (interact || (isPressurePlate && pressurePlateTrigger) || (isDripLeaf && dripLeaf)) { 42 | event.isCancelled = true 43 | } 44 | } 45 | } 46 | 47 | @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) 48 | private fun cancelTripwireInteract(event: PlayerInteractEvent) { 49 | if (!tripwire) return 50 | val block = event.clickedBlock ?: return 51 | if (event.action != Action.PHYSICAL) return 52 | if (block.type != XMaterial.TRIPWIRE.get()!! && block.type != XMaterial.STRING.get()!!) return 53 | val player = event.player 54 | val user = player.user() ?: return 55 | if (!isActive(user)) return 56 | if (!user.isVanished) return 57 | event.isCancelled = true 58 | } 59 | 60 | @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) 61 | private fun cancelButtonInteract(event: PlayerInteractEvent) { 62 | if (!button) return 63 | val block = event.clickedBlock ?: return 64 | if (event.action != Action.RIGHT_CLICK_BLOCK) return 65 | if (!block.type.name.contains("BUTTON")) return 66 | val player = event.player 67 | val user = player.user() ?: return 68 | if (!isActive(user)) return 69 | if (!user.isVanished) return 70 | event.isCancelled = true 71 | } 72 | 73 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/feature/features/prevent/FeaturePreventPickup.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.feature.features.prevent 2 | 3 | import org.bukkit.entity.Player 4 | import org.bukkit.event.EventHandler 5 | import org.bukkit.event.entity.EntityPickupItemEvent 6 | import org.bukkit.event.player.PlayerPickupItemEvent 7 | import org.sayandev.sayanvanish.api.feature.RegisteredFeature 8 | import org.sayandev.sayanvanish.api.feature.category.FeatureCategories 9 | import org.sayandev.sayanvanish.bukkit.api.SayanVanishBukkitAPI.Companion.user 10 | import org.sayandev.sayanvanish.bukkit.feature.ListenedFeature 11 | import org.sayandev.stickynote.bukkit.utils.ServerVersion 12 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 13 | 14 | @RegisteredFeature 15 | @ConfigSerializable 16 | class FeaturePreventPickup: ListenedFeature("prevent_pickup", category = FeatureCategories.PREVENTION) { 17 | 18 | override var condition: Boolean = ServerVersion.supports(9) 19 | 20 | @EventHandler 21 | private fun onPickupItem(event: EntityPickupItemEvent) { 22 | val user = (event.entity as? Player)?.user() ?: return 23 | if (!isActive(user)) return 24 | if (user.isVanished) { 25 | event.isCancelled = true 26 | } 27 | } 28 | } 29 | 30 | @RegisteredFeature 31 | @ConfigSerializable 32 | class FeatureLegacyPreventPickup: ListenedFeature("legacy_prevent_pickup", category = FeatureCategories.PREVENTION) { 33 | 34 | override var condition: Boolean = !ServerVersion.supports(9) 35 | 36 | @EventHandler 37 | @Suppress("DEPRECATION") 38 | private fun onPickupItem(event: PlayerPickupItemEvent) { 39 | val user = event.player.user() ?: return 40 | if (!isActive(user)) return 41 | if (user.isVanished) { 42 | event.isCancelled = true 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/feature/features/prevent/FeaturePreventPush.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.feature.features.prevent 2 | 3 | import org.bukkit.event.EventHandler 4 | import org.bukkit.scoreboard.Team 5 | import org.sayandev.sayanvanish.api.feature.RegisteredFeature 6 | import org.sayandev.sayanvanish.api.feature.category.FeatureCategories 7 | import org.sayandev.sayanvanish.bukkit.api.event.BukkitUserUnVanishEvent 8 | import org.sayandev.sayanvanish.bukkit.api.event.BukkitUserVanishEvent 9 | import org.sayandev.sayanvanish.bukkit.feature.ListenedFeature 10 | import org.sayandev.stickynote.bukkit.StickyNote 11 | import org.sayandev.stickynote.bukkit.hasPlugin 12 | import org.sayandev.stickynote.bukkit.warn 13 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 14 | 15 | @RegisteredFeature 16 | @ConfigSerializable 17 | class FeaturePreventPush: ListenedFeature("prevent_push", enabled = false, category = FeatureCategories.PREVENTION) { 18 | 19 | @EventHandler 20 | private fun onVanish(event: BukkitUserVanishEvent) { 21 | val user = event.user 22 | if (!isActive(user)) return 23 | val player = user.player() ?: return 24 | 25 | 26 | if (hasPlugin("eGlow")) { 27 | StickyNote.warn("tried to register vanished team for user ${user.username} but $id feature is not compatible with eGlow. disable $id feature to remove the warning.") 28 | return 29 | } 30 | 31 | var team = player.scoreboard.getTeam("Vanished") 32 | if (team == null) { 33 | team = player.scoreboard.registerNewTeam("Vanished") 34 | } 35 | team.setOption(Team.Option.COLLISION_RULE, Team.OptionStatus.NEVER) 36 | team.addEntry(player.name) 37 | } 38 | 39 | @EventHandler 40 | private fun onUnVanish(event: BukkitUserUnVanishEvent) { 41 | val user = event.user 42 | if (!isActive(user)) return 43 | val player = user.player() ?: return 44 | /* Make sure the player has `Vanished` team before removing it. Prevents 1.21 players to get kicked with ISE: 45 | java.lang.IllegalStateException: Player is either on another team or not on any team. Cannot remove from team 'Vanished'.*/ 46 | val teams = player.scoreboard.teams 47 | if (teams.find { it.name == "Vanished" } == null) return 48 | 49 | if (hasPlugin("eGlow")) { 50 | StickyNote.warn("tried to remove vanished team for user ${user.username} but $id feature is not compatible with eGlow. disable $id feature to remove the warning.") 51 | return 52 | } 53 | 54 | player.scoreboard.getTeam("Vanished")?.removeEntry(player.name) 55 | } 56 | 57 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/feature/features/prevent/FeaturePreventRaidTrigger.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.feature.features.prevent 2 | 3 | import org.bukkit.event.EventHandler 4 | import org.bukkit.event.raid.RaidTriggerEvent 5 | import org.sayandev.sayanvanish.api.feature.RegisteredFeature 6 | import org.sayandev.sayanvanish.api.feature.category.FeatureCategories 7 | import org.sayandev.sayanvanish.bukkit.api.SayanVanishBukkitAPI.Companion.user 8 | import org.sayandev.sayanvanish.bukkit.feature.ListenedFeature 9 | import org.sayandev.stickynote.bukkit.event.registerListener 10 | import org.sayandev.stickynote.bukkit.utils.ServerVersion 11 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 12 | 13 | @RegisteredFeature 14 | @ConfigSerializable 15 | class FeaturePreventRaidTrigger: ListenedFeature("prevent_raid_trigger", category = FeatureCategories.PREVENTION) { 16 | 17 | @Transient 18 | override var condition: Boolean = ServerVersion.supports(15) 19 | 20 | @EventHandler 21 | private fun onRaidTrigger(event: RaidTriggerEvent) { 22 | val user = event.player.user() ?: return 23 | if (!isActive(user)) return 24 | if (user.isVanished) { 25 | event.isCancelled = true 26 | } 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/feature/features/prevent/FeaturePreventSculk.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.feature.features.prevent 2 | 3 | import org.bukkit.entity.Player 4 | import org.bukkit.event.EventHandler 5 | import org.bukkit.event.EventPriority 6 | import org.bukkit.event.block.Action 7 | import org.bukkit.event.block.BlockReceiveGameEvent 8 | import org.bukkit.event.player.PlayerInteractEvent 9 | import org.sayandev.sayanvanish.api.feature.Configurable 10 | import org.sayandev.sayanvanish.api.feature.RegisteredFeature 11 | import org.sayandev.sayanvanish.api.feature.category.FeatureCategories 12 | import org.sayandev.sayanvanish.bukkit.api.SayanVanishBukkitAPI.Companion.user 13 | import org.sayandev.sayanvanish.bukkit.feature.ListenedFeature 14 | import org.sayandev.stickynote.bukkit.utils.ServerVersion 15 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 16 | 17 | @RegisteredFeature 18 | @ConfigSerializable 19 | class FeaturePreventSculk( 20 | @Configurable val preventSculkSensor: Boolean = true, 21 | @Configurable val preventShriek: Boolean = true 22 | ): ListenedFeature("prevent_sculk", category = FeatureCategories.PREVENTION) { 23 | 24 | val sculkBlocks = listOf( 25 | "SCULK_SENSOR", 26 | "CALIBRATED_SCULK_SENSOR", 27 | "SCULK_SHRIEKER" 28 | ) 29 | 30 | @Transient 31 | override var condition: Boolean = ServerVersion.supports(19) 32 | 33 | @EventHandler(priority = EventPriority.HIGH) 34 | private fun cancelSculkSensor(event: BlockReceiveGameEvent) { 35 | if (!preventSculkSensor) return 36 | val player = event.entity as? Player ?: return 37 | val user = player.user() ?: return 38 | if (!user.isVanished) return 39 | if (!isActive(user)) return 40 | event.isCancelled = true 41 | } 42 | 43 | @EventHandler 44 | private fun cancelShriek(event: PlayerInteractEvent) { 45 | if (!preventShriek) return 46 | val block = event.clickedBlock ?: return 47 | if (event.action != Action.PHYSICAL) return 48 | if (!sculkBlocks.contains(block.type.name)) return 49 | val player = event.player 50 | val user = player.user() ?: return 51 | if (!user.isVanished) return 52 | if (!isActive(user)) return 53 | event.isCancelled = true 54 | } 55 | 56 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/feature/features/prevent/FeaturePreventServerPing.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.feature.features.prevent 2 | 3 | import com.destroystokyo.paper.event.server.PaperServerListPingEvent 4 | import org.bukkit.event.EventHandler 5 | import org.sayandev.sayanvanish.api.feature.RegisteredFeature 6 | import org.sayandev.sayanvanish.api.feature.category.FeatureCategories 7 | import org.sayandev.sayanvanish.bukkit.api.SayanVanishBukkitAPI 8 | import org.sayandev.sayanvanish.bukkit.feature.ListenedFeature 9 | import org.sayandev.stickynote.bukkit.StickyNote 10 | import org.sayandev.stickynote.bukkit.utils.ServerVersion 11 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 12 | 13 | @RegisteredFeature 14 | @ConfigSerializable 15 | class FeaturePreventServerPing: ListenedFeature("prevent_server_ping", category = FeatureCategories.PREVENTION) { 16 | 17 | @EventHandler 18 | private fun onPing(event: PaperServerListPingEvent) { 19 | if (!isActive()) return 20 | val vanishedPlayers = SayanVanishBukkitAPI.getInstance().getVanishedUsers().filter { it.player() != null } 21 | event.numPlayers -= vanishedPlayers.count() 22 | if (StickyNote.isPaper && ServerVersion.supports(21)) { 23 | event.listedPlayers.removeIf { profile -> vanishedPlayers.map { vanishedPlayer -> vanishedPlayer.uniqueId }.contains(profile.id) } 24 | } else { 25 | event.playerSample.removeIf { profile -> vanishedPlayers.map { vanishedPlayer -> vanishedPlayer.uniqueId }.contains(profile.id) } 26 | } 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/feature/features/prevent/FeaturePreventSpawnerSpawn.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.feature.features.prevent 2 | 3 | import com.destroystokyo.paper.event.entity.PreSpawnerSpawnEvent 4 | import org.bukkit.GameMode 5 | import org.bukkit.event.EventHandler 6 | import org.bukkit.event.Listener 7 | import org.sayandev.sayanvanish.api.feature.RegisteredFeature 8 | import org.sayandev.sayanvanish.api.feature.category.FeatureCategories 9 | import org.sayandev.sayanvanish.bukkit.api.SayanVanishBukkitAPI.Companion.user 10 | import org.sayandev.sayanvanish.bukkit.feature.ListenedFeature 11 | import org.sayandev.stickynote.bukkit.StickyNote 12 | import org.sayandev.stickynote.bukkit.onlinePlayers 13 | import org.sayandev.stickynote.bukkit.utils.ServerVersion 14 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 15 | 16 | @RegisteredFeature 17 | @ConfigSerializable 18 | class FeaturePreventSpawnerSpawn: ListenedFeature("prevent_spawner_spawn", category = FeatureCategories.PREVENTION) { 19 | 20 | @Transient 21 | override var condition: Boolean = StickyNote.isPaper && ServerVersion.supports(16) 22 | 23 | /** 24 | * @note This feature will be too performance intense if i want to check feature is active or not on a per-player basis 25 | * */ 26 | @EventHandler 27 | private fun onPreSpawn(event: PreSpawnerSpawnEvent) { 28 | if (!isActive()) return 29 | val nearPlayers = onlinePlayers 30 | .filter { player -> player.gameMode != GameMode.SPECTATOR && player.world == event.spawnerLocation.world && player.location.distance(event.spawnerLocation) <= 256 } 31 | val allIsVanished = nearPlayers.all { it.user()?.isVanished == true } 32 | if (allIsVanished) { 33 | event.isCancelled = true 34 | } 35 | } 36 | 37 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/feature/features/prevent/FeaturePreventTabComplete.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.feature.features.prevent 2 | 3 | import org.bukkit.entity.Player 4 | import org.bukkit.event.EventHandler 5 | import org.bukkit.event.EventPriority 6 | import org.bukkit.event.server.TabCompleteEvent 7 | import org.sayandev.sayanvanish.api.Permission 8 | import org.sayandev.sayanvanish.api.User 9 | import org.sayandev.sayanvanish.api.feature.Configurable 10 | import org.sayandev.sayanvanish.api.feature.RegisteredFeature 11 | import org.sayandev.sayanvanish.api.feature.category.FeatureCategories 12 | import org.sayandev.sayanvanish.bukkit.api.SayanVanishBukkitAPI 13 | import org.sayandev.sayanvanish.bukkit.api.SayanVanishBukkitAPI.Companion.getOrCreateUser 14 | import org.sayandev.sayanvanish.bukkit.feature.ListenedFeature 15 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 16 | import org.spongepowered.configurate.objectmapping.meta.Comment 17 | 18 | @RegisteredFeature 19 | @ConfigSerializable 20 | class FeaturePreventTabComplete( 21 | @Comment("Whether to keep vanished player in tab completion if the player that is getting the suggestion has a higher level of vanish.") 22 | @Configurable val checkVanishLevel: Boolean = false 23 | ): ListenedFeature("prevent_tab_complete", category = FeatureCategories.PREVENTION) { 24 | 25 | @EventHandler(priority = EventPriority.HIGHEST) 26 | private fun onTabComplete(event: TabCompleteEvent) { 27 | val player = event.sender as? Player ?: return 28 | val user = player.getOrCreateUser() 29 | if (!isActive(user)) return 30 | val vanishedUsers = SayanVanishBukkitAPI.getInstance().getVanishedUsers() 31 | val completions = event.completions.toMutableSet() 32 | if (!user.hasPermission(Permission.VANISH) || !checkVanishLevel) { 33 | event.completions = completions 34 | .filter { completion -> !vanishedUsers.map(User::username).contains(completion) } 35 | return 36 | } 37 | 38 | event.completions = completions.filter { completion -> 39 | !vanishedUsers 40 | .filter { vanishedUser -> vanishedUser.vanishLevel > user.vanishLevel } 41 | .map(User::username).contains(completion) 42 | } 43 | } 44 | 45 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/feature/features/prevent/PreventFoodLevelChange.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.feature.features.prevent 2 | 3 | import org.bukkit.entity.Player 4 | import org.bukkit.event.EventHandler 5 | import org.bukkit.event.entity.FoodLevelChangeEvent 6 | import org.sayandev.sayanvanish.api.feature.RegisteredFeature 7 | import org.sayandev.sayanvanish.api.feature.category.FeatureCategories 8 | import org.sayandev.sayanvanish.bukkit.api.SayanVanishBukkitAPI.Companion.user 9 | import org.sayandev.sayanvanish.bukkit.feature.ListenedFeature 10 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 11 | 12 | @RegisteredFeature 13 | @ConfigSerializable 14 | class PreventFoodLevelChange( 15 | val ignoreIfIncrease: Boolean = true 16 | ): ListenedFeature("prevent_food_level_change", category = FeatureCategories.PREVENTION) { 17 | 18 | @EventHandler 19 | private fun onBlockBreak(event: FoodLevelChangeEvent) { 20 | if (ignoreIfIncrease && event.foodLevel > event.entity.foodLevel) return 21 | val player = event.entity as? Player ?: return 22 | val user = player.user() ?: return 23 | if (!isActive(user)) return 24 | if (user.isVanished) { 25 | event.isCancelled = true 26 | } 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/health/HealthCheckRequestPublisher.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.health 2 | 3 | import org.sayandev.sayanvanish.api.health.HealthCheckData 4 | import org.sayandev.stickynote.bukkit.messaging.publisher.ProxyPluginMessagePublisher 5 | import org.sayandev.stickynote.bukkit.plugin 6 | 7 | object HealthCheckRequestPublisher : ProxyPluginMessagePublisher( 8 | plugin.name.lowercase(), 9 | "hc", 10 | Unit::class.java, 11 | HealthCheckData::class.java 12 | ) -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/health/ServerInfoPublisher.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.health 2 | 3 | import org.sayandev.sayanvanish.api.database.databaseConfig 4 | import org.sayandev.sayanvanish.api.health.HealthCheckData 5 | import org.sayandev.sayanvanish.bukkit.config.settings 6 | import org.sayandev.stickynote.bukkit.messaging.publisher.PluginMessagePublisher 7 | import org.sayandev.stickynote.bukkit.plugin 8 | 9 | object ServerInfoPublisher : PluginMessagePublisher( 10 | plugin.name.lowercase(), 11 | "info", 12 | Unit::class.java, 13 | HealthCheckData.ServerInfo::class.java, 14 | true 15 | ) { 16 | override fun handle(payload: Unit): HealthCheckData.ServerInfo { 17 | return HealthCheckData.ServerInfo( 18 | settings.general.serverId, 19 | null, 20 | settings.general.proxyMode, 21 | databaseConfig.method, 22 | databaseConfig.sql.method, 23 | System.currentTimeMillis() 24 | ) 25 | } 26 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/utils/PlayerUtils.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.utils 2 | 3 | import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver 4 | import org.bukkit.OfflinePlayer 5 | import org.bukkit.command.CommandSender 6 | import org.sayandev.sayanvanish.bukkit.config.language 7 | import org.sayandev.sayanvanish.bukkit.config.settings 8 | import org.sayandev.stickynote.bukkit.utils.AdventureUtils 9 | import org.sayandev.stickynote.bukkit.warn 10 | 11 | object PlayerUtils { 12 | 13 | fun CommandSender.sendComponent(content: String, vararg placeholders: TagResolver) { 14 | if (content.isBlank()) return 15 | 16 | val prefix = language.general.prefix 17 | AdventureUtils.sendComponent(this, if (settings.general.includePrefixInMessages) { 18 | prefix + content 19 | } else { 20 | content 21 | }, *placeholders) 22 | } 23 | 24 | fun CommandSender.sendRawComponent(content: String, vararg placeholders: TagResolver) { 25 | if (content.isBlank()) return 26 | AdventureUtils.sendComponent(this, content, *placeholders) 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/utils/ServerUtils.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bukkit.utils 2 | 3 | import com.google.gson.GsonBuilder 4 | import com.google.gson.JsonArray 5 | import com.google.gson.JsonObject 6 | import org.bukkit.plugin.Plugin 7 | import org.sayandev.sayanvanish.bukkit.api.SayanVanishBukkitAPI 8 | import org.sayandev.stickynote.bukkit.onlinePlayers 9 | import org.sayandev.stickynote.bukkit.plugin 10 | import org.sayandev.stickynote.bukkit.server 11 | import java.time.Instant 12 | 13 | 14 | object ServerUtils { 15 | 16 | val gson = GsonBuilder().setPrettyPrinting().create() 17 | 18 | fun getServerData(additionalData: Map = emptyMap()): String { 19 | val jsonObject = JsonObject() 20 | jsonObject.add("paste-info", JsonObject().apply { 21 | this.addProperty("instant", Instant.now().toString()) 22 | }) 23 | 24 | // TODO: re-add system info 25 | jsonObject.add("machine", JsonObject().apply { 26 | this.addProperty("operating-system", System.getProperty("os.name")) 27 | // this.addProperty("processor", SystemInfo().hardware.processor.processorIdentifier.name) 28 | this.addProperty("available-processors", Runtime.getRuntime().availableProcessors()) 29 | this.addProperty("free-memory", Runtime.getRuntime().freeMemory() / 1024) 30 | val maxMemory = Runtime.getRuntime().maxMemory() 31 | this.addProperty("max-memory", if (maxMemory == Long.MAX_VALUE) -1 else (maxMemory / 1024)) 32 | // this.addProperty("system-memory", SystemInfo().hardware.memory.total / 1024) 33 | }) 34 | 35 | jsonObject.add("server", JsonObject().apply { 36 | this.addProperty("name", server.name) 37 | this.addProperty("version", server.version) 38 | this.addProperty("bukkit-version", server.bukkitVersion) 39 | this.addProperty("motd", server.motd) 40 | this.addProperty("online-mode", server.onlineMode) 41 | this.addProperty("port", server.port) 42 | this.addProperty("players", onlinePlayers.joinToString(", ") { it.name }) 43 | this.addProperty("operators", server.operators.filter { it.player != null }.joinToString(", ") { it.name ?: it.uniqueId.toString() }) 44 | this.addProperty("plugins", server.pluginManager.plugins.joinToString(", ") { it.name }) 45 | }) 46 | 47 | jsonObject.add("vanished-users", JsonArray().apply { 48 | SayanVanishBukkitAPI.getInstance().getVanishedUsers().map { it.username }.forEach(this::add) 49 | }) 50 | 51 | jsonObject.add("plugin", serializePlugin(plugin)) 52 | 53 | jsonObject.add("plugins", JsonArray().apply { 54 | server.pluginManager.plugins.filter { it != plugin }.forEach { serverPlugin -> 55 | this.add(serializePlugin(serverPlugin)) 56 | } 57 | }) 58 | 59 | jsonObject.add("additional-data", JsonObject().apply { 60 | additionalData.map { this.addProperty(it.key, it.value) } 61 | }) 62 | 63 | return gson.toJson(jsonObject) 64 | } 65 | 66 | fun serializePlugin(plugin: Plugin): JsonObject { 67 | val description = plugin.description 68 | return JsonObject().apply { 69 | this.add(description.name, JsonObject().apply { 70 | this.addProperty("version", description.version) 71 | this.addProperty("api-version", description.apiVersion) 72 | this.addProperty("libraries", description.libraries.joinToString(", ")) 73 | this.addProperty("depend", description.depend.joinToString(", ")) 74 | this.addProperty("soft-depend", description.softDepend.joinToString(", ")) 75 | }) 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /sayanvanish-proxy/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.sayandev.plugin.StickyNoteModules 2 | 3 | allprojects { 4 | stickynote { 5 | modules(StickyNoteModules.PROXY) 6 | } 7 | 8 | dependencies { 9 | implementation(project(":sayanvanish-api")) 10 | } 11 | } -------------------------------------------------------------------------------- /sayanvanish-proxy/sayanvanish-proxy-bungeecord/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.sayandev.plugin.StickyNoteModules 2 | 3 | plugins { 4 | id("net.minecrell.plugin-yml.bungee") version "0.6.0" 5 | } 6 | 7 | stickynote { 8 | modules(StickyNoteModules.BUNGEECORD) 9 | } 10 | 11 | dependencies { 12 | compileOnly(libs.bungeecord.api) 13 | 14 | api(project(":sayanvanish-proxy")) 15 | } 16 | 17 | modrinth { 18 | loaders.set(listOf("bungeecord", "waterfall")) 19 | detectLoaders.set(false) 20 | } 21 | 22 | bungee { 23 | name = rootProject.name 24 | version = rootProject.version as String 25 | description = rootProject.description 26 | 27 | main = "${rootProject.group}.${findProperty("slug")!! as String}.bungeecord.${rootProject.name}" 28 | 29 | author = findProperty("author")!! as String 30 | } 31 | 32 | modrinth { 33 | loaders.set(listOf( 34 | "bungeecord", 35 | "waterfall", 36 | )) 37 | } -------------------------------------------------------------------------------- /sayanvanish-proxy/sayanvanish-proxy-bungeecord/src/main/kotlin/org/sayandev/sayanvanish/bungeecord/SayanVanish.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bungeecord 2 | 3 | import net.md_5.bungee.api.plugin.Plugin 4 | import org.sayandev.sayanvanish.api.Platform 5 | import org.sayandev.sayanvanish.api.SayanVanishAPI 6 | import org.sayandev.sayanvanish.api.database.DatabaseMethod 7 | import org.sayandev.sayanvanish.api.database.databaseConfig 8 | import org.sayandev.sayanvanish.api.database.sql.SQLDatabase 9 | import org.sayandev.sayanvanish.bungeecord.api.SayanVanishBungeeAPI 10 | import org.sayandev.sayanvanish.proxy.config.settings 11 | import org.sayandev.sayanvanish.velocity.VanishManager 12 | import org.sayandev.stickynote.bungeecord.StickyNote 13 | import org.sayandev.stickynote.bungeecord.dataDirectory 14 | import org.sayandev.stickynote.bungeecord.registerListener 15 | import org.sayandev.stickynote.bungeecord.server 16 | import org.sayandev.stickynote.loader.bungee.StickyNoteBungeeLoader 17 | import java.util.concurrent.TimeUnit 18 | 19 | class SayanVanish : Plugin() { 20 | 21 | override fun onEnable() { 22 | StickyNoteBungeeLoader(this) 23 | // Do NOT set server id here, SettingsConfig can't be used because it depends on Platform rootDirectory 24 | Platform.get().rootDirectory = dataDirectory 25 | if (!Platform.setAndRegister(Platform("bungeecord", logger, dataDirectory, ""))) return 26 | Platform.get().serverId = settings.general.serverId 27 | 28 | SayanVanishBungeeAPI() 29 | 30 | registerListener(VanishManager) 31 | 32 | if (settings.general.purgeOnlineHistoryOnStartup) { 33 | for (onlineServer in server.servers) { 34 | SayanVanishBungeeAPI.getInstance().database.purgeBasic(onlineServer.value.name) 35 | } 36 | SayanVanishBungeeAPI.getInstance().database.purgeBasic(settings.general.serverId) 37 | } 38 | 39 | StickyNote.run({ 40 | SayanVanishBungeeAPI.getInstance().database.getUsersAsync { users -> 41 | SayanVanishBungeeAPI.getInstance().database.cache = users.associateBy { it.uniqueId }.toMutableMap() 42 | SayanVanishAPI.getInstance().database.cache = users.associateBy { it.uniqueId }.toMutableMap() 43 | } 44 | }, settings.general.basicCacheUpdatePeriodMillis, settings.general.basicCacheUpdatePeriodMillis, TimeUnit.MILLISECONDS) 45 | 46 | StickyNote.run({ 47 | if (databaseConfig.method == DatabaseMethod.SQL) { 48 | SayanVanishBungeeAPI.getInstance().database.getBasicUsersAsync { users -> 49 | (SayanVanishBungeeAPI.getInstance().database as SQLDatabase).basicCache = users.associateBy { it.uniqueId }.toMutableMap() 50 | (SayanVanishAPI.getInstance().database as SQLDatabase).basicCache = users.associateBy { it.uniqueId }.toMutableMap() 51 | } 52 | } 53 | }, settings.general.cacheUpdatePeriodMillis, settings.general.cacheUpdatePeriodMillis, TimeUnit.MILLISECONDS) 54 | } 55 | } -------------------------------------------------------------------------------- /sayanvanish-proxy/sayanvanish-proxy-bungeecord/src/main/kotlin/org/sayandev/sayanvanish/bungeecord/VanishManager.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.velocity 2 | 3 | import net.md_5.bungee.api.event.PlayerDisconnectEvent 4 | import net.md_5.bungee.api.event.ServerConnectedEvent 5 | import net.md_5.bungee.api.plugin.Listener 6 | import net.md_5.bungee.event.EventHandler 7 | import org.sayandev.sayanvanish.api.BasicUser 8 | import org.sayandev.sayanvanish.api.Platform 9 | import org.sayandev.sayanvanish.bungeecord.api.SayanVanishBungeeAPI 10 | import org.sayandev.sayanvanish.bungeecord.api.SayanVanishBungeeAPI.Companion.getOrCreateUser 11 | 12 | object VanishManager : Listener { 13 | 14 | @EventHandler 15 | fun onPostLogin(event: ServerConnectedEvent) { 16 | val player = event.player ?: return 17 | SayanVanishBungeeAPI.getInstance().database.addBasicUser(BasicUser.create(player.uniqueId, player.name, player.server.info.name ?: Platform.get().id)) 18 | val user = player.getOrCreateUser() 19 | } 20 | 21 | fun onDisconnect(event: PlayerDisconnectEvent) { 22 | val player = event.player ?: return 23 | SayanVanishBungeeAPI.getInstance().database.removeBasicUser(player.uniqueId) 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /sayanvanish-proxy/sayanvanish-proxy-bungeecord/src/main/kotlin/org/sayandev/sayanvanish/bungeecord/api/BungeeUser.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bungeecord.api 2 | 3 | import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver 4 | import net.md_5.bungee.api.connection.ProxiedPlayer 5 | import org.sayandev.sayanvanish.api.SayanVanishAPI 6 | import org.sayandev.sayanvanish.api.User 7 | import org.sayandev.sayanvanish.api.VanishOptions 8 | import org.sayandev.sayanvanish.bungeecord.event.BungeeUserUnVanishEvent 9 | import org.sayandev.sayanvanish.bungeecord.event.BungeeUserVanishEvent 10 | import org.sayandev.sayanvanish.bungeecord.utils.PlayerUtils.sendComponent 11 | import org.sayandev.sayanvanish.proxy.config.settings 12 | import org.sayandev.stickynote.bungeecord.utils.AdventureUtils.component 13 | import org.sayandev.stickynote.bungeecord.utils.AdventureUtils.sendMessage 14 | import org.sayandev.stickynote.bungeecord.utils.AdventureUtils.sendActionbar 15 | import org.sayandev.stickynote.bungeecord.StickyNote 16 | import org.sayandev.stickynote.bungeecord.plugin 17 | import java.util.UUID 18 | 19 | 20 | open class BungeeUser( 21 | override val uniqueId: UUID, 22 | override var username: String 23 | ) : User { 24 | 25 | override var serverId = settings.general.serverId 26 | override var currentOptions = VanishOptions.defaultOptions() 27 | override var isVanished = false 28 | override var isOnline: Boolean = SayanVanishAPI.getInstance().database.hasBasicUser(uniqueId, true) 29 | override var vanishLevel: Int = 1 30 | 31 | fun stateText(isVanished: Boolean = this.isVanished) = if (isVanished) "ON" else "OFF" 32 | 33 | fun player(): ProxiedPlayer? = StickyNote.getPlayer(uniqueId) 34 | 35 | override fun vanish(options: VanishOptions) { 36 | val vanishEvent = plugin.proxy.pluginManager.callEvent(BungeeUserVanishEvent(this, options)) 37 | if (vanishEvent.isCancelled) return 38 | 39 | val options = vanishEvent.options 40 | currentOptions = options 41 | 42 | database.addToQueue(uniqueId, true) 43 | super.vanish(options) 44 | } 45 | 46 | override fun unVanish(options: VanishOptions) { 47 | val vanishEvent = plugin.proxy.pluginManager.callEvent(BungeeUserUnVanishEvent(this, options)) 48 | if (vanishEvent.isCancelled) return 49 | 50 | val options = vanishEvent.options 51 | currentOptions = options 52 | 53 | database.addToQueue(uniqueId, false) 54 | super.unVanish(options) 55 | } 56 | 57 | override fun hasPermission(permission: String): Boolean { 58 | return player()?.hasPermission(permission) == true 59 | } 60 | 61 | override fun sendComponent(content: String, vararg placeholder: TagResolver) { 62 | player()?.sendComponent(content.component()) 63 | } 64 | 65 | override fun sendActionbar(content: String, vararg placeholder: TagResolver) { 66 | player()?.sendActionbar(content.component()) 67 | } 68 | 69 | companion object { 70 | @JvmStatic 71 | fun fromUser(user: User): BungeeUser { 72 | return BungeeUser(user.uniqueId, user.username).apply { 73 | this.isOnline = user.isOnline 74 | this.isVanished = user.isVanished 75 | this.vanishLevel = user.vanishLevel 76 | } 77 | } 78 | } 79 | 80 | } -------------------------------------------------------------------------------- /sayanvanish-proxy/sayanvanish-proxy-bungeecord/src/main/kotlin/org/sayandev/sayanvanish/bungeecord/api/SayanVanishBungeeAPI.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bungeecord.api 2 | 3 | import net.md_5.bungee.api.connection.ProxiedPlayer 4 | import org.sayandev.sayanvanish.api.SayanVanishAPI 5 | import org.sayandev.sayanvanish.api.database.databaseConfig 6 | import java.util.UUID 7 | 8 | val database = SayanVanishBungeeAPI.getInstance().database 9 | 10 | class SayanVanishBungeeAPI : SayanVanishAPI(BungeeUser::class.java) { 11 | companion object { 12 | private val defaultInstance = SayanVanishBungeeAPI() 13 | 14 | @JvmStatic 15 | fun getInstance(): SayanVanishAPI { 16 | return defaultInstance 17 | } 18 | 19 | @JvmStatic 20 | public fun UUID.user(): BungeeUser? { 21 | return getInstance().getUser(this) 22 | } 23 | 24 | @JvmStatic 25 | public fun ProxiedPlayer.user(): BungeeUser? { 26 | return getInstance().getUser(this.uniqueId) 27 | } 28 | 29 | @JvmStatic 30 | fun ProxiedPlayer.getOrCreateUser(): BungeeUser { 31 | return getInstance().getUser(this.uniqueId) ?: BungeeUser(this.uniqueId, this.name ?: "N/A") 32 | } 33 | 34 | @JvmStatic 35 | fun ProxiedPlayer.getOrAddUser(): BungeeUser { 36 | return getInstance().getUser(this.uniqueId) ?: let { 37 | val newUser = BungeeUser(this.uniqueId, this.name ?: "N/A") 38 | getInstance().database.addUser(newUser) 39 | newUser 40 | } 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /sayanvanish-proxy/sayanvanish-proxy-bungeecord/src/main/kotlin/org/sayandev/sayanvanish/bungeecord/event/BungeeUserUnVanishEvent.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bungeecord.event 2 | 3 | import net.md_5.bungee.api.plugin.Cancellable 4 | import net.md_5.bungee.api.plugin.Event 5 | import org.sayandev.sayanvanish.api.VanishOptions 6 | import org.sayandev.sayanvanish.bungeecord.api.BungeeUser 7 | 8 | class BungeeUserUnVanishEvent(val user: BungeeUser, val options: VanishOptions): Event(), Cancellable { 9 | private var cancelled = false 10 | 11 | override fun isCancelled(): Boolean { 12 | return cancelled 13 | } 14 | 15 | override fun setCancelled(cancelled: Boolean) { 16 | this.cancelled = cancelled 17 | } 18 | } -------------------------------------------------------------------------------- /sayanvanish-proxy/sayanvanish-proxy-bungeecord/src/main/kotlin/org/sayandev/sayanvanish/bungeecord/event/BungeeUserVanishEvent.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bungeecord.event 2 | 3 | import net.md_5.bungee.api.plugin.Cancellable 4 | import net.md_5.bungee.api.plugin.Event 5 | import org.sayandev.sayanvanish.api.VanishOptions 6 | import org.sayandev.sayanvanish.bungeecord.api.BungeeUser 7 | 8 | class BungeeUserVanishEvent(val user: BungeeUser, val options: VanishOptions): Event(), Cancellable { 9 | private var cancelled = false 10 | 11 | override fun isCancelled(): Boolean { 12 | return cancelled 13 | } 14 | 15 | override fun setCancelled(cancelled: Boolean) { 16 | this.cancelled = cancelled 17 | } 18 | } -------------------------------------------------------------------------------- /sayanvanish-proxy/sayanvanish-proxy-bungeecord/src/main/kotlin/org/sayandev/sayanvanish/bungeecord/feature/HookFeature.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bungeecord.feature 2 | 3 | import org.sayandev.sayanvanish.api.BasicUser 4 | import org.sayandev.sayanvanish.api.feature.category.FeatureCategories 5 | import org.sayandev.stickynote.bungeecord.hasPlugin 6 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 7 | 8 | @ConfigSerializable 9 | abstract class HookFeature( 10 | id: String, 11 | @Transient val plugin: String, 12 | enabled: Boolean = true, 13 | category: FeatureCategories = FeatureCategories.HOOK, 14 | ) : ListenedFeature(id, enabled, category) { 15 | 16 | fun hasPlugin(): Boolean { 17 | return hasPlugin(plugin) 18 | } 19 | 20 | override fun isActive(): Boolean { 21 | return super.isActive() && hasPlugin() 22 | } 23 | 24 | override fun isActive(user: BasicUser): Boolean { 25 | return super.isActive(user) && hasPlugin() 26 | } 27 | 28 | } -------------------------------------------------------------------------------- /sayanvanish-proxy/sayanvanish-proxy-bungeecord/src/main/kotlin/org/sayandev/sayanvanish/bungeecord/feature/ListenedFeature.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bungeecord.feature 2 | 3 | import net.md_5.bungee.api.plugin.Listener 4 | import org.sayandev.sayanvanish.api.feature.Feature 5 | import org.sayandev.sayanvanish.api.feature.category.FeatureCategories 6 | import org.sayandev.stickynote.bungeecord.StickyNote.registerListener 7 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 8 | import org.spongepowered.configurate.serialize.TypeSerializerCollection 9 | 10 | @ConfigSerializable 11 | abstract class ListenedFeature( 12 | id: String, 13 | enabled: Boolean = true, 14 | category: FeatureCategories = FeatureCategories.DEFAULT, 15 | additionalSerializers: TypeSerializerCollection = TypeSerializerCollection.defaults() 16 | ) : Feature(id, enabled, category, additionalSerializers), Listener { 17 | 18 | override fun enable() { 19 | if (!condition) return 20 | registerListener(this) 21 | super.enable() 22 | } 23 | 24 | override fun disable(reload: Boolean) { 25 | super.disable(reload) 26 | } 27 | 28 | } -------------------------------------------------------------------------------- /sayanvanish-proxy/sayanvanish-proxy-bungeecord/src/main/kotlin/org/sayandev/sayanvanish/bungeecord/feature/features/FeatureSyncEvents.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bungeecord.feature.features 2 | 3 | import org.sayandev.sayanvanish.api.feature.RegisteredFeature 4 | import org.sayandev.sayanvanish.bungeecord.api.SayanVanishBungeeAPI 5 | import org.sayandev.sayanvanish.bungeecord.event.BungeeUserUnVanishEvent 6 | import org.sayandev.sayanvanish.bungeecord.event.BungeeUserVanishEvent 7 | import org.sayandev.sayanvanish.bungeecord.feature.ListenedFeature 8 | import org.sayandev.stickynote.bungeecord.StickyNote 9 | import org.sayandev.stickynote.bungeecord.plugin 10 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 11 | import java.util.* 12 | import java.util.concurrent.TimeUnit 13 | 14 | @RegisteredFeature 15 | @ConfigSerializable 16 | class FeatureSyncEvents( 17 | val checkPeriodMillis: Long = 50 18 | ) : ListenedFeature("sync_events") { 19 | 20 | @Transient val previousUsers = mutableMapOf() 21 | 22 | override fun enable() { 23 | StickyNote.run({ 24 | for (user in SayanVanishBungeeAPI.getInstance().database.getUsers()) { 25 | if (previousUsers[user.uniqueId] == null) { 26 | previousUsers[user.uniqueId] = user.isVanished 27 | continue 28 | } 29 | 30 | if (previousUsers[user.uniqueId] == false && user.isVanished) { 31 | previousUsers[user.uniqueId] = true 32 | plugin.proxy.pluginManager.callEvent(BungeeUserVanishEvent(user, user.currentOptions)) 33 | } 34 | 35 | if (previousUsers[user.uniqueId] == true && !user.isVanished) { 36 | previousUsers[user.uniqueId] = false 37 | plugin.proxy.pluginManager.callEvent(BungeeUserUnVanishEvent(user, user.currentOptions)) 38 | } 39 | } 40 | }, checkPeriodMillis, checkPeriodMillis, TimeUnit.MILLISECONDS) 41 | super.enable() 42 | } 43 | } -------------------------------------------------------------------------------- /sayanvanish-proxy/sayanvanish-proxy-bungeecord/src/main/kotlin/org/sayandev/sayanvanish/bungeecord/feature/features/prevent/FeaturePreventTabComplete.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bungeecord.feature.features.prevent 2 | 3 | import net.md_5.bungee.api.connection.ProxiedPlayer 4 | import net.md_5.bungee.api.event.TabCompleteEvent 5 | import net.md_5.bungee.event.EventHandler 6 | import net.md_5.bungee.event.EventPriority 7 | import org.sayandev.sayanvanish.api.Permission 8 | import org.sayandev.sayanvanish.api.User 9 | import org.sayandev.sayanvanish.api.feature.Configurable 10 | import org.sayandev.sayanvanish.api.feature.RegisteredFeature 11 | import org.sayandev.sayanvanish.api.feature.category.FeatureCategories 12 | import org.sayandev.sayanvanish.bungeecord.api.SayanVanishBungeeAPI 13 | import org.sayandev.sayanvanish.bungeecord.api.SayanVanishBungeeAPI.Companion.getOrCreateUser 14 | import org.sayandev.sayanvanish.bungeecord.feature.ListenedFeature 15 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 16 | 17 | @RegisteredFeature 18 | @ConfigSerializable 19 | class FeaturePreventTabComplete( 20 | @Configurable val checkVanishLevel: Boolean = false 21 | ): ListenedFeature("prevent_tab_complete", category = FeatureCategories.PREVENTION) { 22 | 23 | @EventHandler(priority = EventPriority.HIGHEST) 24 | fun onTabComplete(event: TabCompleteEvent) { 25 | val player = event.sender as? ProxiedPlayer ?: return 26 | val user = player.getOrCreateUser() 27 | if (!isActive(user)) return 28 | val vanishedUsers = SayanVanishBungeeAPI.getInstance().getVanishedUsers() 29 | if (!user.hasPermission(Permission.VANISH) || !checkVanishLevel) { 30 | event.suggestions 31 | .removeIf { suggestion -> vanishedUsers.map(User::username).contains(suggestion) } 32 | return 33 | } 34 | 35 | event.suggestions.removeIf { suggestion -> 36 | vanishedUsers 37 | .filter { vanishedUser -> vanishedUser.vanishLevel > user.vanishLevel } 38 | .map(User::username).contains(suggestion) 39 | } 40 | } 41 | 42 | } -------------------------------------------------------------------------------- /sayanvanish-proxy/sayanvanish-proxy-bungeecord/src/main/kotlin/org/sayandev/sayanvanish/bungeecord/utils/PlayerUtils.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.bungeecord.utils 2 | 3 | import net.kyori.adventure.text.Component 4 | import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver 5 | import net.md_5.bungee.api.CommandSender 6 | import org.sayandev.sayanvanish.proxy.config.language 7 | import org.sayandev.sayanvanish.proxy.config.settings 8 | import org.sayandev.stickynote.bungeecord.utils.AdventureUtils.component 9 | import org.sayandev.stickynote.bungeecord.utils.AdventureUtils.sendMessage 10 | 11 | object PlayerUtils { 12 | fun CommandSender.sendComponent(content: String, vararg placeholders: TagResolver) { 13 | if (content.isBlank()) return 14 | 15 | val prefix = language.general.prefix.component() 16 | val contentComponent = content.component(*placeholders) 17 | this.sendMessage(if (settings.general.includePrefixInMessages) { 18 | prefix.append(contentComponent) 19 | } else { 20 | contentComponent 21 | }) 22 | } 23 | 24 | fun CommandSender.sendComponent(content: Component) { 25 | if (content == Component.empty()) return 26 | 27 | val prefix = language.general.prefix.component() 28 | this.sendMessage(if (settings.general.includePrefixInMessages) { 29 | prefix.append(content) 30 | } else { 31 | content 32 | }) 33 | } 34 | } -------------------------------------------------------------------------------- /sayanvanish-proxy/sayanvanish-proxy-bungeecord/src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: "${name}" 2 | version: "${version}" 3 | description: "${description}" 4 | main: "org.sayandev.sayanvanish.bungeecord.SayanVanish" 5 | author: "Syrent" 6 | -------------------------------------------------------------------------------- /sayanvanish-proxy/sayanvanish-proxy-velocity/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.sayandev.plugin.StickyNoteModules 2 | 3 | plugins { 4 | id("xyz.jpenilla.run-velocity") version "2.3.0" 5 | } 6 | 7 | stickynote { 8 | modules(StickyNoteModules.VELOCITY) 9 | } 10 | 11 | dependencies { 12 | compileOnly(libs.velocity.api) 13 | compileOnly(libs.velocitab) 14 | compileOnly(libs.enhancedvelocity) 15 | annotationProcessor(libs.velocity.api) 16 | 17 | api(project(":sayanvanish-proxy")) 18 | } 19 | 20 | tasks.withType(xyz.jpenilla.runtask.task.AbstractRun::class) { 21 | javaLauncher = javaToolchains.launcherFor { 22 | vendor = JvmVendorSpec.JETBRAINS 23 | languageVersion = JavaLanguageVersion.of(21) 24 | } 25 | jvmArgs("-XX:+AllowEnhancedClassRedefinition", "-Dnet.kyori.adventure.text.warnWhenLegacyFormattingDetected=false") 26 | } 27 | 28 | 29 | tasks { 30 | runVelocity { 31 | velocityVersion("3.4.0-SNAPSHOT") 32 | 33 | javaLauncher = 34 | rootProject.javaToolchains.launcherFor { 35 | vendor = JvmVendorSpec.JETBRAINS 36 | languageVersion = JavaLanguageVersion.of(21) 37 | } 38 | 39 | downloadPlugins { 40 | // url("https://github.com/NEZNAMY/TAB/releases/download/5.0.3/TAB.v5.0.3.jar") 41 | url("https://download.luckperms.net/1570/velocity/LuckPerms-Velocity-5.4.153.jar") 42 | // url("https://cdn.modrinth.com/data/Q10irTG0/versions/eXh7ktan/Velocitab-1.7.2-67931d8.jar") 43 | // url("https://github.com/Syrent/EnhancedVelocity/releases/download/1.3.3/EnhancedVelocity.v1.3.3.jar") 44 | url("https://cdn.modrinth.com/data/HQyibRsN/versions/FfO1vuOg/MiniPlaceholders-Velocity-2.2.4.jar") 45 | // url("https://cdn.modrinth.com/data/asOrgO06/versions/2t7UMpJh/VelocityCoolList-2.0-SNAPSHOT.jar") 46 | // url("https://github.com/bivashy/MC-Auth-with-Link/releases/download/1.7.12/mcAuth-velocity-1.7.12.jar") 47 | // url("https://github.com/SkinsRestorer/SkinsRestorer/releases/download/15.4.3/SkinsRestorer.jar") 48 | // url("https://hangarcdn.papermc.io/plugins/Andre_601/AdvancedServerList/versions/4.11.1/VELOCITY/AdvancedServerList-Velocity-4.11.1.jar") 49 | // url("https://ci.lucko.me/job/spark/419/artifact/spark-velocity/build/libs/spark-1.10.74-velocity.jar") 50 | hangar("Velocitab", "1.7.5-69cb990") 51 | } 52 | } 53 | } 54 | 55 | modrinth { 56 | loaders.set(listOf( 57 | "velocity", 58 | )) 59 | } -------------------------------------------------------------------------------- /sayanvanish-proxy/sayanvanish-proxy-velocity/src/main/kotlin/org/sayandev/sayanvanish/velocity/SayanVanish.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.velocity 2 | 3 | import com.github.shynixn.mccoroutine.velocity.SuspendingPluginContainer 4 | import com.google.inject.Inject 5 | import com.velocitypowered.api.event.Subscribe 6 | import com.velocitypowered.api.event.proxy.ProxyInitializeEvent 7 | import com.velocitypowered.api.plugin.annotation.DataDirectory 8 | import com.velocitypowered.api.proxy.ProxyServer 9 | import org.sayandev.sayanvanish.api.Platform 10 | import org.sayandev.sayanvanish.api.SayanVanishAPI 11 | import org.sayandev.sayanvanish.api.database.DatabaseMethod 12 | import org.sayandev.sayanvanish.api.database.databaseConfig 13 | import org.sayandev.sayanvanish.api.database.sql.SQLDatabase 14 | import org.sayandev.sayanvanish.proxy.config.language 15 | import org.sayandev.sayanvanish.proxy.config.settings 16 | import org.sayandev.sayanvanish.velocity.api.SayanVanishVelocityAPI 17 | import org.sayandev.sayanvanish.velocity.command.SayanVanishProxyCommandVelocity 18 | import org.sayandev.sayanvanish.velocity.health.HealthCheckMessageSubscriber 19 | import org.sayandev.sayanvanish.velocity.health.ServerInfoPublisher 20 | import org.sayandev.stickynote.loader.velocity.StickyNoteVelocityLoader 21 | import org.sayandev.stickynote.velocity.StickyNote 22 | import org.sayandev.stickynote.velocity.registerListener 23 | import org.slf4j.Logger 24 | import java.io.File 25 | import java.nio.file.Path 26 | import java.util.concurrent.TimeUnit 27 | 28 | lateinit var sayanvanish: SayanVanish 29 | 30 | class SayanVanish @Inject constructor( 31 | val suspendingPluginContainer: SuspendingPluginContainer 32 | ) { 33 | 34 | @Inject 35 | lateinit var server: ProxyServer 36 | 37 | @Inject 38 | lateinit var logger: Logger 39 | 40 | @Inject 41 | @DataDirectory lateinit var dataDirectory: Path 42 | 43 | @Subscribe 44 | fun onProxyInitialize(event: ProxyInitializeEvent) { 45 | StickyNoteVelocityLoader(this, PLUGIN_ID, server, logger, dataDirectory) 46 | suspendingPluginContainer.initialize(this) 47 | sayanvanish = this 48 | Platform.get().rootDirectory = dataDirectory.toFile() 49 | 50 | if (!Platform.setAndRegister(Platform("velocity", java.util.logging.Logger.getLogger("sayanvanish"), dataDirectory.toFile(), ""))) return 51 | 52 | settings 53 | language 54 | 55 | Platform.get().serverId = settings.general.serverId 56 | 57 | SayanVanishVelocityAPI 58 | 59 | HealthCheckMessageSubscriber().register() 60 | ServerInfoPublisher 61 | 62 | SayanVanishProxyCommandVelocity() 63 | 64 | 65 | registerListener(VanishManager) 66 | 67 | if (settings.general.purgeOnlineHistoryOnStartup) { 68 | for (onlineServer in server.allServers) { 69 | SayanVanishVelocityAPI.getInstance().database.purgeBasic(onlineServer.serverInfo.name) 70 | } 71 | SayanVanishVelocityAPI.getInstance().database.purgeBasic(settings.general.serverId) 72 | } 73 | 74 | if (settings.general.purgeUsersOnStartup) { 75 | for (user in SayanVanishVelocityAPI.getInstance().getOnlineUsers()) { 76 | user.isOnline = false 77 | user.save() 78 | } 79 | } 80 | 81 | StickyNote.run({ 82 | if (databaseConfig.method == DatabaseMethod.SQL) { 83 | SayanVanishVelocityAPI.getInstance().database.getBasicUsersAsync { users -> 84 | (SayanVanishVelocityAPI.getInstance().database as SQLDatabase).basicCache = users.associateBy { it.uniqueId }.toMutableMap() 85 | (SayanVanishAPI.getInstance().database as SQLDatabase).basicCache = users.associateBy { it.uniqueId }.toMutableMap() 86 | } 87 | } 88 | }, settings.general.basicCacheUpdatePeriodMillis, TimeUnit.MILLISECONDS, settings.general.basicCacheUpdatePeriodMillis, TimeUnit.MILLISECONDS) 89 | 90 | StickyNote.run({ 91 | SayanVanishVelocityAPI.getInstance().database.getUsersAsync { users -> 92 | SayanVanishVelocityAPI.getInstance().database.cache = users.associateBy { it.uniqueId }.toMutableMap() 93 | SayanVanishAPI.getInstance().database.cache = users.associateBy { it.uniqueId }.toMutableMap() 94 | } 95 | }, settings.general.cacheUpdatePeriodMillis, TimeUnit.MILLISECONDS, settings.general.cacheUpdatePeriodMillis, TimeUnit.MILLISECONDS) 96 | } 97 | 98 | fun pluginFile(): File? { 99 | return dataDirectory.parent.toFile().listFiles().filter { it.isFile }.find { it.name.lowercase().contains("sayanvanish") && it.extension == "jar" } 100 | } 101 | 102 | companion object { 103 | const val PLUGIN_ID = "sayanvanish" 104 | } 105 | 106 | } -------------------------------------------------------------------------------- /sayanvanish-proxy/sayanvanish-proxy-velocity/src/main/kotlin/org/sayandev/sayanvanish/velocity/VanishManager.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.velocity 2 | 3 | import com.velocitypowered.api.event.Subscribe 4 | import com.velocitypowered.api.event.connection.DisconnectEvent 5 | import com.velocitypowered.api.event.player.ServerPostConnectEvent 6 | import org.sayandev.sayanvanish.api.BasicUser 7 | import org.sayandev.sayanvanish.api.Platform 8 | import org.sayandev.sayanvanish.velocity.api.SayanVanishVelocityAPI 9 | import org.sayandev.sayanvanish.velocity.api.SayanVanishVelocityAPI.Companion.getOrCreateUser 10 | import org.sayandev.sayanvanish.velocity.event.VelocityUserUnVanishEvent 11 | import org.sayandev.sayanvanish.velocity.event.VelocityUserVanishEvent 12 | import org.sayandev.stickynote.velocity.server 13 | import kotlin.jvm.optionals.getOrNull 14 | 15 | object VanishManager { 16 | 17 | @Subscribe 18 | private fun onPostLogin(event: ServerPostConnectEvent) { 19 | val player = event.player ?: return 20 | SayanVanishVelocityAPI.getInstance().database.addBasicUser(BasicUser.create(player.uniqueId, player.username, player.currentServer.getOrNull()?.serverInfo?.name ?: Platform.get().id)) 21 | val user = player.getOrCreateUser() 22 | if (user.isVanished) { 23 | server.eventManager.fireAndForget(VelocityUserVanishEvent(user, user.currentOptions)) 24 | } else { 25 | server.eventManager.fireAndForget(VelocityUserUnVanishEvent(user, user.currentOptions)) 26 | } 27 | } 28 | 29 | @Subscribe 30 | private fun onDisconnect(event: DisconnectEvent) { 31 | val player = event.player ?: return 32 | SayanVanishVelocityAPI.getInstance().database.removeBasicUser(player.uniqueId) 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /sayanvanish-proxy/sayanvanish-proxy-velocity/src/main/kotlin/org/sayandev/sayanvanish/velocity/api/SayanVanishVelocityAPI.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.velocity.api 2 | 3 | import com.velocitypowered.api.proxy.Player 4 | import org.sayandev.sayanvanish.api.SayanVanishAPI 5 | import org.sayandev.sayanvanish.api.database.databaseConfig 6 | import java.util.UUID 7 | 8 | val database = SayanVanishVelocityAPI.getInstance().database 9 | 10 | class SayanVanishVelocityAPI() : SayanVanishAPI(VelocityUser::class.java) { 11 | companion object { 12 | private val defaultInstance = SayanVanishVelocityAPI() 13 | 14 | @JvmStatic 15 | fun getInstance(): SayanVanishAPI { 16 | return defaultInstance 17 | } 18 | 19 | @JvmStatic 20 | public fun UUID.user(): VelocityUser? { 21 | return getInstance().getUser(this) 22 | } 23 | 24 | @JvmStatic 25 | public fun Player.user(): VelocityUser? { 26 | return getInstance().getUser(this.uniqueId) 27 | } 28 | 29 | @JvmStatic 30 | fun Player.getOrCreateUser(): VelocityUser { 31 | return getInstance().getUser(this.uniqueId) ?: VelocityUser(this.uniqueId, this.username ?: "N/A") 32 | } 33 | 34 | @JvmStatic 35 | fun Player.getOrAddUser(): VelocityUser { 36 | return getInstance().getUser(this.uniqueId) ?: let { 37 | val newUser = VelocityUser(this.uniqueId, this.username ?: "N/A") 38 | getInstance().database.addUser(newUser) 39 | newUser 40 | } 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /sayanvanish-proxy/sayanvanish-proxy-velocity/src/main/kotlin/org/sayandev/sayanvanish/velocity/api/VelocityUser.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.velocity.api 2 | 3 | import com.velocitypowered.api.proxy.Player 4 | import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver 5 | import org.sayandev.sayanvanish.api.Permission 6 | import org.sayandev.sayanvanish.api.SayanVanishAPI 7 | import org.sayandev.sayanvanish.api.User 8 | import org.sayandev.sayanvanish.api.VanishOptions 9 | import org.sayandev.sayanvanish.api.feature.Features 10 | import org.sayandev.sayanvanish.proxy.config.settings 11 | import org.sayandev.sayanvanish.velocity.event.VelocityUserUnVanishEvent 12 | import org.sayandev.sayanvanish.velocity.event.VelocityUserVanishEvent 13 | import org.sayandev.sayanvanish.velocity.feature.features.hook.FeatureLuckPermsHook 14 | import org.sayandev.sayanvanish.velocity.utils.PlayerUtils.sendComponent 15 | import org.sayandev.stickynote.velocity.StickyNote 16 | import org.sayandev.stickynote.velocity.server 17 | import org.sayandev.stickynote.velocity.utils.AdventureUtils.component 18 | import java.util.* 19 | import kotlin.jvm.optionals.getOrNull 20 | 21 | 22 | open class VelocityUser( 23 | override val uniqueId: UUID, 24 | override var username: String 25 | ) : User { 26 | 27 | override var serverId: String 28 | get() = StickyNote.getPlayer(uniqueId)?.currentServer?.getOrNull()?.serverInfo?.name ?: settings.general.serverId 29 | set(_) {} 30 | override var currentOptions = VanishOptions.defaultOptions() 31 | override var isVanished = false 32 | override var isOnline: Boolean = SayanVanishAPI.getInstance().database.hasBasicUser(uniqueId, true) 33 | override var vanishLevel: Int = 0 34 | get() = player()?.let { player -> 35 | val luckPermsHook = Features.getFeature() 36 | if (luckPermsHook.isActive()) { 37 | luckPermsHook.getPermissions(uniqueId) 38 | .filter { it.startsWith("sayanvanish.level.") } 39 | .maxOfOrNull { it.substringAfter("sayanvanish.level.").toIntOrNull() ?: field } 40 | ?: if (hasPermission(Permission.VANISH)) 1 else { 41 | if (isVanished) 1 else field 42 | } 43 | } else { 44 | field 45 | } 46 | } ?: field 47 | 48 | fun stateText(isVanished: Boolean = this.isVanished) = if (isVanished) "ON" else "OFF" 49 | 50 | fun player(): Player? = StickyNote.getPlayer(uniqueId) 51 | 52 | override fun vanish(options: VanishOptions) { 53 | server.eventManager.fire(VelocityUserVanishEvent(this, options)).whenComplete { event, error -> 54 | error?.printStackTrace() 55 | 56 | val options = event.options 57 | currentOptions = options 58 | 59 | database.addToQueue(uniqueId, true) 60 | super.vanish(options) 61 | } 62 | } 63 | 64 | override fun unVanish(options: VanishOptions) { 65 | server.eventManager.fire(VelocityUserUnVanishEvent(this, options)).whenComplete { event, error -> 66 | error?.printStackTrace() 67 | 68 | val options = event.options 69 | currentOptions = options 70 | 71 | database.addToQueue(uniqueId, false) 72 | super.unVanish(options) 73 | } 74 | } 75 | 76 | override fun hasPermission(permission: String): Boolean { 77 | return player()?.hasPermission(permission) == true 78 | } 79 | 80 | override fun sendComponent(content: String, vararg placeholder: TagResolver) { 81 | player()?.sendComponent(content, *placeholder) 82 | } 83 | 84 | override fun sendActionbar(content: String, vararg placeholder: TagResolver) { 85 | player()?.sendActionBar(content.component(*placeholder)) 86 | } 87 | 88 | companion object { 89 | @JvmStatic 90 | fun fromUser(user: User): VelocityUser { 91 | return VelocityUser(user.uniqueId, user.username).apply { 92 | this.isOnline = user.isOnline 93 | this.isVanished = user.isVanished 94 | this.vanishLevel = user.vanishLevel 95 | } 96 | } 97 | } 98 | 99 | } -------------------------------------------------------------------------------- /sayanvanish-proxy/sayanvanish-proxy-velocity/src/main/kotlin/org/sayandev/sayanvanish/velocity/event/VelocityUserUnVanishEvent.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.velocity.event 2 | 3 | import org.sayandev.sayanvanish.api.VanishOptions 4 | import org.sayandev.sayanvanish.velocity.api.VelocityUser 5 | 6 | class VelocityUserUnVanishEvent(val user: VelocityUser, val options: VanishOptions) -------------------------------------------------------------------------------- /sayanvanish-proxy/sayanvanish-proxy-velocity/src/main/kotlin/org/sayandev/sayanvanish/velocity/event/VelocityUserVanishEvent.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.velocity.event 2 | 3 | import org.sayandev.sayanvanish.api.VanishOptions 4 | import org.sayandev.sayanvanish.velocity.api.VelocityUser 5 | 6 | class VelocityUserVanishEvent(val user: VelocityUser, val options: VanishOptions) -------------------------------------------------------------------------------- /sayanvanish-proxy/sayanvanish-proxy-velocity/src/main/kotlin/org/sayandev/sayanvanish/velocity/feature/HookFeature.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.velocity.feature 2 | 3 | import org.sayandev.sayanvanish.api.BasicUser 4 | import org.sayandev.sayanvanish.api.feature.category.FeatureCategories 5 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 6 | import org.sayandev.stickynote.velocity.hasPlugin 7 | 8 | @ConfigSerializable 9 | abstract class HookFeature( 10 | id: String, 11 | @Transient val plugin: String, 12 | enabled: Boolean = true, 13 | category: FeatureCategories = FeatureCategories.HOOK, 14 | ) : ListenedFeature(id, enabled, category) { 15 | 16 | fun hasPlugin(): Boolean { 17 | return hasPlugin(plugin) 18 | } 19 | 20 | override fun isActive(): Boolean { 21 | return super.isActive() && hasPlugin() 22 | } 23 | 24 | override fun isActive(user: BasicUser): Boolean { 25 | return super.isActive(user) && hasPlugin() 26 | } 27 | 28 | } -------------------------------------------------------------------------------- /sayanvanish-proxy/sayanvanish-proxy-velocity/src/main/kotlin/org/sayandev/sayanvanish/velocity/feature/ListenedFeature.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.velocity.feature 2 | 3 | import org.sayandev.sayanvanish.api.feature.Feature 4 | import org.sayandev.sayanvanish.api.feature.category.FeatureCategories 5 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 6 | import org.sayandev.stickynote.velocity.registerListener 7 | import org.spongepowered.configurate.serialize.TypeSerializerCollection 8 | 9 | @ConfigSerializable 10 | abstract class ListenedFeature( 11 | id: String, 12 | enabled: Boolean = true, 13 | category: FeatureCategories = FeatureCategories.DEFAULT, 14 | additionalSerializers: TypeSerializerCollection = TypeSerializerCollection.defaults() 15 | ) : Feature(id, enabled, category, additionalSerializers) { 16 | 17 | override fun enable() { 18 | if (!condition) return 19 | registerListener(this) 20 | super.enable() 21 | } 22 | 23 | override fun disable(reload: Boolean) { 24 | super.disable(reload) 25 | } 26 | 27 | } -------------------------------------------------------------------------------- /sayanvanish-proxy/sayanvanish-proxy-velocity/src/main/kotlin/org/sayandev/sayanvanish/velocity/feature/features/FeatureSyncEvents.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.velocity.feature.features 2 | 3 | import org.sayandev.sayanvanish.api.feature.RegisteredFeature 4 | import org.sayandev.sayanvanish.velocity.api.SayanVanishVelocityAPI 5 | import org.sayandev.sayanvanish.velocity.event.VelocityUserUnVanishEvent 6 | import org.sayandev.sayanvanish.velocity.event.VelocityUserVanishEvent 7 | import org.sayandev.sayanvanish.velocity.feature.ListenedFeature 8 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 9 | import org.sayandev.stickynote.velocity.StickyNote 10 | import org.sayandev.stickynote.velocity.server 11 | import org.spongepowered.configurate.objectmapping.meta.Comment 12 | import java.util.UUID 13 | import java.util.concurrent.TimeUnit 14 | 15 | @RegisteredFeature 16 | @ConfigSerializable 17 | class FeatureSyncEvents( 18 | @Comment("The period of time to check for vanished players. low values may cause performance issues.") 19 | val checkPeriodMillis: Long = 50 20 | ) : ListenedFeature("sync_events") { 21 | 22 | @Transient val previousUsers = mutableMapOf() 23 | 24 | override fun enable() { 25 | StickyNote.run({ 26 | for (user in SayanVanishVelocityAPI.getInstance().database.getUsers()) { 27 | if (previousUsers[user.uniqueId] == null) { 28 | previousUsers[user.uniqueId] = user.isVanished 29 | continue 30 | } 31 | 32 | if (previousUsers[user.uniqueId] == false && user.isVanished) { 33 | previousUsers[user.uniqueId] = true 34 | server.eventManager.fireAndForget(VelocityUserVanishEvent(user, user.currentOptions)) 35 | } 36 | 37 | if (previousUsers[user.uniqueId] == true && !user.isVanished) { 38 | previousUsers[user.uniqueId] = false 39 | server.eventManager.fireAndForget(VelocityUserUnVanishEvent(user, user.currentOptions)) 40 | } 41 | } 42 | }, checkPeriodMillis, TimeUnit.MILLISECONDS, checkPeriodMillis, TimeUnit.MILLISECONDS) 43 | super.enable() 44 | } 45 | } -------------------------------------------------------------------------------- /sayanvanish-proxy/sayanvanish-proxy-velocity/src/main/kotlin/org/sayandev/sayanvanish/velocity/feature/features/FeatureUpdatePing.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.velocity.feature.features 2 | 3 | import com.velocitypowered.api.event.Subscribe 4 | import com.velocitypowered.api.event.proxy.ProxyPingEvent 5 | import com.velocitypowered.api.proxy.server.ServerPing 6 | import org.sayandev.sayanvanish.api.feature.RegisteredFeature 7 | import org.sayandev.sayanvanish.velocity.api.SayanVanishVelocityAPI 8 | import org.sayandev.sayanvanish.velocity.feature.ListenedFeature 9 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 10 | import kotlin.jvm.optionals.getOrNull 11 | 12 | @RegisteredFeature 13 | @ConfigSerializable 14 | class FeatureUpdatePing : ListenedFeature("update_ping") { 15 | 16 | @Subscribe 17 | fun onProxyPing(event: ProxyPingEvent) { 18 | if (!isActive()) return 19 | val pingPlayers = event.ping.players.getOrNull() ?: return 20 | val vanishedOnlineUsers = SayanVanishVelocityAPI.getInstance().database.getUsers().filter { user -> user.isVanished && user.isOnline } 21 | val nonVanishedPlayersCount = SayanVanishVelocityAPI.getInstance().database.getBasicUsers(true).filter { !vanishedOnlineUsers.map { vanishUser -> vanishUser.username }.contains(it.username) }.size 22 | val nonVanishedPlayersSample = pingPlayers.sample.filter { !vanishedOnlineUsers.map { it.username }.contains(it.name) } 23 | event.ping = event.ping 24 | .asBuilder() 25 | .onlinePlayers(nonVanishedPlayersCount) 26 | .samplePlayers(*nonVanishedPlayersSample.map { ServerPing.SamplePlayer(it.name, it.id) }.toTypedArray()) 27 | .build() 28 | } 29 | } -------------------------------------------------------------------------------- /sayanvanish-proxy/sayanvanish-proxy-velocity/src/main/kotlin/org/sayandev/sayanvanish/velocity/feature/features/hook/FeatureHookAdvancedServerList.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.velocity.feature.features.hook 2 | 3 | import ch.andre601.advancedserverlist.api.AdvancedServerListAPI 4 | import ch.andre601.advancedserverlist.api.PlaceholderProvider 5 | import ch.andre601.advancedserverlist.api.exceptions.InvalidPlaceholderProviderException 6 | import ch.andre601.advancedserverlist.api.objects.GenericPlayer 7 | import ch.andre601.advancedserverlist.api.objects.GenericServer 8 | import org.sayandev.sayanvanish.api.SayanVanishAPI 9 | import org.sayandev.sayanvanish.api.feature.RegisteredFeature 10 | import org.sayandev.sayanvanish.proxy.config.language 11 | import org.sayandev.sayanvanish.velocity.api.SayanVanishVelocityAPI 12 | import org.sayandev.sayanvanish.velocity.api.SayanVanishVelocityAPI.Companion.user 13 | import org.sayandev.sayanvanish.velocity.feature.HookFeature 14 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 15 | import org.sayandev.stickynote.velocity.registerListener 16 | 17 | @RegisteredFeature 18 | @ConfigSerializable 19 | class FeatureHookAdvancedServerList : HookFeature("hook_advanced_server_list", "advancedserverlist") { 20 | override fun enable() { 21 | if (hasPlugin()) { 22 | AdvancedServerListImpl() 23 | } 24 | super.enable() 25 | } 26 | } 27 | 28 | private class AdvancedServerListImpl : PlaceholderProvider("sayanvanish") { 29 | init { 30 | try { 31 | AdvancedServerListAPI.get() 32 | .addPlaceholderProvider(this) 33 | } catch (_: InvalidPlaceholderProviderException) { } 34 | } 35 | 36 | override fun parsePlaceholder( 37 | placeholder: String, 38 | player: GenericPlayer?, 39 | server: GenericServer? 40 | ): String? { 41 | if (placeholder.equals("vanished", true)) { 42 | if (player == null) return "false" 43 | return if (SayanVanishVelocityAPI.getInstance().getVanishedUsers().map { it.username }.contains(player.name)) "true" else "false" 44 | } 45 | 46 | if (placeholder.equals("level", true)) { 47 | if (player == null) return "0" 48 | return player.uuid.user()?.vanishLevel?.toString() ?: "0" 49 | } 50 | 51 | if (placeholder.equals("count", true)) { 52 | return SayanVanishVelocityAPI.getInstance().database.getUsers().filter { user -> user.isOnline && user.isVanished }.size.toString() 53 | } 54 | 55 | if (placeholder.equals("vanish_prefix", true)) { 56 | return if (player?.uuid?.user()?.isVanished == true) language.vanish.placeholderPrefix else "" 57 | } 58 | 59 | if (placeholder.equals("vanish_suffix", true)) { 60 | return if (player?.uuid?.user()?.isVanished == true) language.vanish.placeholderSuffix else "" 61 | } 62 | 63 | if (placeholder.startsWith("online_")) { 64 | val type = placeholder.substring(7) 65 | val vanishedOnlineUsers = SayanVanishVelocityAPI.getInstance().database.getUsers().filter { user -> user.isVanished && user.isOnline } 66 | 67 | return if (type.equals("total", true)) { 68 | SayanVanishAPI.getInstance().database.getBasicUsers(false).filter { !vanishedOnlineUsers.map { vanishUser -> vanishUser.username }.contains(it.username) }.size.toString() 69 | } else { 70 | SayanVanishAPI.getInstance().database.getBasicUsers(false).filter { it.serverId == type && !vanishedOnlineUsers.map { vanishUser -> vanishUser.username }.contains(it.username) }.size.toString() 71 | } 72 | } 73 | 74 | return null 75 | } 76 | 77 | init { 78 | registerListener(this) 79 | } 80 | } -------------------------------------------------------------------------------- /sayanvanish-proxy/sayanvanish-proxy-velocity/src/main/kotlin/org/sayandev/sayanvanish/velocity/feature/features/hook/FeatureHookEnhancedVelocity.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.velocity.feature.features.hook 2 | 3 | import ir.syrent.enhancedvelocity.api.VanishHook 4 | import org.sayandev.sayanvanish.api.feature.RegisteredFeature 5 | import org.sayandev.sayanvanish.velocity.feature.HookFeature 6 | import org.sayandev.sayanvanish.velocity.api.SayanVanishVelocityAPI 7 | import org.sayandev.sayanvanish.velocity.api.SayanVanishVelocityAPI.Companion.getOrAddUser 8 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 9 | import org.sayandev.stickynote.velocity.StickyNote 10 | import java.util.UUID 11 | 12 | @RegisteredFeature 13 | @ConfigSerializable 14 | class FeatureHookEnhancedVelocity : HookFeature("hook_enhancedvelocity", "enhancedvelocity") { 15 | 16 | override fun enable() { 17 | if (hasPlugin()) { 18 | EnhancedVelocityImpl().register() 19 | } 20 | super.enable() 21 | } 22 | } 23 | 24 | private class EnhancedVelocityImpl : VanishHook { 25 | override fun setIsVanished(uniqueId: UUID): Boolean { 26 | return SayanVanishVelocityAPI.getInstance().isVanished(uniqueId) 27 | } 28 | 29 | override fun setVanished(uniqueId: UUID) { 30 | StickyNote.getPlayer(uniqueId)?.getOrAddUser()?.vanish() 31 | } 32 | 33 | override fun setUnVanished(uniqueId: UUID) { 34 | StickyNote.getPlayer(uniqueId)?.getOrAddUser()?.unVanish() 35 | } 36 | } -------------------------------------------------------------------------------- /sayanvanish-proxy/sayanvanish-proxy-velocity/src/main/kotlin/org/sayandev/sayanvanish/velocity/feature/features/hook/FeatureHookTAB.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.velocity.feature.features.hook 2 | 3 | import me.neznamy.tab.api.TabPlayer 4 | import me.neznamy.tab.api.integration.VanishIntegration 5 | import org.sayandev.sayanvanish.api.feature.RegisteredFeature 6 | import org.sayandev.sayanvanish.velocity.api.SayanVanishVelocityAPI 7 | import org.sayandev.sayanvanish.velocity.api.VelocityUser 8 | import org.sayandev.sayanvanish.velocity.feature.HookFeature 9 | import org.sayandev.stickynote.velocity.plugin 10 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 11 | import org.spongepowered.configurate.objectmapping.meta.Comment 12 | 13 | 14 | @RegisteredFeature 15 | @ConfigSerializable 16 | class FeatureHookTAB( 17 | @Comment("Whether to use cache data for vanish status. This will improve performance but may cause a small delay in tablist removal after join.") 18 | val useCacheData: Boolean = false 19 | ): HookFeature("hook_tab", "TAB") { 20 | override fun enable() { 21 | if (hasPlugin()) { 22 | VanishIntegrationTAB(this).register() 23 | } 24 | super.enable() 25 | } 26 | 27 | override fun disable(reload: Boolean) { 28 | if (hasPlugin()) { 29 | VanishIntegrationTAB(this).unregister() 30 | } 31 | super.disable(reload) 32 | } 33 | } 34 | 35 | private class VanishIntegrationTAB(val feature: FeatureHookTAB): VanishIntegration(plugin.container.description.name.get()) { 36 | override fun isVanished(player: TabPlayer): Boolean { 37 | return SayanVanishVelocityAPI.getInstance().isVanished(player.uniqueId, feature.useCacheData) 38 | } 39 | 40 | override fun canSee(viewer: TabPlayer, target: TabPlayer): Boolean { 41 | if (viewer.uniqueId == target.uniqueId) return true 42 | val viewerUser = SayanVanishVelocityAPI.getInstance().getUser(viewer.uniqueId, feature.useCacheData) ?: VelocityUser(viewer.uniqueId, viewer.name) 43 | val targetUser = SayanVanishVelocityAPI.getInstance().getUser(target.uniqueId, feature.useCacheData) ?: return true 44 | return SayanVanishVelocityAPI.getInstance().canSee(viewerUser, targetUser) 45 | } 46 | } -------------------------------------------------------------------------------- /sayanvanish-proxy/sayanvanish-proxy-velocity/src/main/kotlin/org/sayandev/sayanvanish/velocity/feature/features/hook/FeatureLuckPermsHook.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.velocity.feature.features.hook 2 | 3 | import com.velocitypowered.api.proxy.Player 4 | import net.luckperms.api.LuckPermsProvider 5 | import net.luckperms.api.context.ContextCalculator 6 | import net.luckperms.api.context.ContextConsumer 7 | import net.luckperms.api.context.ContextSet 8 | import net.luckperms.api.context.ImmutableContextSet 9 | import net.luckperms.api.node.NodeEqualityPredicate 10 | import net.luckperms.api.node.types.PermissionNode 11 | import net.luckperms.api.query.QueryOptions 12 | import org.sayandev.sayanvanish.api.feature.Configurable 13 | import org.sayandev.sayanvanish.api.feature.RegisteredFeature 14 | import org.sayandev.sayanvanish.velocity.api.SayanVanishVelocityAPI.Companion.getOrCreateUser 15 | import org.sayandev.sayanvanish.velocity.feature.HookFeature 16 | import org.sayandev.stickynote.velocity.StickyNote 17 | import org.sayandev.stickynote.velocity.warn 18 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 19 | import org.spongepowered.configurate.objectmapping.meta.Comment 20 | import java.util.* 21 | 22 | @RegisteredFeature 23 | @ConfigSerializable 24 | class FeatureLuckPermsHook( 25 | @Comment("Register custom context for vanished players, this will allow you to check if a player is vanished using LuckPerms.") 26 | @Configurable val registerCustomContext: Boolean = true, 27 | @Comment("Check permission using LuckPerms, if false, will fallback to velocity permission check.") 28 | @Configurable val checkPermissionViaLuckPerms: Boolean = true, 29 | ): HookFeature("hook_luckperms", "luckperms") { 30 | 31 | override fun enable() { 32 | if (hasPlugin()) { 33 | if (registerCustomContext) { 34 | LuckPermsProvider.get().contextManager.registerCalculator(VanishedContext()) 35 | } 36 | } 37 | super.enable() 38 | } 39 | 40 | fun hasPermission(uniqueId: UUID, permission: String): Boolean { 41 | // Can't check permissions on a per-player basis to prevent stackoverflow 42 | if (!isActive()) { 43 | warn("tried to check permission using LuckPerms, but the `${this.id}` feature is not active, fallback to bukkit permission check.") 44 | return StickyNote.getPlayer(uniqueId)?.hasPermission(permission) == true 45 | } 46 | val user = LuckPermsProvider.get().userManager.getUser(uniqueId) ?: return false 47 | val permissionNode = PermissionNode.builder(permission).value(true).build() 48 | val userPermission = user 49 | .data() 50 | .contains(permissionNode, NodeEqualityPredicate.IGNORE_EXPIRY_TIME) 51 | .asBoolean() 52 | if (!userPermission) { 53 | return user.getInheritedGroups(QueryOptions.nonContextual()).any { it.data().contains(permissionNode, 54 | NodeEqualityPredicate.IGNORE_EXPIRY_TIME 55 | ).asBoolean() } 56 | } 57 | return userPermission 58 | } 59 | 60 | fun getPermissions(uniqueId: UUID): List { 61 | val user = LuckPermsProvider.get().userManager.getUser(uniqueId) ?: return emptyList() 62 | return user 63 | .data() 64 | .toCollection() 65 | .filterIsInstance() 66 | .toMutableList() 67 | .plus(user.getInheritedGroups(QueryOptions.nonContextual()).flatMap { it.data().toCollection().filterIsInstance() }) 68 | .filter { !it.hasExpired() && it.value } 69 | .map { it.permission } 70 | } 71 | } 72 | 73 | private class VanishedContext: ContextCalculator { 74 | 75 | override fun calculate(target: Player, contextConsumer: ContextConsumer) { 76 | contextConsumer.accept("vanished", target.getOrCreateUser().isVanished.toString()) 77 | } 78 | 79 | override fun estimatePotentialContexts(): ContextSet { 80 | val builder = ImmutableContextSet.builder() 81 | builder.add("vanished", "true") 82 | builder.add("vanished", "false") 83 | return builder.build() 84 | } 85 | 86 | } -------------------------------------------------------------------------------- /sayanvanish-proxy/sayanvanish-proxy-velocity/src/main/kotlin/org/sayandev/sayanvanish/velocity/feature/features/prevent/FeaturePreventTabComplete.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.velocity.feature.features.prevent 2 | 3 | import com.velocitypowered.api.event.PostOrder 4 | import com.velocitypowered.api.event.Subscribe 5 | import com.velocitypowered.api.event.player.TabCompleteEvent 6 | import org.sayandev.sayanvanish.api.Permission 7 | import org.sayandev.sayanvanish.api.User 8 | import org.sayandev.sayanvanish.api.feature.Configurable 9 | import org.sayandev.sayanvanish.api.feature.RegisteredFeature 10 | import org.sayandev.sayanvanish.api.feature.category.FeatureCategories 11 | import org.sayandev.sayanvanish.velocity.api.SayanVanishVelocityAPI 12 | import org.sayandev.sayanvanish.velocity.api.SayanVanishVelocityAPI.Companion.getOrCreateUser 13 | import org.sayandev.sayanvanish.velocity.feature.ListenedFeature 14 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 15 | import org.spongepowered.configurate.objectmapping.meta.Comment 16 | 17 | @RegisteredFeature 18 | @ConfigSerializable 19 | class FeaturePreventTabComplete( 20 | @Comment("Whether to keep vanished player in tab completion if the player that is getting the suggestion has a higher level of vanish.") 21 | @Configurable val checkVanishLevel: Boolean = false 22 | ): ListenedFeature("prevent_tab_complete", category = FeatureCategories.PREVENTION) { 23 | 24 | @Subscribe(order = PostOrder.LAST) 25 | fun onTabComplete(event: TabCompleteEvent) { 26 | val player = event.player ?: return 27 | val user = player.getOrCreateUser() 28 | if (!isActive(user)) return 29 | val vanishedUsers = SayanVanishVelocityAPI.getInstance().getVanishedUsers() 30 | if (!user.hasPermission(Permission.VANISH) || !checkVanishLevel) { 31 | event.suggestions 32 | .removeIf { suggestion -> vanishedUsers.map(User::username).contains(suggestion) } 33 | return 34 | } 35 | 36 | event.suggestions.removeIf { suggestion -> 37 | vanishedUsers 38 | .filter { vanishedUser -> vanishedUser.vanishLevel > user.vanishLevel } 39 | .map(User::username).contains(suggestion) 40 | } 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /sayanvanish-proxy/sayanvanish-proxy-velocity/src/main/kotlin/org/sayandev/sayanvanish/velocity/health/HealthCheckMessageSubscriber.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.velocity.health 2 | 3 | import kotlinx.coroutines.CompletableDeferred 4 | import org.sayandev.sayanvanish.api.database.DatabaseMethod 5 | import org.sayandev.sayanvanish.api.database.databaseConfig 6 | import org.sayandev.sayanvanish.api.database.sql.SQLConfig 7 | import org.sayandev.sayanvanish.api.health.HealthCheckData 8 | import org.sayandev.stickynote.core.messaging.publisher.PayloadWrapper 9 | import org.sayandev.stickynote.core.utils.CoroutineUtils.awaitWithTimeout 10 | import org.sayandev.stickynote.velocity.messaging.ProxySubscriber 11 | import org.sayandev.stickynote.velocity.plugin 12 | 13 | class HealthCheckMessageSubscriber: ProxySubscriber( 14 | plugin.container.description.name.get().lowercase(), 15 | "hc", 16 | Unit::class.java 17 | ) { 18 | 19 | override suspend fun onSubscribe(payload: Unit): CompletableDeferred { 20 | val proxyInfo = HealthCheckData.ProxyInfo( 21 | databaseConfig.method, 22 | databaseConfig.sql.method 23 | ) 24 | 25 | val servers = HealthCheckData.Servers(mutableListOf()) 26 | 27 | for (server in plugin.server.allServers) { 28 | servers.servers.add((ServerInfoPublisher.publish(server, PayloadWrapper(Unit, PayloadWrapper.State.FORWARD)).awaitWithTimeout(1000) ?: let { 29 | HealthCheckData.ServerInfo( 30 | null, 31 | server.serverInfo.name, 32 | false, 33 | DatabaseMethod.SQL, 34 | SQLConfig.SQLMethod.SQLITE, 35 | System.currentTimeMillis() 36 | ) 37 | }).apply { this.name = server.serverInfo.name }) 38 | } 39 | 40 | return CompletableDeferred(HealthCheckData(proxyInfo, servers)) 41 | } 42 | } -------------------------------------------------------------------------------- /sayanvanish-proxy/sayanvanish-proxy-velocity/src/main/kotlin/org/sayandev/sayanvanish/velocity/health/ServerInfoPublisher.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.velocity.health 2 | 3 | import org.sayandev.sayanvanish.api.health.HealthCheckData 4 | import org.sayandev.stickynote.velocity.messaging.PluginMessagePublisher 5 | import org.sayandev.stickynote.velocity.plugin 6 | 7 | object ServerInfoPublisher: PluginMessagePublisher( 8 | plugin.container.description.name.get().lowercase(), 9 | "info", 10 | HealthCheckData.ServerInfo::class.java 11 | ) { 12 | init { 13 | register(this) 14 | } 15 | 16 | override fun handle(payload: Unit): HealthCheckData.ServerInfo? { 17 | return null 18 | } 19 | } -------------------------------------------------------------------------------- /sayanvanish-proxy/sayanvanish-proxy-velocity/src/main/kotlin/org/sayandev/sayanvanish/velocity/utils/PlayerUtils.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.velocity.utils 2 | 3 | import com.velocitypowered.api.command.CommandSource 4 | import net.kyori.adventure.text.Component 5 | import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver 6 | import org.sayandev.sayanvanish.proxy.config.LanguageConfig 7 | import org.sayandev.sayanvanish.proxy.config.language 8 | import org.sayandev.sayanvanish.proxy.config.settings 9 | import org.sayandev.stickynote.velocity.utils.AdventureUtils 10 | import org.sayandev.stickynote.velocity.utils.AdventureUtils.component 11 | 12 | object PlayerUtils { 13 | fun CommandSource.sendComponent(content: String, vararg placeholders: TagResolver) { 14 | if (content.isBlank()) return 15 | 16 | val prefix = language.general.prefix.component() 17 | val contentComponent = content.component(*placeholders) 18 | this.sendMessage(if (settings.general.includePrefixInMessages) { 19 | prefix.append(contentComponent) 20 | } else { 21 | contentComponent 22 | }) 23 | } 24 | 25 | fun CommandSource.sendComponent(content: Component) { 26 | if (content == Component.empty()) return 27 | 28 | val prefix = language.general.prefix.component() 29 | this.sendMessage(if (settings.general.includePrefixInMessages) { 30 | prefix.append(content) 31 | } else { 32 | content 33 | }) 34 | } 35 | 36 | fun CommandSource.sendRawComponent(content: String, vararg placeholders: TagResolver) { 37 | this.sendMessage(content.component(*placeholders)) 38 | } 39 | } -------------------------------------------------------------------------------- /sayanvanish-proxy/sayanvanish-proxy-velocity/src/main/resources/velocity-plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "${slug}", 3 | "name": "${name}", 4 | "version": "${version}", 5 | "description": "${description}", 6 | "url": "sayandev.org", 7 | "authors": [ 8 | "Syrent" 9 | ], 10 | "dependencies": [ 11 | { 12 | "id": "enhancedvelocity", 13 | "optional": true 14 | }, 15 | { 16 | "id": "miniplaceholders", 17 | "optional": true 18 | }, 19 | { 20 | "id": "velocitab", 21 | "optional": true 22 | }, 23 | { 24 | "id": "tab", 25 | "optional": true 26 | } 27 | ], 28 | "main": "org.sayandev.sayanvanish.velocity.SayanVanish" 29 | } -------------------------------------------------------------------------------- /sayanvanish-proxy/src/main/kotlin/org/sayandev/sayanvanish/proxy/command/SayanVanishProxyCommand.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.proxy.command 2 | 3 | import org.incendo.cloud.CommandManager 4 | import org.incendo.cloud.kotlin.MutableCommandBuilder 5 | import org.sayandev.sayanvanish.proxy.config.settings 6 | import org.sayandev.stickynote.core.command.Command 7 | import org.sayandev.stickynote.core.command.interfaces.SenderExtension 8 | 9 | abstract class SayanVanishProxyCommand, C: CommandManager>( 10 | manager: C, 11 | ) : Command("sayanvanishproxy", manager, settings.command.name, *settings.command.aliases.toTypedArray()) -------------------------------------------------------------------------------- /sayanvanish-proxy/src/main/kotlin/org/sayandev/sayanvanish/proxy/config/LanguageConfig.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.proxy.config 2 | 3 | import org.sayandev.sayanvanish.api.Platform 4 | import org.sayandev.stickynote.core.configuration.Config 5 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 6 | import java.io.File 7 | 8 | public var language: LanguageConfig = LanguageConfig.fromConfig() ?: LanguageConfig.defaultConfig() 9 | 10 | @ConfigSerializable 11 | class LanguageConfig( 12 | val general: General = General(), 13 | val vanish: Vanish = Vanish(), 14 | ) : Config(languageDirectory, "${settings.general.language}.yml") { 15 | 16 | @ConfigSerializable 17 | data class General( 18 | val prefix: String = "<#67e8f9>SayanVanish | ", 19 | val reloaded: String = "Plugin successfully reloaded. Please note that some changes may require a server restart to take effect. Subsequent reloads may cause issues.", 20 | val playerNotFound: String = "Player not found", 21 | val userNotFound: String = "Couldn't get user data for player ", 22 | val haveToProvidePlayer: String = "You have to provide a player.", 23 | val dontHavePermission: String = "You don't have permission to do that.", 24 | val confirmUpdate: String = "Execute update again to update the plugin.", 25 | val updating: String = "Updating the plugin... please wait.", 26 | val updated: String = "Plugin has been successfully updated to version . make sure to restart the server to prevent unexpected behaviour.", 27 | val proxyUpdateWarning: String = "Please note that this update could impact your proxy server. Be sure to update your proxy server separately to ensure compatibility.", 28 | val updateFailed: String = "Failed to update the plugin. Please try again later." 29 | ) 30 | 31 | @ConfigSerializable 32 | data class Vanish( 33 | val placeholderPrefix: String = "&7[Vanished]&r ", 34 | val placeholderSuffix: String = " &r&7[Vanished]", 35 | val vanishToggle: String = "Successfully updated vanish state to ", 36 | ) 37 | 38 | enum class Language(val id: String) { 39 | EN_US("en_US"), 40 | } 41 | 42 | companion object { 43 | val languageDirectory = File(Platform.get().rootDirectory, "languages") 44 | 45 | @JvmStatic 46 | 47 | fun defaultConfig(): LanguageConfig { 48 | return LanguageConfig().also { it.save() } 49 | } 50 | 51 | @JvmStatic 52 | fun fromConfig(): LanguageConfig? { 53 | return fromConfig(File(languageDirectory, "${settings.general.language}.yml")) 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /sayanvanish-proxy/src/main/kotlin/org/sayandev/sayanvanish/proxy/config/SettingsConfig.kt: -------------------------------------------------------------------------------- 1 | package org.sayandev.sayanvanish.proxy.config 2 | 3 | import org.sayandev.sayanvanish.api.Platform 4 | import org.sayandev.stickynote.core.configuration.Config 5 | import org.spongepowered.configurate.objectmapping.ConfigSerializable 6 | import org.spongepowered.configurate.objectmapping.meta.Comment 7 | import java.io.File 8 | import java.util.UUID 9 | 10 | public var settings: SettingsConfig = SettingsConfig.fromConfig() ?: SettingsConfig.defaultConfig() 11 | 12 | @ConfigSerializable 13 | class SettingsConfig( 14 | @Comment(""" 15 | Do NOT copy and paste the SayanVanish directory across multiple servers. 16 | The server-id is generated during the plugin's first startup. 17 | Duplicating this file could lead to synchronization issues. 18 | 19 | General settings for the plugin 20 | """) 21 | val general: General = General(), 22 | @Comment("Command settings for the plugin") 23 | val command: Command = Command() 24 | ) : Config( 25 | Platform.get().rootDirectory, 26 | fileName 27 | ) { 28 | 29 | @ConfigSerializable 30 | data class General( 31 | @Comment("Unique server identifier. doesn't do anything special yet.") 32 | val serverId: String = "${Platform.get().id}-${UUID.randomUUID()}", 33 | @Comment(""" 34 | Language name 35 | Note: By default, it only includes the `en_US` language. 36 | However, you can create and add your own custom languages. 37 | """) 38 | val language: String = LanguageConfig.Language.EN_US.id, 39 | @Comment("Weather to purge online history of users on startup.") 40 | val purgeOnlineHistoryOnStartup: Boolean = true, 41 | val purgeUsersOnStartup: Boolean = true, 42 | @Comment("Cache update period in milliseconds. low values may cause performance issues.") 43 | val cacheUpdatePeriodMillis: Long = 300, 44 | @Comment("Basic cache update period in milliseconds. low values may cause performance issues.") 45 | val basicCacheUpdatePeriodMillis: Long = 5000, 46 | @Comment("Whether to include prefix in messages, can be found in the language file.") 47 | val includePrefixInMessages: Boolean = true, 48 | ) 49 | 50 | @ConfigSerializable 51 | data class Command( 52 | @Comment("Name of the main command") 53 | val name: String = "sayanvanishproxy", 54 | @Comment("Aliases for the main command") 55 | val aliases: List = listOf( 56 | "vp", 57 | "vanishp", 58 | "svp" 59 | ) 60 | ) 61 | 62 | companion object { 63 | private val fileName = "settings.yml" 64 | val settingsFile = File(Platform.get().rootDirectory, fileName) 65 | 66 | @JvmStatic 67 | fun defaultConfig(): SettingsConfig { 68 | return SettingsConfig().also { it.save() } 69 | } 70 | 71 | @JvmStatic 72 | fun fromConfig(): SettingsConfig? { 73 | return fromConfig(settingsFile) 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenLocal() 4 | gradlePluginPortal() 5 | 6 | maven("https://repo.sayandev.org/snapshots") 7 | } 8 | } 9 | 10 | plugins { 11 | id("org.gradle.toolchains.foojay-resolver-convention") version "0.9.0" 12 | id("org.sayandev.stickynote.settings") version "1.9.1.10" 13 | } 14 | 15 | rootProject.name = "SayanVanish" 16 | 17 | include("sayanvanish-api") 18 | include("sayanvanish-bukkit") 19 | include("sayanvanish-proxy") 20 | include("sayanvanish-proxy:sayanvanish-proxy-velocity") 21 | include("sayanvanish-proxy:sayanvanish-proxy-bungeecord") --------------------------------------------------------------------------------