Release Vanish-OG

This commit is contained in:
NotAlexNoyle committed 2026-04-08 07:53:46 -04:00
1 parent 84627afee6
commit 5b11813ff7
104 files changed
+5320 -2049

No files matched your search

View File
Whitespace-only changes.
+6 -1
View File
@@ -1,6 +1,12 @@
*.class
target/
out/
build/
.gradle/
bin/
.classpath
.project
.checkstyle
# Mobile Tools for Java (J2ME)
.mtj.tmp/
@@ -10,7 +16,6 @@ out/
*.eml
*.iml
# Eclipse files
.settings
-34
View File
@@ -1,34 +0,0 @@
image: maven:3-jdk-9-slim
cache:
paths:
- .m2/repository
variables:
MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=WARN -Dorg.slf4j.simpleLogger.showDateTime=true -Djava.awt.headless=true"
MAVEN_CLI_OPTS: "--batch-mode --errors --fail-at-end --show-version -DinstallAtEnd=true -DdeployAtEnd=true"
stages:
- build
- deploy
build:
stage: build
script:
- apt update
- apt install wget -y
- wget https://gitlab.com/cubekrowd/mavenrepo/raw/master/build2.sh
- bash build2.sh
artifacts:
paths:
- "*.jar"
deploy:
stage: deploy
script:
- apt update
- apt install wget -y
- wget https://gitlab.com/cubekrowd/mavenrepo/raw/master/deploy1.sh
- bash deploy1.sh
only:
- master
+3
View File
@@ -0,0 +1,3 @@
[submodule "libs/Utilities-OG"]
path = libs/Utilities-OG
url = https://github.com/true-og/Utilities-OG
+164 -20
View File
@@ -1,27 +1,171 @@
# SuperVanish
Website: https://www.spigotmc.org/resources/supervanish-be-invisible.1331/
# Vanish-OG
Bukkit-Plugin for Spigot/CraftBukkit (modded Minecraft server)
A fork of [SuperVanish](https://www.spigotmc.org/resources/supervanish-be-invisible.1331/) for [TrueOG Network](https://trueog.net). Lets staff become completely invisible so they can quietly observe and moderate.
Allows server admins to be completely invisible and undetectable for other players, which helps them with their administrative work.
Built for **Purpur 1.19.4**. Clients from **Minecraft 1.8 through 1.21.11** can connect through ViaSuite (ViaVersion / ViaBackwards / ViaRewind) and vanish works correctly across the board.
Please note that I currently do not have much time to dedicate to this plugin and as such can not do extensive testing of pull requests. I appreciate pull requests, but please keep them as small and simple as possible. Please report issues on SpigotMC via PM!
### What's different from SuperVanish?
## Maven Repository
- **Tab list hiding works on 1.19+** Upstream switched it off after a Mojang change broke it. Vanish-OG handles the new format so vanished staff stay hidden from the tab list.
- **Spectator indication works on 1.19.4 again.** `MarkVanishedPlayersAsSpectators` now uses the native 1.19.4 player info update packet, so staff who can see vanished players still get the dark-gray/head-only tab list cue across Via-served 1.8-1.21.11 clients.
- **Silent chest opening works on 1.19+ again.** Same fix — peeking into containers looks completely normal to other players.
- **All connected clients see the same thing.** Whether someone joins from 1.8 or the latest 1.21.11 release through ViaSuite, vanish behaves identically.
- **[Chat-OG](https://github.com/true-og/Chat-OG) owns announcement visibility.** Vanish-OG no longer rewrites join/quit, death, or advancement messages and no longer emits fake join/quit broadcasts on vanish toggles. Chat-OG is responsible for hiding vanished-player activity from Discord-facing announcements.
- **OpenInv support.** If installed, Vanish-OG hands off chest viewing to OpenInv so staff never enter spectator mode and can't get stuck there after a crash.
- **MiniPlaceholders support** (via Utilities-OG) for use in chat, tab, and hologram plugins.
- **Safer chest peeking.** Staff no longer occasionally fall through the floor when finishing a peek. Pair with [WGamemode](https://github.com/true-og/WGamemode) to auto-restore gamemodes after crashes if OpenInv is not installed.
- **Removed old clutter.** Deprecated functions are removed and the code is updated for java 17 conventions.
### Chat-OG handles some events that used to be handled by SuperVanish
If your stack includes [Chat-OG](https://github.com/true-og/Chat-OG), think of the split like this:
- Vanish-OG manages vanish state, player/entity visibility, tab list hiding, spectator indication, and silent chest behavior.
- Chat-OG manages whether vanished-player join/quit, kick, advancement, death, and Discord bridge announcements are surfaced.
This avoids two plugins trying to suppress or fake the same announcement events.
### API
Vanish-OG ships the same Bukkit-facing API package as SuperVanish: `de.myzelyam.api.vanish`.
### Main entry point
Use [`VanishAPI`](/home/notalexnoyle/eclipse-workspace/Vanish-OG/src/main/java/de/myzelyam/api/vanish/VanishAPI.java) for all direct integrations.
```java
import de.myzelyam.api.vanish.VanishAPI;
import net.trueog.utilitiesog.UtilitiesOG;
```
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependencies>
<!-- SuperVanish -->
<dependency>
<groupId>com.github.LeonMangler</groupId>
<artifactId>SuperVanish</artifactId>
<version>6.2.18-3</version>
</dependency>
</dependencies>
### Commands
Main command: `/vanish` (also `/v`, `/sv`, `/supervanish`).
### State queries
- `VanishAPI.getInvisiblePlayers()`
Returns the UUIDs of all online vanished players. Run this on the main server thread.
- `VanishAPI.getAllInvisiblePlayers()`
Returns the UUIDs of all vanished players, including offline players stored in `data.yml`. Run this on the main server thread.
- `VanishAPI.isPlayerVanished(Player player)`
Preferred asynchronous check for an online player. Safe to call from either thread. The returned `CompletableFuture<Boolean>` completes off the main thread.
- `VanishAPI.isUUIDVanished(UUID uuid)`
Preferred asynchronous check for any player UUID. Safe to call from either thread. The returned `CompletableFuture<Boolean>` completes off the main thread.
If you touch Bukkit APIs in an async completion callback, switch back to the server thread first.
Java:
```java
VanishAPI.isUUIDVanished(player.getUniqueId()).thenAccept(vanished ->
Bukkit.getScheduler().runTask(plugin, () -> {
if (vanished) {
UtilitiesOG.trueogMessage(player, "You are vanished.");
}
})
);
```
Kotlin:
```kotlin
VanishAPI.isUUIDVanished(player.uniqueId).thenAccept { vanished ->
Bukkit.getScheduler().runTask(plugin) {
if (vanished) {
UtilitiesOG.trueogMessage(player, "You are vanished.")
}
}
}
```
### Visibility control
- `VanishAPI.hidePlayer(Player player)`
Vanishes the player through the normal SuperVanish pipeline and fires the related events. Main thread only.
- `VanishAPI.showPlayer(Player player)`
Reveals the player through the normal SuperVanish pipeline and fires the related events. Main thread only.
- `VanishAPI.canSee(Player viewer, Player viewed)`
Returns whether `viewer` should currently be able to see `viewed`. Main thread only.
Java:
```java
if (!VanishAPI.canSee(viewer, target)) {
UtilitiesOG.trueogMessage(viewer, "<red>That player is hidden from you.");
}
VanishAPI.hidePlayer(target);
VanishAPI.showPlayer(target);
```
Kotlin:
```kotlin
if (!VanishAPI.canSee(viewer, target)) {
UtilitiesOG.trueogMessage(viewer, "<red>That player is hidden from you.")
}
VanishAPI.hidePlayer(target)
VanishAPI.showPlayer(target)
```
### Configuration access
- `VanishAPI.getConfiguration()`
Returns `config.yml`. Main thread only; treat the returned `FileConfiguration` as Bukkit-owned mutable state.
- `VanishAPI.getMessages()`
Returns `messages.yml`. Main thread only; treat the returned `FileConfiguration` as Bukkit-owned mutable state.
- `VanishAPI.getPlayerData()`
Returns the persistent player storage file (`data.yml` / player data config). Main thread only.
- `VanishAPI.reloadConfig()`
Reloads the plugin. Main thread only.
- `VanishAPI.getPlugin()`
Returns the active `SuperVanish` plugin instance. Fetching the reference is cheap, but any interaction with the plugin or Bukkit APIs should stay on the main thread.
### Events
The API exposes the following Bukkit events in `de.myzelyam.api.vanish`:
All of these are normal Bukkit events and should be handled on the main server thread. Do not assume they fire asynchronously.
- [`PlayerHideEvent`](/home/notalexnoyle/eclipse-workspace/Vanish-OG/src/main/java/de/myzelyam/api/vanish/PlayerHideEvent.java)
Fired before a player is hidden. Cancellable. Includes `isSilent()` / `setSilent(boolean)`.
- [`PostPlayerHideEvent`](/home/notalexnoyle/eclipse-workspace/Vanish-OG/src/main/java/de/myzelyam/api/vanish/PostPlayerHideEvent.java)
Fired after a player has been hidden. Not cancellable. Includes `isSilent()`.
- [`PlayerShowEvent`](/home/notalexnoyle/eclipse-workspace/Vanish-OG/src/main/java/de/myzelyam/api/vanish/PlayerShowEvent.java)
Fired before a player is shown. Cancellable. Includes `isSilent()` / `setSilent(boolean)`.
- [`PostPlayerShowEvent`](/home/notalexnoyle/eclipse-workspace/Vanish-OG/src/main/java/de/myzelyam/api/vanish/PostPlayerShowEvent.java)
Fired after a player has been shown. Not cancellable. Includes `isSilent()`.
- [`PlayerVanishStateChangeEvent`](/home/notalexnoyle/eclipse-workspace/Vanish-OG/src/main/java/de/myzelyam/api/vanish/PlayerVanishStateChangeEvent.java)
Fired when persisted vanish state changes for a UUID. Cancellable. Exposes `isVanishing()`, `getUUID()`, `getName()`, and `getCause()`.
### Example listener
Java:
```java
@EventHandler
public void onPlayerHide(PlayerHideEvent event) {
if (!event.getPlayer().hasPermission("myplugin.allowvanish")) {
UtilitiesOG.trueogMessage(event.getPlayer(), "<red>You are not allowed to vanish.");
event.setCancelled(true);
}
}
```
Kotlin:
```kotlin
@EventHandler
fun onPlayerHide(event: PlayerHideEvent) {
if (!event.player.hasPermission("myplugin.allowvanish")) {
UtilitiesOG.trueogMessage(event.player, "<red>You are not allowed to vanish.")
event.isCancelled = true
}
}
```
## Reporting issues
Open an issue on the [Vanish-OG GitHub repository](https://github.com/true-og/Vanish-OG/issues).
Executable
+4
View File
@@ -0,0 +1,4 @@
#!/usr/bin/bash
# Fetch all submodule content.
git submodule update --force --recursive --init
+151
View File
@@ -0,0 +1,151 @@
/* This is free and unencumbered software released into the public domain */
/* ------------------------------ Plugins ------------------------------ */
plugins {
id("java") // Import Java plugin.
id("java-library") // Import Java Library plugin.
id("com.diffplug.spotless") version "8.1.0" // Import Spotless plugin.
id("com.gradleup.shadow") version "8.3.9" // Import Shadow plugin.
id("checkstyle") // Import Checkstyle plugin.
eclipse // Import Eclipse plugin.
}
/* --------------------------- JDK / Kotlin ---------------------------- */
java {
sourceCompatibility = JavaVersion.VERSION_17 // Compile with JDK 17 compatibility.
toolchain { // Select Java toolchain.
languageVersion.set(JavaLanguageVersion.of(17)) // Use JDK 17.
vendor.set(JvmVendorSpec.GRAAL_VM) // Use GraalVM CE.
}
}
/* ----------------------------- Metadata ------------------------------ */
group = "de.myzelyam.vanishog" // Declare bundle identifier.
version = "6.2.22" // Declare plugin version (will be in .jar).
val apiVersion = "1.13" // Declare minecraft server target version.
/* ----------------------------- Resources ----------------------------- */
tasks.named<ProcessResources>("processResources") {
val props = mapOf("version" to version, "apiVersion" to apiVersion)
inputs.properties(props) // Indicates to rerun if version changes.
filesMatching("plugin.yml") { expand(props) }
from("LICENSE") { into("/") } // Bundle licenses into jarfiles.
}
/* ---------------------------- Repos ---------------------------------- */
repositories {
mavenCentral() // Import the Maven Central Maven Repository.
gradlePluginPortal() // Import the Gradle Plugin Portal Maven Repository.
maven { url = uri("https://repo.purpurmc.org/snapshots") } // Import the PurpurMC Maven Repository.
maven { url = uri("https://repo.papermc.io/repository/maven-public/") } // PaperMC.
maven { url = uri("https://repo.essentialsx.net/releases/") } // EssentialsX.
maven { url = uri("https://jitpack.io") } // JitPack (VaultAPI, TrailGUI, OpenInv).
maven { url = uri("https://repo.citizensnpcs.co/") } // Citizens.
maven { url = uri("https://repo.mikeprimm.com/") } // Dynmap.
maven { url = uri("https://libraries.minecraft.net") } // Mojang Brigadier.
}
/* ---------------------- Java project deps ---------------------------- */
dependencies {
// Purpur API (server target).
compileOnly("org.purpurmc.purpur:purpur-api:1.19.4-R0.1-SNAPSHOT")
// Import TrueOG Network Utilities-OG Java API (from source).
compileOnlyApi(project(":libs:Utilities-OG"))
// EssentialsX API.
compileOnly("net.essentialsx:EssentialsX:2.21.0") {
exclude(group = "org.bstats", module = "bstats-bukkit") // Exclude bstats.
}
// LuckPerms API (group, prefix, suffix lookups).
compileOnly("net.luckperms:api:5.5")
// ProtocolLib (local jar in libs/).
compileOnly(files("libs/ProtocolLib-5.0.jar"))
// Citizens API.
compileOnly("net.citizensnpcs:citizensapi:2.0.28-SNAPSHOT")
// TrailGUI (via JitPack).
compileOnly("com.github.SinnDevelopment:TrailGUI:37659dda03")
// Dynmap API.
compileOnly("us.dynmap:dynmap-api:3.1")
// Import MiniPlaceholders API.
compileOnly("io.github.miniplaceholders:miniplaceholders-api:2.2.3")
// Mojang Brigadier (provided at runtime by Purpur).
compileOnly("com.mojang:brigadier:1.0.18")
}
apply(from = "eclipse.gradle.kts") // Import eclipse classpath support script.
/* ---------------------- Reproducible jars ---------------------------- */
tasks.withType<AbstractArchiveTask>().configureEach { // Ensure reproducible .jars
isPreserveFileTimestamps = false
isReproducibleFileOrder = true
}
/* ----------------------------- Shadow -------------------------------- */
tasks.shadowJar {
exclude("io.github.miniplaceholders.*") // Exclude the MiniPlaceholders package from being shadowed.
isEnableRelocation = true
relocationPrefix = "${project.group}.shadow"
archiveClassifier.set("") // Use empty string instead of null.
archiveBaseName.set(rootProject.name) // Vanish-OG-${version}.jar
minimize()
}
tasks.jar { archiveClassifier.set("part") } // Applies to root jarfile only.
tasks.build { dependsOn(tasks.spotlessApply, tasks.shadowJar) } // Build depends on spotless and shadow.
/* --------------------------- Javac opts ------------------------------- */
tasks.withType<JavaCompile>().configureEach {
options.compilerArgs.add("-parameters") // Enable reflection for java code.
options.isFork = true // Run javac in its own process.
options.compilerArgs.add("-Xlint:deprecation") // Trigger deprecation warning messages.
options.encoding = "UTF-8" // Use UTF-8 file encoding.
}
/* ----------------------------- Auto Formatting ------------------------ */
spotless {
java {
eclipse().configFile("config/formatter/eclipse-java-formatter.xml") // Eclipse java formatting.
leadingTabsToSpaces() // Convert leftover leading tabs to spaces.
removeUnusedImports() // Remove imports that aren't being called.
}
kotlinGradle {
ktfmt().kotlinlangStyle().configure { it.setMaxWidth(120) } // JetBrains Kotlin formatting.
target("build.gradle.kts", "settings.gradle.kts") // Gradle files to format.
}
}
checkstyle {
toolVersion = "10.18.1" // Declare checkstyle version to use.
configFile = file("config/checkstyle/checkstyle.xml") // Point checkstyle to config file.
isIgnoreFailures = true // Don't fail the build if checkstyle does not pass.
isShowViolations = true // Show the violations in any IDE with the checkstyle plugin.
}
tasks.named("compileJava") {
dependsOn("spotlessApply") // Run spotless before compiling with the JDK.
}
tasks.named("spotlessCheck") {
dependsOn("spotlessApply") // Run spotless before checking if spotless ran.
}
/* ------------------------------ Eclipse SHIM ------------------------- */
// This can't be put in eclipse.gradle.kts because Gradle is weird.
subprojects {
apply(plugin = "java-library")
apply(plugin = "eclipse")
eclipse.project.name = "${project.name}-${rootProject.name}"
tasks.withType<Jar>().configureEach { archiveBaseName.set("${project.name}-${rootProject.name}") }
}
+34
View File
@@ -0,0 +1,34 @@
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
"https://checkstyle.org/dtds/configuration_1_3.dtd">
<module name="Checker">
<module name="FileTabCharacter">
<property name="eachLine" value="true"/>
<property name="severity" value="error"/>
</module>
<module name="TreeWalker">
<module name="FinalLocalVariable"/>
<module name="AvoidStarImport"/>
<module name="Indentation">
<property name="severity" value="ignore"/>
</module>
</module>
<module name="RegexpSingleline">
<property name="format" value="\borg\.junit\.jupiter\.api\.Assertions\.fail\s*\(\s*&quot;Not yet implemented&quot;\s*\)\s*;"/>
<property name="message" value="Remove placeholder fail() and implement the test."/>
<property name="ignoreCase" value="true"/>
<property name="severity" value="warning"/>
</module>
<module name="RegexpSingleline">
<property name="format" value="(?&lt;!\w)fail\s*\(\s*&quot;Not yet implemented&quot;\s*\)\s*;"/>
<property name="message" value="Remove placeholder fail() and implement the test."/>
<property name="severity" value="warning"/>
</module>
</module>
+404
View File
@@ -0,0 +1,404 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<profiles version="23">
<profile kind="CodeFormatterProfile" name="TrueOG Network Formatting" version="23">
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_ellipsis" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_javadoc_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.indentation.size" value="4"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.align_with_spaces" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation" value="2"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_before_code_block" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_switch_case_expressions" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_package" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.indent_root_tags" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.enabling_tag" value="@formatter:on"/>
<setting id="org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_record_components" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_multiplicative_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_logical_operator" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_annotation_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_record_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_multiplicative_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_abstract_method" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.keep_enum_constant_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.align_variable_declarations_on_columns" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiplicative_operator" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.keep_anonymous_type_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_switch_case_expressions" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_shift_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" value="next_line_on_wrap"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_code_block" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_bitwise_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_type_parameters" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_loops" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.keep_simple_for_body_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_switch_case_arrow_operator" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_unary_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_ellipsis" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_enum_constant" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.text_block_indentation" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.align_type_members_on_columns" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_assignment" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_module_statements" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.align_tags_names_descriptions" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_if_then_body_block_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_permitted_types" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case" value="next_line_on_wrap"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression_chain" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_header" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_type_annotations" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_assertion_message_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_method_declaration" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines" value="2147483647"/>
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_bitwise_operator" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration" value="next_line_on_wrap"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_resources_in_try" value="80"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_source_code" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_field" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_method" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_not_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_html" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_if" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.indent_empty_lines" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_type_arguments" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_unary_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_package" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_label" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_case" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_permitted_types_in_type_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_record_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_bitwise_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.comment.javadoc_do_not_separate_block_tags" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.indent_tag_description" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_record_constructor" value="next_line_on_wrap"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_string_concatenation" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_array_initializer" value="next_line_on_wrap"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_shift_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_shift_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_simple_do_while_body_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_record_components" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_additive_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.keep_simple_getter_setter_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case_after_arrow" value="next_line_on_wrap"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_string_concatenation" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.join_lines_in_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_record_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_relational_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_import_groups" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_logical_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_record_declaration" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_default" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.disabling_tag" value="@formatter:off"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_method_body" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_case" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.keep_switch_body_block_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_block" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration" value="next_line_on_wrap"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_switch_case_with_arrow" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.align_tags_descriptions_grouped" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.comment.line_length" value="80"/>
<setting id="org.eclipse.jdt.core.formatter.use_on_off_tags" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.keep_method_body_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_loop_body_block_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" value="next_line_on_wrap"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_type_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_additive_operator" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_constructor" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_relational_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_record_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration" value="next_line_on_wrap"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_lambda_body" value="next_line_on_wrap"/>
<setting id="org.eclipse.jdt.core.formatter.compact_else_if" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_parameter" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_relational_operator" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.align_arrows_in_switch_on_columns" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_additive_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_string_concatenation" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_line_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.align_selector_in_method_invocation_on_expression_first_line" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_record_declaration" value="next_line_on_wrap"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_switch_case_with_arrow_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_switch_case_with_colon" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_after_code_block" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" value="80"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_type" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_local_variable" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration" value="next_line_on_wrap"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_default" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_between_different_tags" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_additive_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.join_wrapped_lines" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_field" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_conditional_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.join_line_comments" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_shift_operator" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.keep_code_block_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_record_components" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.tabulation.size" value="4"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_bitwise_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer" value="2"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_record_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_assignment_operator" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_switch" value="next_line_on_wrap"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_switch_case_with_arrow" value="20"/>
<setting id="org.eclipse.jdt.core.formatter.keep_lambda_body_block_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_method" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_record_constructor_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_assertion_message" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_member_type" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_logical_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_record_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_relational_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_block_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_last_class_body_declaration" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_body" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.keep_simple_while_body_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_logical_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_statement_group_in_switch" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_permitted_types" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_enum_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_constant" value="next_line_on_wrap"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_type_declaration" value="next_line_on_wrap"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_multiplicative_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_package" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.indent_parameter_description" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_code_block" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.tabulation.char" value="space"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_string_concatenation" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.lineSplit" value="120"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch" value="insert"/>
</profile>
</profiles>
+158
View File
@@ -0,0 +1,158 @@
/* This is free and unencumbered software released into the public domain */
import org.gradle.api.tasks.bundling.Jar
import org.gradle.plugins.ide.eclipse.model.EclipseModel
import java.nio.file.Files
import javax.xml.parsers.DocumentBuilderFactory
import javax.xml.transform.OutputKeys
import javax.xml.transform.TransformerFactory
import javax.xml.transform.dom.DOMSource
import javax.xml.transform.stream.StreamResult
val sourcePluginProjects =
configurations
.getByName("compileClasspath")
.allDependencies
.filterIsInstance<ProjectDependency>()
.map { it.path }
.filter { it.startsWith(":libs:") }
.distinct()
val sourcePluginProjectNames = sourcePluginProjects.map { it.substringAfterLast(":") }.toSet()
sourcePluginProjects.forEach { evaluationDependsOn(it) }
val ideLibs: Configuration by
configurations.creating {
isCanBeResolved = true
isCanBeConsumed = false
}
configure<EclipseModel> {
project {
name = "${rootProject.name}-Plugin"
}
classpath {
plusConfigurations.add(ideLibs)
}
}
val ideLibDir = layout.buildDirectory.dir("ide-libs")
val hashRegex = Regex("-[0-9a-fA-F]{10}(?=\\.jar$)") // Black magic.
val copyTasks = mutableListOf<TaskProvider<Copy>>()
sourcePluginProjects.forEach { path ->
val sub = project(path)
val archiveTaskName = if ("shadowJar" in sub.tasks.names) "shadowJar" else "jar"
val archiveTask = sub.tasks.named<Jar>(archiveTaskName)
val copyTask =
tasks.register<Copy>("ideCopy${sub.name.replaceFirstChar(Char::titlecase)}") {
dependsOn(archiveTask)
from(archiveTask.flatMap { it.archiveFile })
into(ideLibDir)
rename { it.replace(hashRegex, "") } // Remove git commit hash from jarfile.
}
copyTasks += copyTask
}
/* Ensure the jars exist before .classpath is generated */
tasks.named("eclipseClasspath").configure { dependsOn(copyTasks) }
/* Supply those jars to the ideLibs configuration (after evaluation) */
afterEvaluate { dependencies { add("ideLibs", fileTree(ideLibDir) { include("*.jar") }) } }
val injectIdeLibs =
tasks.register("injectIdeLibs") {
dependsOn("eclipse") // run after all eclipse files are generated
doLast {
val cpFile = file(".classpath")
if (!cpFile.exists()) return@doLast
val jars = fileTree(ideLibDir.get()) { include("*.jar") }.files.sortedBy { it.name }
if (jars.isEmpty()) return@doLast
// Parse DOM
val dbf = DocumentBuilderFactory.newInstance()
val doc = dbf.newDocumentBuilder().parse(cpFile)
val root = doc.documentElement
// Helper to see if an entry already exists
fun exists(path: String): Boolean =
root.getElementsByTagName("classpathentry").let { list ->
(0 until list.length).any { i ->
val n = list.item(i)
val kind = n.attributes?.getNamedItem("kind")?.nodeValue
val p = n.attributes?.getNamedItem("path")?.nodeValue
kind == "lib" && p == path
}
}
// Remove any entry that points to ide-libs dir or unresolved :libs: project refs.
val toRemove = mutableListOf<org.w3c.dom.Node>()
val list = root.getElementsByTagName("classpathentry")
val dirPath = ideLibDir.get().asFile.absolutePath
for (i in 0 until list.length) {
val n = list.item(i)
val kind = n.attributes?.getNamedItem("kind")?.nodeValue
val p = n.attributes?.getNamedItem("path")?.nodeValue ?: ""
val isIdeLibEntry = kind == "lib" && (p == dirPath || p.startsWith("$dirPath/"))
val sourceProjectPath = p.removePrefix("/")
val isHalfImportedSourceModule = kind == "src" && p.startsWith("/libs:")
val isSourcePluginModule = kind == "src" && sourceProjectPath in sourcePluginProjectNames
if (isIdeLibEntry || isHalfImportedSourceModule || isSourcePluginModule) {
toRemove += n
}
}
toRemove.forEach { root.removeChild(it) }
// Append our jar entries LAST
jars.forEach { f ->
val abs = f.absolutePath
if (!exists(abs)) {
val entry = doc.createElement("classpathentry")
entry.setAttribute("kind", "lib")
entry.setAttribute("path", abs)
root.appendChild(entry)
}
}
// Write back pretty
val tf =
TransformerFactory.newInstance().newTransformer().apply {
setOutputProperty(OutputKeys.INDENT, "yes")
setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "1")
setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no")
setOutputProperty(OutputKeys.ENCODING, "UTF-8")
}
Files.newBufferedWriter(cpFile.toPath()).use { w -> tf.transform(DOMSource(doc), StreamResult(w)) }
}
}
tasks.named("eclipse").configure { finalizedBy(injectIdeLibs) }
val eclipseModel = extensions.getByType<EclipseModel>()
fun Project.addResolvableEclipseConfigs() {
val jarAttr =
objects.named(org.gradle.api.attributes.LibraryElements::class, org.gradle.api.attributes.LibraryElements.JAR)
val apiAttr = objects.named(org.gradle.api.attributes.Usage::class, org.gradle.api.attributes.Usage.JAVA_API)
val compileOnlyRes =
configurations.create("eclipseCompileOnly") {
extendsFrom(configurations.getByName("compileOnly"))
isCanBeResolved = true
isCanBeConsumed = false
attributes.attribute(org.gradle.api.attributes.Usage.USAGE_ATTRIBUTE, apiAttr)
attributes.attribute(org.gradle.api.attributes.LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, jarAttr)
}
val compileOnlyApiRes =
configurations.create("eclipseCompileOnlyApi") {
extendsFrom(configurations.getByName("compileOnlyApi"))
isCanBeResolved = true
isCanBeConsumed = false
attributes.attribute(org.gradle.api.attributes.Usage.USAGE_ATTRIBUTE, apiAttr)
attributes.attribute(org.gradle.api.attributes.LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, jarAttr)
}
eclipseModel.classpath.plusConfigurations.addAll(listOf(compileOnlyRes, compileOnlyApiRes))
}
addResolvableEclipseConfigs()
+1
View File
@@ -0,0 +1 @@
org.gradle.java.home=/home/notalexnoyle/.sdkman/candidates/java/17.0.9-graalce
Binary file not shown.
+7
View File
@@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored Executable
+251
View File
@@ -0,0 +1,251 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH="\\\"\\\""
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"
Vendored
+94
View File
@@ -0,0 +1,94 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:execute
@rem Setup the command line
set CLASSPATH=
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
+339
View File
@@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
Binary file not shown.
+1
Submodule libs/Utilities-OG added at 20c2947f83.
-189
View File
@@ -1,189 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>de.myzelyam</groupId>
<artifactId>SuperVanish</artifactId>
<version>6.2.21</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>17</source>
<target>17</target>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
<repositories>
<repository>
<id>papermc-repo</id>
<url>https://papermc.io/repo/repository/maven-public/</url>
</repository>
<repository>
<id>essentials-repo</id>
<url>https://repo.essentialsx.net/releases</url>
</repository>
<repository>
<id>dmulloy2-repo</id>
<url>https://repo.dmulloy2.net/nexus/repository/public/</url>
</repository>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
<repository>
<id>citizens-repo</id>
<url>https://repo.citizensnpcs.co/</url>
</repository>
<repository>
<id>placeholderapi-repo</id>
<url>https://repo.extendedclip.com/content/repositories/placeholderapi/</url>
</repository>
<repository>
<id>dynmap-repo</id>
<url>https://repo.mikeprimm.com/</url>
</repository>
<repository>
<id>minecraft-libraries</id>
<name>Minecraft Libraries</name>
<url>https://libraries.minecraft.net</url>
</repository>
<repository>
<id>mvdw-software-repo</id>
<name>MVdW Public Repositories</name>
<url>https://repo.mvdw-software.com/content/groups/public/</url>
</repository>
</repositories>
<dependencies>
<!--Paper API-->
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>1.20.1-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<!--Lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
<!--EssentialsX API-->
<dependency>
<groupId>net.essentialsx</groupId>
<artifactId>EssentialsX</artifactId>
<version>2.19.0</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>org.bstats</groupId>
<artifactId>bstats-bukkit</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--ProtocolLib-->
<dependency>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib</artifactId>
<version>4.8.0</version>
<scope>provided</scope>
</dependency>
<!--Vault API-->
<dependency>
<groupId>com.github.MilkBowl</groupId>
<artifactId>VaultAPI</artifactId>
<version>1.7.1</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
</exclusion>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>craftbukkit</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--Citizens API-->
<dependency>
<groupId>net.citizensnpcs</groupId>
<artifactId>citizensapi</artifactId>
<version>2.0.28-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<!--TrailGUI-->
<dependency>
<groupId>com.github.SinnDevelopment</groupId>
<artifactId>TrailGUI</artifactId>
<version>37659dda03</version>
<scope>provided</scope>
</dependency>
<!--Dynmap-->
<dependency>
<groupId>us.dynmap</groupId>
<artifactId>dynmap-api</artifactId>
<version>3.1</version>
<scope>provided</scope>
</dependency>
<!--MVdWPlaceholderAPI-->
<dependency>
<groupId>be.maximvdw</groupId>
<artifactId>MVdWPlaceholderAPI</artifactId>
<version>3.1.1-SNAPSHOT</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--PlaceholderAPI-->
<dependency>
<groupId>me.clip</groupId>
<artifactId>placeholderapi</artifactId>
<version>2.10.10</version>
<scope>provided</scope>
</dependency>
<!--BrigadierAPI-->
<dependency>
<groupId>com.mojang</groupId>
<artifactId>brigadier</artifactId>
<version>1.0.18</version>
</dependency>
<!--OpenInv-->
<dependency>
<groupId>com.github.Jikoo</groupId>
<artifactId>OpenInv</artifactId>
<version>5.1.12</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
+13
View File
@@ -0,0 +1,13 @@
rootProject.name = "Vanish-OG"
ProcessBuilder("sh", "bootstrap.sh").directory(rootDir).inheritIO().start().let {
if (it.waitFor() != 0) throw GradleException("bootstrap.sh failed")
}
file("libs")
.listFiles()
?.filter { it.isDirectory && !it.name.startsWith(".") }
?.forEach { dir ->
include(":libs:${dir.name}")
project(":libs:${dir.name}").projectDir = dir
}
@@ -20,34 +20,49 @@ public class PlayerHideEvent extends PlayerEvent implements Cancellable {
private boolean isCancelled = false;
public PlayerHideEvent(Player p, boolean silent) {
super(p);
this.silent = silent;
}
public static HandlerList getHandlerList() {
return handlers;
}
public boolean isSilent() {
return silent;
}
public void setSilent(boolean silent) {
this.silent = silent;
}
@Override
public boolean isCancelled() {
return isCancelled;
}
@Override
public void setCancelled(boolean cancel) {
isCancelled = cancel;
}
@Override
public HandlerList getHandlers() {
return handlers;
}
}
@@ -20,34 +20,49 @@ public class PlayerShowEvent extends PlayerEvent implements Cancellable {
private boolean isCancelled = false;
public PlayerShowEvent(Player p, boolean silent) {
super(p);
this.silent = silent;
}
public static HandlerList getHandlerList() {
return handlers;
}
public boolean isSilent() {
return silent;
}
public void setSilent(boolean silent) {
this.silent = silent;
}
@Override
public boolean isCancelled() {
return isCancelled;
}
@Override
public void setCancelled(boolean cancel) {
isCancelled = cancel;
}
@Override
public HandlerList getHandlers() {
return handlers;
}
}
@@ -25,19 +25,25 @@ public class PlayerVanishStateChangeEvent extends Event implements Cancellable {
private boolean isCancelled = false;
public PlayerVanishStateChangeEvent(UUID uuid, String name, boolean vanishing, String cause) {
this.vanishing = vanishing;
this.uuid = uuid;
this.name = name;
this.cause = cause;
}
public static HandlerList getHandlerList() {
return handlers;
}
@Override
public boolean isCancelled() {
return isCancelled;
}
/**
@@ -45,41 +51,55 @@ public class PlayerVanishStateChangeEvent extends Event implements Cancellable {
*/
@Override
public void setCancelled(boolean cancel) {
isCancelled = cancel;
}
/**
* @return TRUE if the player is vanishing, FALSE if the player is reappearing
*/
public boolean isVanishing() {
return vanishing;
}
/**
* @return The name of the player who is vanishing/reappearing
*/
public String getName() {
return name;
}
/**
* @return The UUID of the player who is vanishing/reappearing
*/
public UUID getUUID() {
return uuid;
}
/**
* @return The name of the command sender who caused the vanish state change or null if the cause is
* either not specified or it's SuperVanish itself, please note that if this returns 'CONSOLE' then it's
* the console which caused this state change
* @return The name of the command sender who caused the vanish state change or
* null if the cause is either not specified or it's SuperVanish itself,
* please note that if this returns 'CONSOLE' then it's the console
* which caused this state change
*/
public String getCause() {
return cause;
}
@Override
public HandlerList getHandlers() {
return handlers;
}
}
@@ -18,20 +18,29 @@ public class PostPlayerHideEvent extends PlayerEvent {
private final boolean silent;
public PostPlayerHideEvent(Player p, boolean silent) {
super(p);
this.silent = silent;
}
public static HandlerList getHandlerList() {
return handlers;
}
public boolean isSilent() {
return silent;
}
@Override
public HandlerList getHandlers() {
return handlers;
}
}
@@ -18,20 +18,29 @@ public class PostPlayerShowEvent extends PlayerEvent {
private final boolean silent;
public PostPlayerShowEvent(Player p, boolean silent) {
super(p);
this.silent = silent;
}
public static HandlerList getHandlerList() {
return handlers;
}
public boolean isSilent() {
return silent;
}
@Override
public HandlerList getHandlers() {
return handlers;
}
}
@@ -11,10 +11,12 @@ package de.myzelyam.api.vanish;
import de.myzelyam.supervanish.SuperVanish;
import de.myzelyam.supervanish.utils.Validation;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.concurrent.CompletableFuture;
import java.util.List;
import java.util.UUID;
@@ -30,38 +32,48 @@ public class VanishAPI {
* @return A collection of the UUIDs of all online vanished players
*/
public static List<UUID> getInvisiblePlayers() {
return new ArrayList<>(PLUGIN.getVanishStateMgr().getOnlineVanishedPlayers());
}
/**
* @return A collection of the UUIDs of all vanished players, both online and offline players
* @return A collection of the UUIDs of all vanished players, both online and
* offline players
*/
public static List<UUID> getAllInvisiblePlayers() {
return new ArrayList<>(PLUGIN.getVanishStateMgr().getVanishedPlayers());
}
/**
* Player must be online for this to return true if MySQL is enabled
* Asynchronously checks if an online player is currently vanished.
*
* @param p - the player.
* @return TRUE if the player is invisible, FALSE otherwise.
* @return A future that completes with TRUE if the player is vanished, FALSE
* otherwise.
*/
public static boolean isInvisible(Player p) {
public static CompletableFuture<Boolean> isPlayerVanished(Player p) {
Validation.checkNotNull("Player cannot be null!", p);
return PLUGIN.getVanishStateMgr().isVanished(p.getUniqueId());
return isUUIDVanished(p.getUniqueId());
}
/**
* Checks if a player is invisible, online or not
* <p/>
* Deprecated: Will cause minor lag if mysql is enabled, use asynchronously or sparingly
* Asynchronously checks if a player is vanished, online or offline.
*
* @param uuid - the player's UUID.
* @return TRUE if the player is invisible, FALSE otherwise.
* @return A future that completes with TRUE if the player is vanished, FALSE
* otherwise.
*/
public static boolean isInvisibleOffline(UUID uuid) {
public static CompletableFuture<Boolean> isUUIDVanished(UUID uuid) {
Validation.checkNotNull("UUID cannot be null!", uuid);
return PLUGIN.getVanishStateMgr().isVanished(uuid);
return CompletableFuture.supplyAsync(() -> PLUGIN.getVanishStateMgr().isVanished(uuid),
runnable -> Bukkit.getScheduler().runTaskAsynchronously(PLUGIN, runnable));
}
/**
@@ -70,8 +82,10 @@ public class VanishAPI {
* @param p - the player.
*/
public static void hidePlayer(Player p) {
Validation.checkNotNull("Player cannot be null!", p);
PLUGIN.getVisibilityChanger().hidePlayer(p);
}
/**
@@ -80,8 +94,10 @@ public class VanishAPI {
* @param p - the player.
*/
public static void showPlayer(Player p) {
Validation.checkNotNull("Player cannot be null!", p);
PLUGIN.getVisibilityChanger().showPlayer(p);
}
/**
@@ -89,33 +105,49 @@ public class VanishAPI {
*
* @param viewer - the viewer
* @param viewed - the viewed player
* @return TRUE if viewed is not vanished or viewer has the permission to see viewed
* @return TRUE if viewed is not vanished or viewer has the permission to see
* viewed
*/
public static boolean canSee(Player viewer, Player viewed) {
return PLUGIN.canSee(viewer, viewed);
}
public static FileConfiguration getConfiguration() {
return PLUGIN.getSettings();
}
public static FileConfiguration getMessages() {
return PLUGIN.getMessages();
}
public static FileConfiguration getPlayerData() {
return PLUGIN.getPlayerData();
}
public static void reloadConfig() {
PLUGIN.reload();
}
public static SuperVanish getPlugin() {
return PLUGIN;
}
public static void setPlugin(SuperVanish plugin) {
VanishAPI.PLUGIN = plugin;
}
}
@@ -18,48 +18,64 @@ public class LayeredPermissionChecker {
private final FileConfiguration settings;
public LayeredPermissionChecker(SuperVanish plugin) {
this.plugin = plugin;
settings = plugin.getSettings();
}
/**
* @return TRUE if sender has *permission* to use /sv on, else FALSE; TRUE doesn't mean that sender can
* actually use /sv on
* @return TRUE if sender has *permission* to use /sv on, else FALSE; TRUE
* doesn't mean that sender can actually use /sv on
*/
public boolean hasPermissionToVanish(CommandSender sender) {
if (settings.getBoolean(
"IndicationFeatures.LayeredPermissions.LayeredSeeAndUsePermissions", false)) {
if (sender.hasPermission("sv.use")) return true;
if (settings.getBoolean("IndicationFeatures.LayeredPermissions.LayeredSeeAndUsePermissions", false)) {
if (sender.hasPermission("sv.use"))
return true;
int permissionLevel;
if (sender instanceof Player)
permissionLevel = plugin.getVanishPlayer((Player) sender).getUsePermissionLevel();
else permissionLevel = getLayeredPermissionLevel(sender, "use");
else
permissionLevel = getLayeredPermissionLevel(sender, "use");
return permissionLevel > 0 && sender.hasPermission("sv.use.level" + permissionLevel);
} else return sender.hasPermission("sv.use");
} else
return sender.hasPermission("sv.use");
}
public boolean hasPermissionToSee(Player viewer, Player viewed) {
if (viewer == null)
throw new IllegalArgumentException("viewer cannot be null");
if (viewer == viewed) return true;
if (settings.getBoolean(
"IndicationFeatures.LayeredPermissions.LayeredSeeAndUsePermissions", false)) {
if (viewer == viewed)
return true;
if (settings.getBoolean("IndicationFeatures.LayeredPermissions.LayeredSeeAndUsePermissions", false)) {
VanishPlayer vanishViewer = plugin.getVanishPlayer(viewer);
VanishPlayer vanishViewed = plugin.getVanishPlayer(viewed);
int viewerLevel = vanishViewer.getSeePermissionLevel();
if (viewerLevel == 0) return false;
if (viewerLevel == 0)
return false;
int viewedLevel = Math.max(1, vanishViewed.getUsePermissionLevel());
return viewerLevel >= viewedLevel;
} else {
boolean enableSeePermission = settings
.getBoolean("IndicationFeatures.LayeredPermissions.EnableSeePermission", true);
return enableSeePermission && viewer.hasPermission("sv.see");
}
}
public int getLayeredPermissionLevel(CommandSender sender, String permission) {
boolean enableSeePermission = settings
.getBoolean("IndicationFeatures.LayeredPermissions.EnableSeePermission", true);
boolean enableSeePermission = settings.getBoolean("IndicationFeatures.LayeredPermissions.EnableSeePermission",
true);
if (!enableSeePermission && permission.equalsIgnoreCase("see"))
return 0;
int maxLevel = settings.getInt("IndicationFeatures.LayeredPermissions.MaxLevel", 100);
@@ -68,5 +84,7 @@ public class LayeredPermissionChecker {
if (sender.hasPermission("sv." + permission + ".level" + i))
level = i;
return level;
}
}
@@ -8,169 +8,297 @@
package de.myzelyam.supervanish;
import com.earth2me.essentials.Essentials;
import com.earth2me.essentials.User;
import de.myzelyam.supervanish.hooks.PlaceholderAPIHook;
import de.myzelyam.supervanish.utils.Validation;
import net.milkbowl.vault.chat.Chat;
import net.milkbowl.vault.permission.Permission;
import java.util.ArrayList;
import java.util.List;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.RegisteredServiceProvider;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.earth2me.essentials.Essentials;
import com.earth2me.essentials.User;
import be.maximvdw.placeholderapi.PlaceholderAPI;
import de.myzelyam.supervanish.utils.Validation;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import net.luckperms.api.LuckPerms;
import net.luckperms.api.LuckPermsProvider;
import net.luckperms.api.cacheddata.CachedMetaData;
import net.trueog.utilitiesog.UtilitiesOG;
public class PlaceholderConverter {
private final SuperVanish plugin;
private static final LegacyComponentSerializer LEGACY_SERIALIZER = LegacyComponentSerializer.legacySection();
public PlaceholderConverter(SuperVanish plugin) {
this.plugin = plugin;
}
public String replacePlaceholders(String msg, Object... additionalPlayerInfo) {
Validation.checkIsTrue("Failed to replace variables (Illegal arguments)",
msg != null, additionalPlayerInfo != null);
//noinspection ConstantConditions
Validation.checkIsTrue(additionalPlayerInfo.length > 0);
// check vararg
final List<Object> additionalPlayerInfoList = Arrays
.asList(additionalPlayerInfo);
Object unspecifiedPlayer = additionalPlayerInfoList.get(0);
String unspecifiedOtherPlayersName = null;
if (additionalPlayerInfoList.size() > 1
&& (additionalPlayerInfoList.get(1) instanceof String
|| additionalPlayerInfoList.get(1) instanceof Player)) {
unspecifiedOtherPlayersName = (String) (additionalPlayerInfoList
.get(1) instanceof Player
? ((Player) additionalPlayerInfoList.get(1)).getName()
: additionalPlayerInfoList.get(1));
return LEGACY_SERIALIZER.serialize(expandPlaceholders(msg, additionalPlayerInfo));
}
//noinspection ConstantConditions
msg = msg.replace("\\n", "\n");
// replace sender specific variables
replaceVariables:
{
if (unspecifiedPlayer instanceof OfflinePlayer
&& !(unspecifiedPlayer instanceof Player)) {
// offline player
OfflinePlayer specifiedPlayer = (OfflinePlayer) unspecifiedPlayer;
// MVdWPlaceholderAPI
if (Bukkit.getPluginManager().isPluginEnabled("MVdWPlaceholderAPI")
&& plugin.getSettings().getBoolean("HookOptions.EnableMVdWPlaceholderAPIHook", true)) {
String replaced = PlaceholderAPI.replacePlaceholders(specifiedPlayer, msg);
msg = replaced == null ? msg : replaced;
public Component expandPlaceholders(String msg, Object... additionalPlayerInfo) {
return expandPlaceholders(null, msg, additionalPlayerInfo);
}
// replace essentials nick names
if (Bukkit.getPluginManager()
.getPlugin("Essentials") != null) {
msg = msg.replace("%nick%", specifiedPlayer.getName());
public Component expandPlaceholders(CommandSender recipient, String msg, Object... additionalPlayerInfo) {
Validation.checkNotNull("Failed to replace variables (Illegal arguments)", msg, additionalPlayerInfo);
final PlaceholderContext context = parseContext(additionalPlayerInfo);
final String preparedMessage = applyInlinePlaceholders(msg.replace("\\n", "\n"), context);
if (recipient instanceof Player player) {
if (context.primaryPlayer != null) {
return UtilitiesOG.trueogExpand(preparedMessage, player, context.primaryPlayer);
}
// replace general variables
msg = msg.replace("%d%", specifiedPlayer.getName())
.replace("%p%", specifiedPlayer.getName())
.replace("%tab%", specifiedPlayer.getName());
// replace other player's name if possible
msg = msg.replace("%other%", unspecifiedOtherPlayersName != null
? unspecifiedOtherPlayersName : "UNKNOWN");
break replaceVariables;
return UtilitiesOG.trueogExpand(preparedMessage, player);
}
if (unspecifiedPlayer instanceof Player) {
// player
Player specifiedPlayer = (Player) unspecifiedPlayer;
// PlaceholderAPI
if (Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")
&& plugin.getSettings().getBoolean("HookOptions.EnablePlaceholderAPIHook", true)) {
String replaced = PlaceholderAPIHook.translatePlaceholders(msg, specifiedPlayer);
//noinspection ConstantConditions
msg = replaced == null ? msg : replaced;
if (context.primaryPlayer != null) {
return UtilitiesOG.trueogExpand(preparedMessage, context.primaryPlayer);
}
// MVdWPlaceholderAPI
if (Bukkit.getPluginManager().isPluginEnabled("MVdWPlaceholderAPI")
&& plugin.getSettings().getBoolean("HookOptions.EnableMVdWPlaceholderAPIHook", true)) {
String replaced = PlaceholderAPI.replacePlaceholders(specifiedPlayer, msg);
msg = replaced == null ? msg : replaced;
return UtilitiesOG.trueogExpand(preparedMessage);
}
// replace essentials nick names
if (Bukkit.getPluginManager().getPlugin("Essentials") != null) {
Essentials ess = (Essentials) Bukkit.getServer()
.getPluginManager().getPlugin("Essentials");
User u = ess.getUser(specifiedPlayer);
if (u != null)
if (u.getNickname() != null)
msg = msg.replace("%nick%", u.getNickname());
public static MessagePlaceholder placeholder(String name, Object value) {
return new MessagePlaceholder(name, value == null ? "" : String.valueOf(value));
}
// replace vault info
if (Bukkit.getPluginManager().getPlugin("Vault") != null) {
RegisteredServiceProvider<Permission> permService = plugin.getServer()
.getServicesManager().getRegistration(Permission.class);
RegisteredServiceProvider<Chat> chatService = plugin.getServer()
.getServicesManager().getRegistration(Chat.class);
Permission permAPI = permService != null ? permService.getProvider() : null;
Chat chatAPI = chatService != null ? chatService.getProvider() : null;
private PlaceholderContext parseContext(Object... additionalPlayerInfo) {
final PlaceholderContext context = new PlaceholderContext();
if (additionalPlayerInfo == null) {
return context;
}
for (Object entry : additionalPlayerInfo) {
if (entry instanceof MessagePlaceholder messagePlaceholder) {
context.inlinePlaceholders.add(messagePlaceholder);
continue;
}
if (context.primaryObject == null) {
context.primaryObject = entry;
if (entry instanceof Player player) {
context.primaryPlayer = player;
}
continue;
}
if (context.otherName == null && (entry instanceof Player || entry instanceof String)) {
if (entry instanceof Player player) {
context.otherName = player.getName();
} else {
context.otherName = String.valueOf(entry);
}
}
}
return context;
}
private String applyInlinePlaceholders(String message, PlaceholderContext context) {
message = replaceTag(message, "sv_player_name", getPrimaryName(context.primaryObject));
message = replaceTag(message, "sv_player_display_name", getPrimaryDisplayName(context.primaryObject));
message = replaceTag(message, "sv_player_tab_name", getPrimaryTabName(context.primaryObject));
message = replaceTag(message, "sv_player_prefix", getPrimaryPrefix(context.primaryObject));
message = replaceTag(message, "sv_player_suffix", getPrimarySuffix(context.primaryObject));
message = replaceTag(message, "sv_player_group", getPrimaryGroup(context.primaryObject));
message = replaceTag(message, "sv_player_nick", getPrimaryNick(context.primaryObject));
message = replaceTag(message, "sv_other_name", context.otherName != null ? context.otherName : "UNKNOWN");
for (MessagePlaceholder inlinePlaceholder : context.inlinePlaceholders) {
message = replaceTag(message, inlinePlaceholder.name(), inlinePlaceholder.value());
}
return message;
}
private String getPrimaryName(Object primaryObject) {
if (primaryObject instanceof OfflinePlayer offlinePlayer) {
return offlinePlayer.getName();
}
if (primaryObject instanceof CommandSender) {
return "Console";
}
return "";
}
private String getPrimaryDisplayName(Object primaryObject) {
if (primaryObject instanceof Player player) {
return LEGACY_SERIALIZER.serialize(player.displayName());
}
return getPrimaryName(primaryObject);
}
private String getPrimaryTabName(Object primaryObject) {
if (primaryObject instanceof Player player) {
return LEGACY_SERIALIZER.serialize(player.playerListName());
}
return getPrimaryName(primaryObject);
}
private String getPrimaryNick(Object primaryObject) {
if (!(primaryObject instanceof Player player) || Bukkit.getPluginManager().getPlugin("Essentials") == null) {
return getPrimaryName(primaryObject);
}
final Essentials essentials = (Essentials) Bukkit.getServer().getPluginManager().getPlugin("Essentials");
final User user = essentials.getUser(player);
if (user == null || user.getNickname() == null) {
return player.getName();
}
return user.getNickname();
}
private String getPrimaryGroup(Object primaryObject) {
if (!(primaryObject instanceof OfflinePlayer offlinePlayer)) {
return "";
}
if (Bukkit.getPluginManager().getPlugin("LuckPerms") == null) {
return "";
}
try {
if (permAPI != null) {
String group = permAPI.getPrimaryGroup(specifiedPlayer);
if (group != null)
msg = msg.replace("%group%", group);
}
if (chatAPI != null) {
String prefix = chatAPI.getPlayerPrefix(specifiedPlayer);
String suffix = chatAPI.getPlayerSuffix(specifiedPlayer);
if (prefix != null) {
msg = msg.replace("%prefix%", prefix);
}
if (suffix != null) {
msg = msg.replace("%suffix%", suffix);
}
}
} catch (UnsupportedOperationException ignored) {
}
}
// replace general variables
msg = msg.replace("%d%", "" + specifiedPlayer.getDisplayName())
.replace("%p%", "" + specifiedPlayer.getName())
.replace("%tab%",
"" + specifiedPlayer.getPlayerListName());
// replace other player's name if possible
msg = msg.replace("%other%", unspecifiedOtherPlayersName != null
? unspecifiedOtherPlayersName : "UNKNOWN");
break replaceVariables;
}
if (unspecifiedPlayer instanceof CommandSender) {
// console
// replace general variables
msg = msg.replace("%d%", "Console").replace("%p%", "Console")
.replace("%tab%", "Console");
// replace other player's name if possible
msg = msg.replace("%other%", unspecifiedOtherPlayersName != null
? unspecifiedOtherPlayersName : "UNKNOWN");
}
}
// convert color codes
if (plugin.getVersionUtil().isOneDotXOrHigher(16)) {
Pattern pattern = Pattern.compile("\\{?&?#[a-fA-F0-9]{6}\\}?");
Matcher matcher = pattern.matcher(msg);
final LuckPerms luckPerms = LuckPermsProvider.get();
final net.luckperms.api.model.user.User user = luckPerms.getUserManager()
.getUser(offlinePlayer.getUniqueId());
return user == null ? "" : user.getPrimaryGroup();
} catch (IllegalStateException ignored) {
return "";
while (matcher.find()) {
String color = msg.substring(matcher.start(), matcher.end());
msg = msg.replace(color, net.md_5.bungee.api.ChatColor.of(color.replace("&", "").replace("{", "").replace("}", "")) + "");
matcher = pattern.matcher(msg);
}
}
msg = ChatColor.translateAlternateColorCodes('&', msg);
return msg;
private String getPrimaryPrefix(Object primaryObject) {
final CachedMetaData metaData = getPrimaryMetaData(primaryObject);
return metaData == null || metaData.getPrefix() == null ? "" : metaData.getPrefix();
}
private String getPrimarySuffix(Object primaryObject) {
final CachedMetaData metaData = getPrimaryMetaData(primaryObject);
return metaData == null || metaData.getSuffix() == null ? "" : metaData.getSuffix();
}
private CachedMetaData getPrimaryMetaData(Object primaryObject) {
if (!(primaryObject instanceof OfflinePlayer offlinePlayer)
|| Bukkit.getPluginManager().getPlugin("LuckPerms") == null)
{
return null;
}
try {
final LuckPerms luckPerms = LuckPermsProvider.get();
final net.luckperms.api.model.user.User user = luckPerms.getUserManager()
.getUser(offlinePlayer.getUniqueId());
return user == null ? null : user.getCachedData().getMetaData();
} catch (IllegalStateException ignored) {
return null;
}
}
private String replaceTag(String message, String tagName, String value) {
return message.replace("<" + tagName + ">", value == null ? "" : value);
}
public record MessagePlaceholder(String name, String value) {
}
private static final class PlaceholderContext {
private Object primaryObject;
private Player primaryPlayer;
private String otherName;
private final List<MessagePlaceholder> inlinePlaceholders = new ArrayList<>();
}
}
@@ -8,22 +8,12 @@
package de.myzelyam.supervanish;
import com.comphenix.protocol.ProtocolLibrary;
import de.myzelyam.api.vanish.VanishAPI;
import de.myzelyam.supervanish.commands.VanishCommand;
import de.myzelyam.supervanish.config.ConfigMgr;
import de.myzelyam.supervanish.listeners.*;
import de.myzelyam.supervanish.features.FeatureMgr;
import de.myzelyam.supervanish.hooks.PluginHookMgr;
import de.myzelyam.supervanish.net.UpdateNotifier;
import de.myzelyam.supervanish.utils.ExceptionLogger;
import de.myzelyam.supervanish.utils.VersionUtil;
import de.myzelyam.supervanish.visibility.ActionBarMgr;
import de.myzelyam.supervanish.visibility.FileVanishStateMgr;
import de.myzelyam.supervanish.visibility.ServerListPacketListener;
import de.myzelyam.supervanish.visibility.VisibilityChanger;
import de.myzelyam.supervanish.visibility.hiders.PreventionHider;
import lombok.Getter;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import org.apache.commons.lang3.StringUtils;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
@@ -37,58 +27,70 @@ import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import com.comphenix.protocol.ProtocolLibrary;
import de.myzelyam.api.vanish.VanishAPI;
import de.myzelyam.supervanish.commands.VanishCommand;
import de.myzelyam.supervanish.config.ConfigMgr;
import de.myzelyam.supervanish.features.FeatureMgr;
import de.myzelyam.supervanish.hooks.PluginHookMgr;
import de.myzelyam.supervanish.listeners.GeneralListener;
import de.myzelyam.supervanish.listeners.JoinListener;
import de.myzelyam.supervanish.listeners.LoginListener;
import de.myzelyam.supervanish.listeners.PlayerBlockModifyListener;
import de.myzelyam.supervanish.listeners.QuitListener;
import de.myzelyam.supervanish.listeners.TabCompleteListener;
import de.myzelyam.supervanish.listeners.WorldChangeListener;
import de.myzelyam.supervanish.utils.ExceptionLogger;
import de.myzelyam.supervanish.utils.VersionUtil;
import de.myzelyam.supervanish.visibility.ActionBarMgr;
import de.myzelyam.supervanish.visibility.FileVanishStateMgr;
import de.myzelyam.supervanish.visibility.ServerListPacketListener;
import de.myzelyam.supervanish.visibility.VisibilityChanger;
import de.myzelyam.supervanish.visibility.hiders.PreventionHider;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import net.trueog.utilitiesog.UtilitiesOG;
public class SuperVanish extends JavaPlugin implements SuperVanishPlugin {
public static final String[] NON_REQUIRED_SETTINGS_UPDATES = {"6.0.0", "6.0.1", "6.0.2", "6.0.3",
"6.0.4", "6.0.5", "6.1.0", "6.1.1", "6.1.2", "6.1.3", "6.1.4", "6.1.5", "6.1.6", "6.1.7",
"6.1.8", "6.2.0", "6.2.1", "6.2.2", "6.2.3", "6.2.4", "6.2.5", "6.2.6", "6.2.7", "6.2.8",
"6.2.9", "6.2.10", "6.2.11", "6.2.12", "6.2.13", "6.2.14", "6.2.15", "6.2.16", "6.2.17",
"6.2.18", "6.2.19", "6.2.20"},
NON_REQUIRED_MESSAGES_UPDATES = {"6.0.0", "6.0.1", "6.0.2", "6.0.3", "6.0.4", "6.0.5", "6.1.0",
"6.1.1", "6.1.2", "6.1.3", "6.1.4", "6.1.5", "6.1.6", "6.1.7", "6.1.8", "6.2.0", "6.2.1",
"6.2.2", "6.2.3", "6.2.4", "6.2.5", "6.2.6", "6.2.7", "6.2.8", "6.2.9", "6.2.10", "6.2.11",
"6.2.12", "6.2.13", "6.2.14", "6.2.15", "6.2.16", "6.2.17", "6.2.18", "6.2.19", "6.2.20"};
public static final String[] NON_REQUIRED_SETTINGS_UPDATES = { "6.0.0", "6.0.1", "6.0.2", "6.0.3", "6.0.4", "6.0.5",
"6.1.0", "6.1.1", "6.1.2", "6.1.3", "6.1.4", "6.1.5", "6.1.6", "6.1.7", "6.1.8", "6.2.0", "6.2.1", "6.2.2",
"6.2.3", "6.2.4", "6.2.5", "6.2.6", "6.2.7", "6.2.8", "6.2.9", "6.2.10", "6.2.11", "6.2.12", "6.2.13",
"6.2.14", "6.2.15", "6.2.16", "6.2.17", "6.2.18", "6.2.19", "6.2.20" };
public static final String[] NON_REQUIRED_MESSAGES_UPDATES = { "6.0.0", "6.0.1", "6.0.2", "6.0.3", "6.0.4", "6.0.5",
"6.1.0", "6.1.1", "6.1.2", "6.1.3", "6.1.4", "6.1.5", "6.1.6", "6.1.7", "6.1.8", "6.2.0", "6.2.1", "6.2.2",
"6.2.3", "6.2.4", "6.2.5", "6.2.6", "6.2.7", "6.2.8", "6.2.9", "6.2.10", "6.2.11", "6.2.12", "6.2.13",
"6.2.14", "6.2.15", "6.2.16", "6.2.17", "6.2.18", "6.2.19", "6.2.20" };
@Getter
private boolean useProtocolLib;
@Getter
private ActionBarMgr actionBarMgr;
@Getter
private FileVanishStateMgr vanishStateMgr;
@Getter
private VersionUtil versionUtil;
@Getter
private ConfigMgr configMgr;
@Getter
private FeatureMgr featureMgr;
@Getter
private PlaceholderConverter placeholderConverter;
@Getter
private VanishCommand command;
@Getter
private VisibilityChanger visibilityChanger;
@Getter
private UpdateNotifier updateNotifier;
@Getter
private LoginListener loginListener;
@Getter
private LayeredPermissionChecker layeredPermissionChecker;
@Getter
private PluginHookMgr pluginHookMgr;
private Set<VanishPlayer> vanishPlayers = new HashSet<>();
private final Set<VanishPlayer> vanishPlayers = new HashSet<>();
@Override
public void onEnable() {
try {
useProtocolLib = getServer().getPluginManager().isPluginEnabled("ProtocolLib");
if (!useProtocolLib) log(Level.INFO,
"Please install ProtocolLib to be able to use all SuperVanish features: " +
"https://www.spigotmc.org/resources/protocollib.1997/");
if (!useProtocolLib) {
log(Level.INFO, "Please install ProtocolLib to be able to use all SuperVanish features: "
+ "https://www.spigotmc.org/resources/protocollib.1997/");
}
configMgr = new ConfigMgr(this);
configMgr.prepareFiles();
placeholderConverter = new PlaceholderConverter(this);
@@ -96,204 +98,418 @@ public class SuperVanish extends JavaPlugin implements SuperVanishPlugin {
command = new VanishCommand(this);
versionUtil = new VersionUtil(this);
vanishStateMgr = new FileVanishStateMgr(this);
if (getSettings().getBoolean("MiscellaneousOptions.UpdateChecker.Enable", true))
updateNotifier = new UpdateNotifier(this);
visibilityChanger = new VisibilityChanger(new PreventionHider(this), this);
if (versionUtil.isOneDotXOrHigher(8) && useProtocolLib)
if (versionUtil.isOneDotXOrHigher(8) && useProtocolLib) {
actionBarMgr = new ActionBarMgr(this);
if (useProtocolLib && ServerListPacketListener.isEnabled(this))
}
if (useProtocolLib && ServerListPacketListener.isEnabled(this)) {
ServerListPacketListener.register(this);
}
registerEvents();
pluginHookMgr = new PluginHookMgr(this);
featureMgr = new FeatureMgr(this);
featureMgr.enableFeatures();
if (!Bukkit.getOnlinePlayers().isEmpty())
if (!Bukkit.getOnlinePlayers().isEmpty()) {
onReload();
} catch (Exception e) {
logException(e);
}
} catch (Exception e) {
logException(e);
}
try {
VanishAPI.setPlugin(this);
} catch (NoSuchMethodError ignored) {
// API already loaded by other plugin
}
}
@Override
public void onDisable() {
try {
if (featureMgr != null) featureMgr.disableFeatures();
if (featureMgr != null) {
featureMgr.disableFeatures();
}
vanishPlayers.clear();
VanishAPI.setPlugin(null);
} catch (Throwable e) {
if (e instanceof ThreadDeath || e instanceof VirtualMachineError) throw e;
if (e instanceof ThreadDeath || e instanceof VirtualMachineError) {
throw e;
}
if (!(e instanceof NoClassDefFoundError | e instanceof NoSuchMethodError)) {
e.printStackTrace();
}
}
}
private void onReload() {
for (Player player : Bukkit.getOnlinePlayers()) {
boolean itemPickUps = getPlayerData().getBoolean(
Bukkit.getOnlinePlayers().forEach((Player player) -> {
final boolean itemPickUps = getPlayerData().getBoolean(
"PlayerData." + player.getUniqueId() + ".itemPickUps",
getSettings().getBoolean("InvisibilityFeatures.DefaultPickUpItemsOption"));
boolean vanished = vanishStateMgr.isVanished(player.getUniqueId());
final boolean vanished = vanishStateMgr.isVanished(player.getUniqueId());
createVanishPlayer(player, itemPickUps);
if (vanished) {
for (Player onlinePlayer : Bukkit.getOnlinePlayers())
if (!hasPermissionToSee(onlinePlayer, player))
Bukkit.getOnlinePlayers().forEach((Player onlinePlayer) -> {
if (!hasPermissionToSee(onlinePlayer, player)) {
visibilityChanger.getHider().setHidden(player, onlinePlayer, true);
}
if (getSettings().getBoolean("MessageOptions.DisplayActionBar")
&& vanished && actionBarMgr != null) {
});
}
if (getSettings().getBoolean("MessageOptions.DisplayActionBar") && vanished && actionBarMgr != null) {
actionBarMgr.addActionBar(player);
}
}
});
}
public void reload() {
getServer().getScheduler().cancelTasks(this);
HandlerList.unregisterAll(this);
if (useProtocolLib)
if (useProtocolLib) {
ProtocolLibrary.getProtocolManager().removePacketListeners(this);
}
onDisable();
onEnable();
}
@Override
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
return this.command.tabComplete(command, sender, alias, args);
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
this.command.execute(command, sender, label, args);
return true;
}
private void registerEvents() {
PluginManager pluginManager = getServer().getPluginManager();
final PluginManager pluginManager = getServer().getPluginManager();
pluginManager.registerEvents(new GeneralListener(this), this);
pluginManager.registerEvents(new PlayerBlockModifyListener(this), this);
pluginManager.registerEvents(new WorldChangeListener(this), this);
if (versionUtil.isOneDotXOrHigher(10)) {
pluginManager.registerEvents(new TabCompleteListener(this), this);
}
pluginManager.registerEvents(loginListener = new LoginListener(this), this);
JoinListener joinListener = new JoinListener(this);
pluginManager.registerEvent(PlayerJoinEvent.class, joinListener,
getEventPriority(PlayerJoinEvent.class), joinListener, this, false);
QuitListener quitListener = new QuitListener(this);
pluginManager.registerEvent(PlayerQuitEvent.class, quitListener,
getEventPriority(PlayerQuitEvent.class), quitListener, this, false);
final JoinListener joinListener = new JoinListener(this);
pluginManager.registerEvent(PlayerJoinEvent.class, joinListener, getEventPriority(PlayerJoinEvent.class),
joinListener, this, false);
final QuitListener quitListener = new QuitListener(this);
pluginManager.registerEvent(PlayerQuitEvent.class, quitListener, getEventPriority(PlayerQuitEvent.class),
quitListener, this, false);
}
private EventPriority getEventPriority(Class<? extends Event> eventClass) {
try {
String eventName = eventClass.getSimpleName();
String configString = getSettings().getString("CompatibilityOptions." + eventName + "Priority");
if (configString == null) return EventPriority.NORMAL;
EventPriority priority = EventPriority.valueOf(configString);
return priority;
final String eventName = eventClass.getSimpleName();
final String configString = getSettings().getString("CompatibilityOptions." + eventName + "Priority");
if (configString == null) {
return EventPriority.NORMAL;
}
return EventPriority.valueOf(configString);
} catch (Exception e) {
logException(e);
return EventPriority.NORMAL;
}
}
public String replacePlaceholders(String msg, Object... additionalPlayerInfo) {
return placeholderConverter.replacePlaceholders(msg, additionalPlayerInfo);
}
public Component expandPlaceholders(CommandSender recipient, String msg, Object... additionalPlayerInfo) {
return placeholderConverter.expandPlaceholders(recipient, msg, additionalPlayerInfo);
}
public String getMessage(String path) {
String message = getMessages().getString("Messages." + path);
if (message == null) {
message = configMgr.getMessagesFile().getDefaultConfig().getString("Messages." + path);
}
return message;
}
public VanishPlayer getVanishPlayer(Player player) {
for (VanishPlayer vanishPlayer : vanishPlayers) {
if (vanishPlayer.getPlayerUUID().equals(player.getUniqueId())) {
return vanishPlayer;
}
}
// ensure that there is always a vanish player
boolean itemPickUps = getPlayerData().getBoolean(
"PlayerData." + player.getUniqueId() + ".itemPickUps",
final boolean itemPickUps = getPlayerData().getBoolean("PlayerData." + player.getUniqueId() + ".itemPickUps",
getSettings().getBoolean("InvisibilityFeatures.DefaultPickUpItemsOption"));
final VanishPlayer vanishPlayer = new VanishPlayer(player, this, itemPickUps);
vanishPlayers.add(vanishPlayer);
return vanishPlayer;
}
public void createVanishPlayer(Player player, boolean itemPickUps) {
VanishPlayer vanishPlayer = new VanishPlayer(player, this, itemPickUps);
final VanishPlayer vanishPlayer = new VanishPlayer(player, this, itemPickUps);
vanishPlayers.add(vanishPlayer);
}
public void removeVanishPlayer(VanishPlayer vanishPlayer) {
vanishPlayers.remove(vanishPlayer);
}
public void sendMessage(CommandSender p, String messagesYmlPath, Object... additionalPlayerInfo) {
String message;
if (!messagesYmlPath.contains(" ") && getMessage(messagesYmlPath) != null)
if (!StringUtils.contains(messagesYmlPath, " ") && getMessage(messagesYmlPath) != null) {
message = getMessage(messagesYmlPath);
else message = messagesYmlPath;
if ("".equalsIgnoreCase(message) || "".equalsIgnoreCase(messagesYmlPath))
} else {
message = messagesYmlPath;
}
if (StringUtils.equalsIgnoreCase("", message) || StringUtils.equalsIgnoreCase("", messagesYmlPath)) {
return;
message = replacePlaceholders(message, additionalPlayerInfo);
p.sendMessage(message);
}
if (p instanceof Player player) {
UtilitiesOG.trueogMessage(player, expandPlaceholders(player, message, additionalPlayerInfo));
} else {
p.sendMessage(LegacyComponentSerializer.legacySection()
.serialize(expandPlaceholders(p, message, additionalPlayerInfo)));
}
}
public boolean canSee(Player viewer, Player viewed) {
return !visibilityChanger.getHider().isHidden(viewed, viewer);
}
public boolean hasPermissionToVanish(CommandSender sender) {
return layeredPermissionChecker.hasPermissionToVanish(sender);
}
public boolean hasPermissionToSee(Player viewer, Player viewed) {
return layeredPermissionChecker.hasPermissionToSee(viewer, viewed);
}
public int getLayeredPermissionLevel(CommandSender sender, String permission) {
return layeredPermissionChecker.getLayeredPermissionLevel(sender, permission);
}
public FileConfiguration getSettings() {
return configMgr.getSettings();
}
public FileConfiguration getMessages() {
return configMgr.getMessages();
}
public FileConfiguration getPlayerData() {
return configMgr.getPlayerData();
}
@Override
public FileConfiguration getConfig() {
return getSettings();
}
@Override
public void log(Level level, String msg) {
getLogger().log(level, msg);
}
@Override
public void log(Level level, String msg, Throwable ex) {
getLogger().log(level, msg, ex);
}
@Override
public void logException(Throwable e) {
ExceptionLogger.logException(e, this);
}
public boolean isUseProtocolLib() {
return useProtocolLib;
}
public ActionBarMgr getActionBarMgr() {
return actionBarMgr;
}
@Override
public FileVanishStateMgr getVanishStateMgr() {
return vanishStateMgr;
}
public VersionUtil getVersionUtil() {
return versionUtil;
}
public ConfigMgr getConfigMgr() {
return configMgr;
}
public FeatureMgr getFeatureMgr() {
return featureMgr;
}
public PlaceholderConverter getPlaceholderConverter() {
return placeholderConverter;
}
public VanishCommand getCommand() {
return command;
}
public VisibilityChanger getVisibilityChanger() {
return visibilityChanger;
}
public LoginListener getLoginListener() {
return loginListener;
}
public LayeredPermissionChecker getLayeredPermissionChecker() {
return layeredPermissionChecker;
}
public PluginHookMgr getPluginHookMgr() {
return pluginHookMgr;
}
}
@@ -24,4 +24,5 @@ public interface SuperVanishPlugin {
void logException(Throwable e);
VanishStateMgr getVanishStateMgr();
}
@@ -10,54 +10,88 @@ package de.myzelyam.supervanish;
import org.bukkit.entity.Player;
import lombok.Getter;
import lombok.Setter;
import java.util.Objects;
import java.util.UUID;
/**
* Holds additional information about players in the context of vanishing
*/
public class VanishPlayer {
private final SuperVanish plugin;
@Getter
private final UUID playerUUID;
@Setter
private boolean itemPickUps;
@Getter
private int seePermissionLevel, usePermissionLevel;
VanishPlayer(Player player, SuperVanish plugin, boolean itemPickUps) {
this.plugin = plugin;
this.playerUUID = player.getUniqueId();
this.itemPickUps = itemPickUps;
if (plugin.getSettings().getBoolean("IndicationFeatures.LayeredPermissions.LayeredSeeAndUsePermissions", false)) {
if (plugin.getSettings().getBoolean("IndicationFeatures.LayeredPermissions.LayeredSeeAndUsePermissions",
false))
{
seePermissionLevel = plugin.getLayeredPermissionLevel(player, "see");
usePermissionLevel = plugin.getLayeredPermissionLevel(player, "use");
}
}
public boolean isOnlineVanished() {
return plugin.getVanishStateMgr().isVanished(playerUUID);
}
public boolean hasItemPickUpsEnabled() {
return itemPickUps;
}
public UUID getPlayerUUID() {
return playerUUID;
}
public void setItemPickUps(boolean itemPickUps) {
this.itemPickUps = itemPickUps;
}
public int getSeePermissionLevel() {
return seePermissionLevel;
}
public int getUsePermissionLevel() {
return usePermissionLevel;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
VanishPlayer that = (VanishPlayer) o;
return Objects.equals(playerUUID, that.playerUUID);
}
@Override
public int hashCode() {
return Objects.hash(playerUUID);
}
}
@@ -16,89 +16,68 @@ import org.bukkit.command.CommandSender;
import java.util.ArrayList;
import java.util.List;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public enum CommandAction {
VANISH_SELF(
"sv.use",
false,
"/sv [on|off]",
"Toggles your visibility") {
VANISH_SELF("sv.use", false, "/sv [on|off]", "Toggles your visibility") {
@Override
public boolean checkPermission(CommandSender sender, SuperVanish plugin) {
Validation.checkNotNull(plugin, "plugin cannot be null");
return plugin.hasPermissionToVanish(sender);
}
},
VANISH_OTHER(
"sv.others",
true,
"/sv [on|off] <player>",
"Shows or hides a player"),
VANISHED_LIST(
"sv.list",
true,
"/sv list",
"Shows a list of vanished players"),
BROADCAST_LOGIN(
"sv.login",
false,
"/sv login",
"Broadcasts a fake login message"),
BROADCAST_LOGOUT(
"sv.logout",
false,
"/sv logout",
"Broadcasts a fake logout message"),
TOGGLE_ITEM_PICKUPS(
"sv.toggleitems",
false,
"/sv tipu",
"Toggles picking up items") {
VANISH_OTHER("sv.others", true, "/sv [on|off] <player>", "Shows or hides a player"),
VANISHED_LIST("sv.list", true, "/sv list", "Shows a list of vanished players"),
BROADCAST_LOGIN("sv.login", false, "/sv login", "Broadcasts a fake login message"),
BROADCAST_LOGOUT("sv.logout", false, "/sv logout", "Broadcasts a fake logout message"),
TOGGLE_ITEM_PICKUPS("sv.toggleitems", false, "/sv tipu", "Toggles picking up items")
{
@Override
public boolean checkPermission(CommandSender sender, SuperVanish plugin) {
return sender.hasPermission(getMainPermission()) || sender.hasPermission("sv.toggleitempickups");
}
},
RECREATE_FILES(
"sv.recreatefiles",
true,
"/sv recreatefiles",
"Recreates the config") {
RECREATE_FILES("sv.recreatefiles", true, "/sv recreatefiles", "Recreates the config")
{
@Override
public boolean checkPermission(CommandSender sender, SuperVanish plugin) {
return sender.hasPermission(getMainPermission()) || sender.hasPermission("sv.updatecfg");
}
},
RELOAD(
"sv.reload",
true,
"/sv reload",
"Reloads all settings and messages"),
PRINT_STACKTRACE(
"sv.stacktrace",
true,
"/sv stacktrace",
"Logs info for a bug report"),
SHOW_HELP(
"sv.help",
true,
"/sv help",
"Shows this help page");
RELOAD("sv.reload", true, "/sv reload", "Reloads all settings and messages"),
PRINT_STACKTRACE("sv.stacktrace", true, "/sv stacktrace", "Logs info for a bug report"),
SHOW_HELP("sv.help", true, "/sv help", "Shows this help page");
/**
* Use {@link #checkPermission(CommandSender, SuperVanish)} to check whether a {@link CommandSender} has
* permission to perform an action or not
* Use {@link #checkPermission(CommandSender, SuperVanish)} to check whether a
* {@link CommandSender} has permission to perform an action or not
*/
@Getter
private final String mainPermission;
private final boolean console;
@Getter
private final String usage, description;
CommandAction(String mainPermission, boolean console, String usage, String description) {
this.mainPermission = mainPermission;
this.console = console;
this.usage = usage;
this.description = description;
}
static List<String> getAvailableFirstArguments(CommandSender sender, SuperVanish plugin) {
Validation.checkNotNull(plugin, "plugin cannot be null");
List<String> list = new ArrayList<>();
if (SHOW_HELP.checkPermission(sender, plugin))
@@ -118,20 +97,46 @@ public enum CommandAction {
if (PRINT_STACKTRACE.checkPermission(sender, plugin))
list.add("stacktrace");
return list;
}
public static boolean hasAnyCmdPermission(CommandSender sender, SuperVanish plugin) {
for (CommandAction action : CommandAction.values())
if (action.checkPermission(sender, plugin))
return true;
return false;
}
public boolean checkPermission(CommandSender sender, SuperVanish plugin) {
return sender.hasPermission(getMainPermission());
}
public boolean usableByConsole() {
return console;
}
public String getMainPermission() {
return mainPermission;
}
public String getUsage() {
return usage;
}
public String getDescription() {
return description;
}
}
@@ -14,4 +14,5 @@ import org.bukkit.command.CommandSender;
public interface Executable {
void execute(Command cmd, CommandSender sender, String[] args, String label);
}
@@ -21,42 +21,61 @@ public abstract class SubCommand implements Executable {
protected final SuperVanish plugin;
public SubCommand(SuperVanish plugin) {
this.plugin = plugin;
}
protected Collection<UUID> getAllVanishedPlayers() {
return plugin.getVanishStateMgr().getVanishedPlayers();
}
protected Collection<UUID> getOnlineVanishedPlayers() {
return plugin.getVanishStateMgr().getOnlineVanishedPlayers();
}
public void hidePlayer(Player player) {
plugin.getVisibilityChanger().hidePlayer(player);
}
public void showPlayer(Player player) {
plugin.getVisibilityChanger().showPlayer(player);
}
public boolean isVanished(UUID uuid) {
return plugin.getVanishStateMgr().isVanished(uuid);
}
public boolean canDo(CommandSender sender, CommandAction action, boolean sendErrors) {
if (!(sender instanceof Player))
if (!action.usableByConsole()) {
if (sendErrors)
plugin.sendMessage(sender, "InvalidSender", sender);
return false;
}
if (!action.checkPermission(sender, plugin)) {
if (sendErrors)
plugin.sendMessage(sender, "NoPermission", sender);
return false;
}
return true;
}
}
@@ -25,120 +25,141 @@ public class SubCommandMgr {
private SuperVanish plugin;
public SubCommandMgr(SuperVanish plugin) {
this.plugin = plugin;
}
public void execute(Command cmd, CommandSender sender, String[] args, String label) {
try {
// invalid usage by default
Executable executable = new InvalidUsage(plugin);
// vanish self #1
if (args.length == 0) {
if (sender instanceof Player)
executable = new VanishSelf(plugin);
executable.execute(cmd, sender, args, label);
return;
}
// vanish other
if ((args[0].equalsIgnoreCase("on")
|| args[0].equalsIgnoreCase("off")
|| args[0].equalsIgnoreCase("vanish")
|| args[0].equalsIgnoreCase("reappear")
|| args[0].equalsIgnoreCase("enable")
|| args[0].equalsIgnoreCase("disable"))
&& args.length > 1
|| Bukkit.getPlayer(args[0]) != null) {
executable = Bukkit.getPlayer(args[0]) != null
&& !(args[0].equalsIgnoreCase("on")
|| args[0].equalsIgnoreCase("off")
|| args[0].equalsIgnoreCase("vanish")
|| args[0].equalsIgnoreCase("reappear")
|| args[0].equalsIgnoreCase("enable")
|| args[0].equalsIgnoreCase("disable"))
? new VanishOther(Bukkit.getPlayer(args[0]), plugin)
: new VanishOther(plugin);
}
// vanish self #2
if ((args[0].equalsIgnoreCase("on")
|| args[0].equalsIgnoreCase("off")
|| args[0].equalsIgnoreCase("vanish")
|| args[0].equalsIgnoreCase("reappear")
|| args[0].equalsIgnoreCase("enable")
|| args[0].equalsIgnoreCase("disable")
|| args[0].equalsIgnoreCase("-s"))
&& args.length == 1) {
executable = new VanishSelf(plugin);
}
//
if (args[0].equalsIgnoreCase("help")
|| args[0].equalsIgnoreCase("commands")
|| args[0].equalsIgnoreCase("?")) {
executable = new ShowHelp(plugin);
}
if (args[0].equalsIgnoreCase("printstacktrace")
|| args[0].equalsIgnoreCase("logstacktrace")
|| args[0].equalsIgnoreCase("dumpstacktrace")
|| args[0].equalsIgnoreCase("stacktrace")) {
executable = new PrintStacktrace(plugin);
}
if (args[0].equalsIgnoreCase("toggleitempickups")
|| args[0].equalsIgnoreCase("toggleitempickup")
|| args[0].equalsIgnoreCase("toggleitem")
|| args[0].equalsIgnoreCase("toggleitems")
|| args[0].equalsIgnoreCase("togglepickup")
|| args[0].equalsIgnoreCase("togglepickups")
|| args[0].equalsIgnoreCase("pickup")
|| args[0].equalsIgnoreCase("pickups")
|| args[0].equalsIgnoreCase("tipu")
|| args[0].equalsIgnoreCase("tip")) {
executable = new ToggleItemPickups(plugin);
}
if (args[0].equalsIgnoreCase("recreatecfg")
|| args[0].equalsIgnoreCase("rccfg")
|| args[0].equalsIgnoreCase("recreateconfig")
|| args[0].equalsIgnoreCase("rcconfig")
|| args[0].equalsIgnoreCase("updatecfg")
|| args[0].equalsIgnoreCase("recreate")
|| args[0].equalsIgnoreCase("recreatefiles")
|| args[0].equalsIgnoreCase("rf")
|| args[0].equalsIgnoreCase("rcf")
|| args[0].equalsIgnoreCase("refi")) {
executable = new RecreateFiles(plugin);
}
if (args[0].equalsIgnoreCase("reload")
|| args[0].equalsIgnoreCase("rl")
|| args[0].equalsIgnoreCase("re")
|| args[0].equalsIgnoreCase("rlc")
|| args[0].equalsIgnoreCase("rlcfg")
|| args[0].equalsIgnoreCase("reloadconfig")
|| args[0].equalsIgnoreCase("rlp")
|| args[0].equalsIgnoreCase("rlpl")
|| args[0].equalsIgnoreCase("reloadplugin")) {
executable = new Reload(plugin);
}
if (args[0].equalsIgnoreCase("list")) {
executable = new VanishedList(plugin);
}
if (args[0].equalsIgnoreCase("login")) {
executable = new BroadcastLogin(plugin);
}
if (args[0].equalsIgnoreCase("logout")) {
executable = new BroadcastLogout(plugin);
}
executable.execute(cmd, sender, args, label);
} catch (Exception e) {
plugin.logException(e);
}
}
public List<String> onTabComplete(Command command, CommandSender sender,
String alias, String[] args) {
// vanish other
if ((args[0].equalsIgnoreCase("on") || args[0].equalsIgnoreCase("off") || args[0].equalsIgnoreCase("vanish")
|| args[0].equalsIgnoreCase("reappear") || args[0].equalsIgnoreCase("enable")
|| args[0].equalsIgnoreCase("disable")) && args.length > 1 || Bukkit.getPlayer(args[0]) != null)
{
executable = Bukkit.getPlayer(args[0]) != null
&& !(args[0].equalsIgnoreCase("on") || args[0].equalsIgnoreCase("off")
|| args[0].equalsIgnoreCase("vanish") || args[0].equalsIgnoreCase("reappear")
|| args[0].equalsIgnoreCase("enable") || args[0].equalsIgnoreCase("disable"))
? new VanishOther(Bukkit.getPlayer(args[0]), plugin)
: new VanishOther(plugin);
}
// vanish self #2
if ((args[0].equalsIgnoreCase("on") || args[0].equalsIgnoreCase("off") || args[0].equalsIgnoreCase("vanish")
|| args[0].equalsIgnoreCase("reappear") || args[0].equalsIgnoreCase("enable")
|| args[0].equalsIgnoreCase("disable") || args[0].equalsIgnoreCase("-s")) && args.length == 1)
{
executable = new VanishSelf(plugin);
}
//
if (args[0].equalsIgnoreCase("help") || args[0].equalsIgnoreCase("commands")
|| args[0].equalsIgnoreCase("?"))
{
executable = new ShowHelp(plugin);
}
if (args[0].equalsIgnoreCase("printstacktrace") || args[0].equalsIgnoreCase("logstacktrace")
|| args[0].equalsIgnoreCase("dumpstacktrace") || args[0].equalsIgnoreCase("stacktrace"))
{
executable = new PrintStacktrace(plugin);
}
if (args[0].equalsIgnoreCase("toggleitempickups") || args[0].equalsIgnoreCase("toggleitempickup")
|| args[0].equalsIgnoreCase("toggleitem") || args[0].equalsIgnoreCase("toggleitems")
|| args[0].equalsIgnoreCase("togglepickup") || args[0].equalsIgnoreCase("togglepickups")
|| args[0].equalsIgnoreCase("pickup") || args[0].equalsIgnoreCase("pickups")
|| args[0].equalsIgnoreCase("tipu") || args[0].equalsIgnoreCase("tip"))
{
executable = new ToggleItemPickups(plugin);
}
if (args[0].equalsIgnoreCase("recreatecfg") || args[0].equalsIgnoreCase("rccfg")
|| args[0].equalsIgnoreCase("recreateconfig") || args[0].equalsIgnoreCase("rcconfig")
|| args[0].equalsIgnoreCase("updatecfg") || args[0].equalsIgnoreCase("recreate")
|| args[0].equalsIgnoreCase("recreatefiles") || args[0].equalsIgnoreCase("rf")
|| args[0].equalsIgnoreCase("rcf") || args[0].equalsIgnoreCase("refi"))
{
executable = new RecreateFiles(plugin);
}
if (args[0].equalsIgnoreCase("reload") || args[0].equalsIgnoreCase("rl") || args[0].equalsIgnoreCase("re")
|| args[0].equalsIgnoreCase("rlc") || args[0].equalsIgnoreCase("rlcfg")
|| args[0].equalsIgnoreCase("reloadconfig") || args[0].equalsIgnoreCase("rlp")
|| args[0].equalsIgnoreCase("rlpl") || args[0].equalsIgnoreCase("reloadplugin"))
{
executable = new Reload(plugin);
}
if (args[0].equalsIgnoreCase("list")) {
executable = new VanishedList(plugin);
}
if (args[0].equalsIgnoreCase("login")) {
executable = new BroadcastLogin(plugin);
}
if (args[0].equalsIgnoreCase("logout")) {
executable = new BroadcastLogout(plugin);
}
executable.execute(cmd, sender, args, label);
} catch (Exception e) {
plugin.logException(e);
}
}
public List<String> onTabComplete(Command command, CommandSender sender, String alias, String[] args) {
// don't provide any help at all if user doesn't have permission to
// execute '/sv help'
if (!CommandAction.hasAnyCmdPermission(sender, plugin)) {
return Collections.emptyList();
}
if (args.length == 1) {
String toComplete = args[0];
// didn't start to type first argument
if (toComplete.equalsIgnoreCase(""))
@@ -146,34 +167,48 @@ public class SubCommandMgr {
// started to type it, only show args that start with given argument
List<String> matchingFirstArguments = new ArrayList<>();
for (String arg : CommandAction.getAvailableFirstArguments(sender, plugin)) {
if (arg.toLowerCase(Locale.ENGLISH).startsWith(toComplete.toLowerCase(Locale.ENGLISH)))
matchingFirstArguments.add(arg);
}
if (matchingFirstArguments.isEmpty())
return getOnlinePlayerNameTabCompletions(toComplete);
return matchingFirstArguments;
// extra commands
} else if (args.length == 2) {
// vanish other player tab completion
if ((args[0].equalsIgnoreCase("on")
|| args[0].equalsIgnoreCase("off")
|| args[0].equalsIgnoreCase("vanish")
|| args[0].equalsIgnoreCase("reappear")
|| args[0].equalsIgnoreCase("enable")
if ((args[0].equalsIgnoreCase("on") || args[0].equalsIgnoreCase("off") || args[0].equalsIgnoreCase("vanish")
|| args[0].equalsIgnoreCase("reappear") || args[0].equalsIgnoreCase("enable")
|| args[0].equalsIgnoreCase("disable"))
&& CommandAction.VANISH_OTHER.checkPermission(sender, plugin)) {
&& CommandAction.VANISH_OTHER.checkPermission(sender, plugin))
{
return getOnlinePlayerNameTabCompletions(args[1]);
}
}
return Collections.emptyList();
}
private List<String> getOnlinePlayerNameTabCompletions(String arg) {
List<String> playerNames = new ArrayList<>();
for (Player p : plugin.getServer().getOnlinePlayers()) {
if (p.getName().toLowerCase(Locale.ENGLISH).startsWith(arg.toLowerCase(Locale.ENGLISH)))
playerNames.add(p.getName());
}
return playerNames;
}
}
@@ -8,29 +8,33 @@
package de.myzelyam.supervanish.commands;
import de.myzelyam.supervanish.SuperVanish;
import java.util.List;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import java.util.List;
import de.myzelyam.supervanish.SuperVanish;
public class VanishCommand {
private final SuperVanish plugin;
private final SubCommandMgr subCommandMgr;
public VanishCommand(SuperVanish plugin) {
this.plugin = plugin;
subCommandMgr = new SubCommandMgr(plugin);
}
public void execute(Command command, CommandSender sender, String commandLabel, String[] args) {
subCommandMgr.execute(command, sender, args, commandLabel);
}
public List<String> tabComplete(Command command, CommandSender sender, String alias, String[] args)
throws IllegalArgumentException {
public List<String> tabComplete(Command command, CommandSender sender, String alias, String[] args) {
return subCommandMgr.onTabComplete(command, sender, alias, args);
}
}
@@ -20,14 +20,21 @@ import org.bukkit.entity.Player;
public class BroadcastLogin extends SubCommand {
public BroadcastLogin(SuperVanish plugin) {
super(plugin);
}
@Override
public void execute(Command cmd, CommandSender p, String[] args, String label) {
if (canDo(p, CommandAction.BROADCAST_LOGIN, true)) {
for (Player onlinePlayer : Bukkit.getOnlinePlayers())
plugin.sendMessage(onlinePlayer, "ReappearMessage", p, onlinePlayer);
}
}
}
@@ -20,14 +20,21 @@ import org.bukkit.entity.Player;
public class BroadcastLogout extends SubCommand {
public BroadcastLogout(SuperVanish plugin) {
super(plugin);
}
@Override
public void execute(Command cmd, CommandSender p, String[] args, String label) {
if (canDo(p, CommandAction.BROADCAST_LOGOUT, true)) {
for (Player onlinePlayer : Bukkit.getOnlinePlayers())
plugin.sendMessage(onlinePlayer, "VanishMessage", p, onlinePlayer);
}
}
}
@@ -18,15 +18,23 @@ import org.bukkit.command.CommandSender;
public class InvalidUsage extends SubCommand {
public InvalidUsage(SuperVanish plugin) {
super(plugin);
}
@Override
public void execute(Command cmd, CommandSender sender, String[] args, String label) {
if (!CommandAction.hasAnyCmdPermission(sender, plugin)) {
plugin.sendMessage(sender, "NoPermission", sender);
return;
}
plugin.sendMessage(sender, "InvalidUsage", sender);
}
}
@@ -18,14 +18,21 @@ import org.bukkit.command.CommandSender;
public class PrintStacktrace extends SubCommand {
public PrintStacktrace(SuperVanish plugin) {
super(plugin);
}
@Override
public void execute(Command cmd, CommandSender sender, String[] args, String label) {
if (canDo(sender, CommandAction.PRINT_STACKTRACE, true)) {
plugin.sendMessage(sender, "PrintedStacktrace", sender);
plugin.logException(null);
}
}
}
@@ -8,103 +8,161 @@
package de.myzelyam.supervanish.commands.subcommands;
import de.myzelyam.supervanish.SuperVanish;
import de.myzelyam.supervanish.commands.CommandAction;
import de.myzelyam.supervanish.commands.SubCommand;
import java.io.File;
import org.bukkit.ChatColor;
import org.apache.commons.lang3.StringUtils;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.io.File;
import de.myzelyam.supervanish.PlaceholderConverter;
import de.myzelyam.supervanish.SuperVanish;
import de.myzelyam.supervanish.commands.CommandAction;
import de.myzelyam.supervanish.commands.SubCommand;
import net.trueog.utilitiesog.UtilitiesOG;
public class RecreateFiles extends SubCommand {
public RecreateFiles(SuperVanish plugin) {
super(plugin);
}
@Override
public void execute(Command cmd, CommandSender p, String[] args, String label) {
if (canDo(p, CommandAction.RECREATE_FILES, true)) {
String updates;
if (!canDo(p, CommandAction.RECREATE_FILES, true)) {
return;
}
String files;
String changes;
if (plugin.getConfigMgr().isSettingsUpdateRequired()
&& plugin.getConfigMgr().isMessagesUpdateRequired()) {
updates = "the config and the messages file";
if (plugin.getConfigMgr().isSettingsUpdateRequired() && plugin.getConfigMgr().isMessagesUpdateRequired()) {
files = "the config and the messages file";
changes = "all settings and messages";
} else if (plugin.getConfigMgr().isSettingsUpdateRequired()) {
updates = "the config";
files = "the config";
changes = "all settings";
} else {
updates = "the messages file";
files = "the messages file";
changes = "all messages";
}
if (args.length != 2) {
if (!plugin.getConfigMgr().isSettingsUpdateRequired()
&& !plugin.getConfigMgr().isMessagesUpdateRequired()) {
&& !plugin.getConfigMgr().isMessagesUpdateRequired())
{
plugin.sendMessage(p, "NoConfigUpdateAvailable", p);
return;
}
plugin.sendMessage(p, plugin.getMessage("UpdateWarning").replace("%changes%", changes)
.replace("%updates%", updates), p);
plugin.sendMessage(p, plugin.getMessage("UpdateWarning"), p,
PlaceholderConverter.placeholder("changes", changes),
PlaceholderConverter.placeholder("files", files));
return;
}
if (args[1].equalsIgnoreCase("confirm") || args[1].equalsIgnoreCase("force")) {
if (!plugin.getConfigMgr().isSettingsUpdateRequired()
&& !plugin.getConfigMgr().isMessagesUpdateRequired()
&& !args[1].equalsIgnoreCase("force")) {
if (StringUtils.equalsIgnoreCase("confirm", args[1]) || StringUtils.equalsIgnoreCase(args[1], "force")) {
if (!plugin.getConfigMgr().isSettingsUpdateRequired() && !plugin.getConfigMgr().isMessagesUpdateRequired()
&& !StringUtils.equalsIgnoreCase(args[1], "force"))
{
plugin.sendMessage(p, "NoConfigUpdateAvailable", p);
return;
}
if (args[1].equalsIgnoreCase("force")) {
updates = "the config and the messages file";
if (StringUtils.equalsIgnoreCase(args[1], "force")) {
files = "the config and the messages file";
changes = "all settings and messages";
}
// delete necessary files
// Delete necessary files.
boolean success = false;
if (plugin.getConfigMgr().isSettingsUpdateRequired()
|| args[1].equalsIgnoreCase("force")) {
File file = new File(plugin.getDataFolder().getPath() + File.separator + "config.yml");
if (plugin.getConfigMgr().isSettingsUpdateRequired() || StringUtils.equalsIgnoreCase(args[1], "force")) {
final File file = new File(plugin.getDataFolder().getPath() + File.separator + "config.yml");
success = file.delete();
plugin.getConfigMgr().getSettingsFile().save();
plugin.getConfigMgr().getSettingsFile().reload();
plugin.getConfigMgr().setSettings(plugin.getConfigMgr().getSettingsFile().getConfig());
}
if (plugin.getConfigMgr().isMessagesUpdateRequired()
|| args[1].equalsIgnoreCase("force")) {
File file2 = new File(plugin.getDataFolder().getPath() + File.separator + "messages.yml");
if (plugin.getConfigMgr().isMessagesUpdateRequired() || StringUtils.equalsIgnoreCase(args[1], "force")) {
final File file2 = new File(plugin.getDataFolder().getPath() + File.separator + "messages.yml");
success &= file2.delete();
plugin.getConfigMgr().getMessagesFile().save();
plugin.getConfigMgr().getMessagesFile().reload();
plugin.getConfigMgr().setMessages(plugin.getConfigMgr().getMessagesFile().getConfig());
}
if (!success) {
p.sendMessage(ChatColor.RED +
"Cannot update config, failed to delete file. Are the file permissions valid?");
final String failMsg = "&cERROR: Cannot recreate files because one of them could not be deleted. Are the file permissions valid?";
if (p instanceof Player player) {
UtilitiesOG.trueogMessage(player, failMsg);
} else {
p.sendMessage(failMsg);
}
return;
}
plugin.sendMessage(p, plugin.getMessage("RecreatedConfig")
.replace("%changes%", changes)
.replace("%updates%", updates), p);
// update update-information
plugin.sendMessage(p, plugin.getMessage("RecreatedConfig"), p,
PlaceholderConverter.placeholder("changes", changes),
PlaceholderConverter.placeholder("files", files));
// Refresh recreation status after regenerating the files.
plugin.getConfigMgr().checkFilesForLeftOvers();
} else if (args[1].equalsIgnoreCase("dismiss")) {
String currentVersion = plugin.getDescription().getVersion();
boolean isDismissed =
plugin.getPlayerData().getBoolean("PlayerData." + (p instanceof Player ? ((Player) p)
.getUniqueId()
.toString() : "CONSOLE") + ".dismissed." + currentVersion.replace(".", "_"), false);
plugin.getPlayerData().set("PlayerData." + (p instanceof Player ? ((Player) p).getUniqueId().toString()
: "CONSOLE") + ".dismissed." + currentVersion.replace(".", "_"), !isDismissed);
} else if (StringUtils.equalsIgnoreCase("dismiss", args[1])) {
final String currentVersion = plugin.getPluginMeta().getVersion();
final boolean isDismissed = plugin.getPlayerData().getBoolean(
"PlayerData." + (p instanceof Player ? ((Player) p).getUniqueId().toString() : "CONSOLE")
+ ".dismissed." + currentVersion.replace(".", "_"),
false);
plugin.getPlayerData()
.set("PlayerData." + (p instanceof Player ? ((Player) p).getUniqueId().toString() : "CONSOLE")
+ ".dismissed." + currentVersion.replace(".", "_"), !isDismissed);
plugin.getConfigMgr().getPlayerDataFile().save();
if (!isDismissed)
if (!isDismissed) {
plugin.sendMessage(p, "DismissedRecreationWarning", p);
else
} else {
plugin.sendMessage(p, "UndismissedRecreationWarning", p);
}
}
}
}
@@ -8,35 +8,57 @@
package de.myzelyam.supervanish.commands.subcommands;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import de.myzelyam.supervanish.PlaceholderConverter;
import de.myzelyam.supervanish.SuperVanish;
import de.myzelyam.supervanish.commands.CommandAction;
import de.myzelyam.supervanish.commands.SubCommand;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import net.trueog.utilitiesog.UtilitiesOG;
public class Reload extends SubCommand {
public Reload(SuperVanish plugin) {
super(plugin);
}
@Override
public void execute(Command cmd, CommandSender p, String[] args, String label) {
if (canDo(p, CommandAction.RELOAD, true)) {
long before = System.currentTimeMillis();
if (!canDo(p, CommandAction.RELOAD, true)) {
return;
}
final long before = System.currentTimeMillis();
plugin.reload();
if (!Bukkit.getPluginManager().isPluginEnabled(plugin)) {
p.sendMessage(ChatColor.RED +
"Failed to reload SuperVanish since it failed to restart itself. " +
"More information is in the console. ("
+ (System.currentTimeMillis() - before) + "ms)");
final String failMsg = "&cERROR: Failed to reload SuperVanish since it failed to restart itself. "
+ "More information is in the console. (" + (System.currentTimeMillis() - before) + "ms)";
if (p instanceof Player player) {
UtilitiesOG.trueogMessage(player, failMsg);
} else {
p.sendMessage(failMsg);
}
return;
}
plugin.sendMessage(p, plugin.getMessage("PluginReloaded").replace("%time%",
(System.currentTimeMillis() - before) + ""), p);
}
plugin.sendMessage(p, plugin.getMessage("PluginReloaded"), p,
PlaceholderConverter.placeholder("time", System.currentTimeMillis() - before));
}
}
@@ -8,7 +8,7 @@
package de.myzelyam.supervanish.commands.subcommands;
import de.myzelyam.supervanish.PlaceholderConverter;
import de.myzelyam.supervanish.SuperVanish;
import de.myzelyam.supervanish.commands.CommandAction;
import de.myzelyam.supervanish.commands.SubCommand;
@@ -19,21 +19,32 @@ import org.bukkit.command.CommandSender;
public class ShowHelp extends SubCommand {
public ShowHelp(SuperVanish plugin) {
super(plugin);
}
@Override
public void execute(Command cmd, CommandSender sender, String[] args, String label) {
if (canDo(sender, CommandAction.SHOW_HELP, true)) {
plugin.sendMessage(sender, "HelpHeader", sender);
for (CommandAction action : CommandAction.values()) {
if (canDo(sender, action, false)) {
plugin.sendMessage(sender, plugin.getMessage("HelpFormat")
.replace("%usage%", action.getUsage())
.replace("%description%", action.getDescription())
.replace("%permission%", action.getMainPermission()), sender);
plugin.sendMessage(sender, plugin.getMessage("HelpFormat"), sender,
PlaceholderConverter.placeholder("usage", action.getUsage()),
PlaceholderConverter.placeholder("description", action.getDescription()),
PlaceholderConverter.placeholder("permission", action.getMainPermission()));
}
}
}
}
}
@@ -20,24 +20,32 @@ import org.bukkit.entity.Player;
public class ToggleItemPickups extends SubCommand {
public ToggleItemPickups(SuperVanish plugin) {
super(plugin);
}
@Override
public void execute(Command cmd, CommandSender sender, String[] args, String label) {
if (canDo(sender, CommandAction.TOGGLE_ITEM_PICKUPS, true)) {
Player p = (Player) sender;
plugin.sendMessage(p, plugin.getMessage("ToggledPickingUpItems"
+ (toggleState(plugin.getVanishPlayer(p)) ? "On" : "Off")), p);
plugin.sendMessage(p, plugin
.getMessage("ToggledPickingUpItems" + (toggleState(plugin.getVanishPlayer(p)) ? "On" : "Off")), p);
}
}
private boolean toggleState(VanishPlayer vp) {
boolean hasEnabled = plugin.getPlayerData().getBoolean("PlayerData."
+ vp.getPlayerUUID() + ".itemPickUps");
boolean hasEnabled = plugin.getPlayerData().getBoolean("PlayerData." + vp.getPlayerUUID() + ".itemPickUps");
plugin.getPlayerData().set("PlayerData." + vp.getPlayerUUID() + ".itemPickUps", !hasEnabled);
vp.setItemPickUps(!hasEnabled);
plugin.getConfigMgr().getPlayerDataFile().save();
return !hasEnabled;
}
}
@@ -24,82 +24,126 @@ public class VanishOther extends SubCommand {
private Player specifiedPlayer;
public VanishOther(SuperVanish plugin) {
this(null, plugin);
}
public VanishOther(Player specifiedPlayer, SuperVanish plugin) {
super(plugin);
this.specifiedPlayer = specifiedPlayer;
}
@Override
public void execute(Command cmd, CommandSender sender, String[] args, String label) {
if (canDo(sender, CommandAction.VANISH_OTHER, true)) {
boolean hide = false, offline = false, silent = false;
Player target;
String name;
UUID uuid;
if (specifiedPlayer == null) {
if (args[0].equalsIgnoreCase("on") || args[0].equalsIgnoreCase("enable")
|| args[0].equalsIgnoreCase("vanish"))
hide = true;
target = Bukkit.getPlayer(args[1]);
name = target == null ? args[1] : target.getName();
} else {
target = specifiedPlayer;
name = specifiedPlayer.getName();
hide = !isVanished(target.getUniqueId());
}
if (target == null) {
offline = true;
uuid = plugin.getVanishStateMgr().getVanishedUUIDFromNameOnFile(name);
if (uuid == null) {
plugin.sendMessage(sender, "PlayerNonExistent", sender, name);
return;
}
} else {
name = target.getName();
uuid = target.getUniqueId();
}
if (!offline && sender instanceof Player && sender != target
&& target.hasPermission("sv.notoggle")) {
if (!offline && sender instanceof Player && sender != target && target.hasPermission("sv.notoggle")) {
plugin.sendMessage(sender, "CannotHideOtherPlayer", sender, name);
return;
}
if (plugin.getSettings().getBoolean(
"IndicationFeatures.LayeredPermissions.HideInvisibleInCommands", false) && target != null
&& sender instanceof Player && !plugin.hasPermissionToSee((Player) sender, target)) {
if (plugin.getSettings().getBoolean("IndicationFeatures.LayeredPermissions.HideInvisibleInCommands", false)
&& target != null && sender instanceof Player
&& !plugin.hasPermissionToSee((Player) sender, target))
{
plugin.sendMessage(sender, "PlayerNonExistent", sender, name);
return;
}
if (hide && (offline ? isVanished(uuid) : isVanished(uuid))) {
plugin.sendMessage(sender, "AlreadyInvisibleMessage", sender, name);
return;
} else if (!hide && !(offline ? isVanished(uuid) : isVanished(uuid))) {
plugin.sendMessage(sender, "AlreadyVisibleMessage", sender, name);
return;
}
if (args.length == 3)
silent = args[2].equalsIgnoreCase("-s");
else if (args.length == 2)
silent = args[1].equalsIgnoreCase("-s");
if (!offline) {
if (hide) {
plugin.getVisibilityChanger().hidePlayer(target, sender.getName(), silent);
plugin.sendMessage(sender, "HideOtherMessage", sender, name);
} else {
plugin.getVisibilityChanger().showPlayer(target, sender.getName());
plugin.sendMessage(sender, "ShowOtherMessage", sender, name, silent);
}
} else {
if (hide) {
plugin.getVanishStateMgr().setVanishedState(uuid, name, true, sender.getName());
plugin.sendMessage(sender, "HideOtherMessage", sender, name);
} else {
plugin.getVanishStateMgr().setVanishedState(uuid, name, false, sender.getName());
plugin.sendMessage(sender, "ShowOtherMessage", sender, name);
}
}
}
}
}
@@ -19,40 +19,63 @@ import org.bukkit.entity.Player;
public class VanishSelf extends SubCommand {
public VanishSelf(SuperVanish plugin) {
super(plugin);
}
@Override
public void execute(Command cmd, CommandSender s, String[] args, String label) {
if (canDo(s, CommandAction.VANISH_SELF, true)) {
Player p = (Player) s;
if (args.length == 0) {
if (isVanished(p.getUniqueId()))
showPlayer(p);
else
hidePlayer(p);
} else if (args.length == 1) {
if (args[0].equalsIgnoreCase("off")
|| args[0].equalsIgnoreCase("reappear")
|| args[0].equalsIgnoreCase("disable")) {
if (args[0].equalsIgnoreCase("off") || args[0].equalsIgnoreCase("reappear")
|| args[0].equalsIgnoreCase("disable"))
{
if (!isVanished(p.getUniqueId())) {
plugin.sendMessage(p, "NotVanishedError", p);
return;
}
showPlayer(p);
} else if (args[0].equalsIgnoreCase("-s")) {
if (isVanished(p.getUniqueId()))
plugin.getVisibilityChanger().showPlayer(p, null, true);
else
plugin.getVisibilityChanger().hidePlayer(p, null, true);
} else {
if (isVanished(p.getUniqueId())) {
plugin.sendMessage(p, "AlreadyVanishedError", p);
return;
}
hidePlayer(p);
}
}
}
}
}
@@ -10,12 +10,14 @@ package de.myzelyam.supervanish.commands.subcommands;
import com.google.common.collect.ImmutableList;
import de.myzelyam.supervanish.PlaceholderConverter;
import de.myzelyam.supervanish.SuperVanish;
import de.myzelyam.supervanish.commands.CommandAction;
import de.myzelyam.supervanish.commands.SubCommand;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
@@ -25,31 +27,53 @@ import java.util.UUID;
public class VanishedList extends SubCommand {
public VanishedList(SuperVanish plugin) {
super(plugin);
}
@Override
public void execute(Command cmd, final CommandSender sender, final String[] args, String label) {
if (canDo(sender, CommandAction.VANISHED_LIST, true)) {
String listMessage = plugin.getMessage("ListMessagePrefix");
StringBuilder stringBuilder = new StringBuilder();
List<UUID> allInvisiblePlayerUUIDs = ImmutableList.copyOf(getAllVanishedPlayers());
if (allInvisiblePlayerUUIDs.isEmpty()) {
stringBuilder.append("none");
if (!canDo(sender, CommandAction.VANISHED_LIST, true)) {
return;
}
String listMessage = plugin.getMessage("ListMessagePrefix");
final StringBuilder stringBuilder = new StringBuilder();
final List<UUID> allInvisiblePlayerUUIDs = ImmutableList.copyOf(getAllVanishedPlayers());
if (allInvisiblePlayerUUIDs.isEmpty()) {
stringBuilder.append("none");
}
for (int i = 0; i < allInvisiblePlayerUUIDs.size(); i++) {
UUID playerUUID = allInvisiblePlayerUUIDs.get(i);
final UUID playerUUID = allInvisiblePlayerUUIDs.get(i);
String name = Bukkit.getOfflinePlayer(playerUUID).getName();
if (Bukkit.getPlayer(playerUUID) == null) {
name = name + ChatColor.RED + "[offline]" + ChatColor.WHITE;
name = LegacyComponentSerializer.legacySection()
.serialize(MiniMessage.miniMessage().deserialize(name + "<red>[offline]<white>"));
}
stringBuilder.append(name);
if (i != allInvisiblePlayerUUIDs.size() - 1) {
stringBuilder.append(ChatColor.GREEN).append(", ").append(ChatColor.WHITE);
stringBuilder.append(LegacyComponentSerializer.legacySection()
.serialize(MiniMessage.miniMessage().deserialize("<green>, <white>")));
}
}
listMessage = listMessage.replace("%l", stringBuilder.toString());
plugin.sendMessage(sender, listMessage, sender);
}
plugin.sendMessage(sender, listMessage, sender,
PlaceholderConverter.placeholder("vanished_list", stringBuilder.toString()));
}
}
@@ -9,34 +9,38 @@
package de.myzelyam.supervanish.config;
import de.myzelyam.supervanish.SuperVanish;
import net.trueog.utilitiesog.UtilitiesOG;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.configuration.file.FileConfiguration;
import java.util.List;
import java.util.Collections;
import java.util.logging.Level;
import lombok.Data;
import static de.myzelyam.supervanish.SuperVanish.*;
@Data
public class ConfigMgr {
private final SuperVanish plugin;
private final FileMgr fileMgr;
private boolean settingsUpdateRequired, messagesUpdateRequired;
private FileConfiguration settings, messages, playerData;
private ConfigurableFile messagesFile, settingsFile;
private boolean settingsUpdateRequired;
private boolean messagesUpdateRequired;
private FileConfiguration settings;
private FileConfiguration messages;
private FileConfiguration playerData;
private ConfigurableFile messagesFile;
private ConfigurableFile settingsFile;
private StorageFile playerDataFile;
public ConfigMgr(SuperVanish plugin) {
this.plugin = plugin;
fileMgr = new FileMgr(plugin);
}
public void prepareFiles() {
// messages
messagesFile = (ConfigurableFile) fileMgr.addFile("messages", FileMgr.FileType.CONFIG);
messages = messagesFile.getConfig();
@@ -48,16 +52,19 @@ public class ConfigMgr {
playerData = playerDataFile.getConfig();
playerData.addDefault("InvisiblePlayers", Collections.emptyList());
playerData.options().copyDefaults(true);
playerData.options().header("SuperVanish v" + plugin.getDescription().getVersion() + " - Data file");
playerData.options().setHeader(List.of("SuperVanish v" + plugin.getPluginMeta().getVersion() + " - Data file"));
playerDataFile.save();
checkFilesForLeftOvers();
}
public void checkFilesForLeftOvers() {
try {
String currentSettingsVersion = settings.getString("ConfigVersion");
String newestVersion = plugin.getDescription().getVersion();
String newestVersion = plugin.getPluginMeta().getVersion();
String currentMessagesVersion = messages.getString("MessagesVersion");
messagesUpdateRequired = fileRequiresRecreation(currentMessagesVersion, false);
settingsUpdateRequired = fileRequiresRecreation(currentSettingsVersion, true);
@@ -66,32 +73,111 @@ public class ConfigMgr {
if (newestVersion.equals(currentMessagesVersion))
messagesUpdateRequired = false;
if (settingsUpdateRequired || messagesUpdateRequired) {
String currentVersion = plugin.getDescription().getVersion();
boolean isDismissed = playerData.getBoolean("PlayerData.CONSOLE.dismissed."
+ currentVersion.replace(".", "_"), false);
if (!isDismissed) plugin.log(Level.WARNING, "At least one config file is outdated, " +
"it's recommended to regenerate it using '/sv recreatefiles'");
String currentVersion = plugin.getPluginMeta().getVersion();
boolean isDismissed = playerData
.getBoolean("PlayerData.CONSOLE.dismissed." + currentVersion.replace(".", "_"), false);
if (!isDismissed)
plugin.log(Level.WARNING, "At least one config file needs to be recreated. "
+ "Use '/sv recreatefiles' to regenerate it.");
}
if (currentSettingsVersion.startsWith("1.5.") || currentSettingsVersion.startsWith("1.4.")) {
Bukkit.getConsoleSender().sendMessage(ChatColor.YELLOW + "You have a very outdated " +
"config file, your settings will not work until you regenerate your SV-files " +
"using /sv recreatefiles");
UtilitiesOG.logToConsole("[SuperVanish]",
"<yellow>You have a very old config file. Your settings will not work until you "
+ "regenerate your SV files using /sv recreatefiles.");
}
} catch (Exception e) {
plugin.logException(e);
}
}
private boolean fileRequiresRecreation(String currentVersion, boolean isSettingsFile) {
if (currentVersion == null) return true;
for (String ignoredVersion : isSettingsFile ? NON_REQUIRED_SETTINGS_UPDATES
: NON_REQUIRED_MESSAGES_UPDATES) {
if (currentVersion.equalsIgnoreCase(ignoredVersion)) return false;
}
if (currentVersion == null)
return true;
for (String ignoredVersion : isSettingsFile ? NON_REQUIRED_SETTINGS_UPDATES : NON_REQUIRED_MESSAGES_UPDATES) {
if (currentVersion.equalsIgnoreCase(ignoredVersion))
return false;
}
return true;
}
public FileMgr getFileMgr() {
return fileMgr;
}
public boolean isSettingsUpdateRequired() {
return settingsUpdateRequired;
}
public boolean isMessagesUpdateRequired() {
return messagesUpdateRequired;
}
public FileConfiguration getSettings() {
return settings;
}
public FileConfiguration getMessages() {
return messages;
}
public FileConfiguration getPlayerData() {
return playerData;
}
public void setSettings(FileConfiguration settings) {
this.settings = settings;
}
public void setMessages(FileConfiguration messages) {
this.messages = messages;
}
public ConfigurableFile getMessagesFile() {
return messagesFile;
}
public ConfigurableFile getSettingsFile() {
return settingsFile;
}
public StorageFile getPlayerDataFile() {
return playerDataFile;
}
}
@@ -27,47 +27,72 @@ public class ConfigurableFile implements PluginFile<FileConfiguration> {
private FileConfiguration defaultFileConfiguration;
public ConfigurableFile(String name, SuperVanish plugin) {
this.name = name + ".yml";
this.plugin = plugin;
setup();
}
@Override
public String getName() {
return name;
}
private void setup() {
file = new File(plugin.getDataFolder(), name);
try (Reader reader = new InputStreamReader(plugin.getResource(name))) {
defaultFileConfiguration = YamlConfiguration.loadConfiguration(reader);
} catch (IOException e) {
e.printStackTrace();
}
save();
}
@Override
public void reload() {
fileConfiguration = YamlConfiguration.loadConfiguration(file);
}
@Override
public FileConfiguration getConfig() {
if (fileConfiguration == null) {
reload();
}
return fileConfiguration;
}
@Override
public void save() {
if (!file.exists()) {
plugin.saveResource(name, false);
}
}
public FileConfiguration getDefaultConfig() {
return defaultFileConfiguration;
}
}
@@ -20,40 +20,55 @@ public class FileMgr {
private Map<String, PluginFile<?>> files;
public FileMgr(SuperVanishPlugin plugin) {
this.plugin = plugin;
files = new HashMap<>();
}
public PluginFile<?> addFile(String name, FileType type) {
if (name == null)
throw new IllegalArgumentException("The file name cannot be null!");
if (type == FileType.STORAGE) {
StorageFile file = new StorageFile(name, (SuperVanish) plugin);
files.put(name, file);
return file;
} else if (type == FileType.CONFIG) {
ConfigurableFile file = new ConfigurableFile(name, (SuperVanish) plugin);
files.put(name, file);
return file;
} else {
throw new IllegalArgumentException("The FileType cannot be null!");
}
}
public void reloadFile(String fileName) {
PluginFile<?> file = files.get(fileName);
if (file != null)
file.reload();
else
throw new IllegalArgumentException("Specified file doesn't exist!");
}
public void reloadAll() {
for (String fileName : files.keySet())
reloadFile(fileName);
}
public enum FileType {
STORAGE, CONFIG
}
}
@@ -17,4 +17,5 @@ public interface PluginFile<CT> {
CT getConfig();
void save();
}
@@ -24,39 +24,55 @@ public class StorageFile implements PluginFile<FileConfiguration> {
private FileConfiguration config;
public StorageFile(String name, SuperVanish plugin) {
this.name = name;
this.plugin = plugin;
setup();
}
@Override
public String getName() {
return name;
}
private void setup() {
file = new File(plugin.getDataFolder().getPath() + File.separator
+ name + ".yml");
file = new File(plugin.getDataFolder().getPath() + File.separator + name + ".yml");
config = YamlConfiguration.loadConfiguration(file);
save();
}
@Override
public void reload() {
setup();
}
@Override
public FileConfiguration getConfig() {
return config;
}
@Override
public void save() {
try {
config.save(file);
} catch (IOException e) {
e.printStackTrace();
}
}
}
@@ -1,117 +0,0 @@
/*
* Copyright © 2015, Leon Mangler and the SuperVanish contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package de.myzelyam.supervanish.features;
import de.myzelyam.api.vanish.PlayerShowEvent;
import de.myzelyam.api.vanish.PostPlayerHideEvent;
import de.myzelyam.supervanish.SuperVanish;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
public class Broadcast extends Feature {
public Broadcast(SuperVanish plugin) {
super(plugin);
}
public static void announceSilentJoin(Player vanished, SuperVanish plugin) {
if (plugin.getSettings().getBoolean("MessageOptions.AnnounceRealJoinQuitToAdmins", true)) {
for (Player onlinePlayer : Bukkit.getOnlinePlayers()) {
if (vanished == onlinePlayer)
continue;
if (plugin.canSee(onlinePlayer, vanished)) {
plugin.sendMessage(onlinePlayer, "SilentJoinMessageForAdmins", vanished, onlinePlayer);
}
}
}
}
public static void announceSilentDeath(Player p, SuperVanish plugin, String deathMessage) {
if (plugin.getSettings().getBoolean("MessageOptions.AnnounceDeathToAdmins", true)) {
for (Player onlinePlayer : Bukkit.getOnlinePlayers()) {
if (p == onlinePlayer)
continue;
if (plugin.canSee(onlinePlayer, p)) {
String message = plugin.getMessage("SilentDeathMessage")
.replace("%deathmsg%", deathMessage);
plugin.sendMessage(onlinePlayer, message, p, onlinePlayer);
}
}
}
}
public static void announceSilentQuit(Player p, SuperVanish plugin) {
if (plugin.getSettings().getBoolean("MessageOptions.AnnounceRealJoinQuitToAdmins", true)) {
for (Player onlinePlayer : Bukkit.getOnlinePlayers()) {
if (p == onlinePlayer)
continue;
if (plugin.canSee(onlinePlayer, p)) {
plugin.sendMessage(onlinePlayer, "SilentQuitMessageForAdmins", p, onlinePlayer);
}
}
}
}
@Override
public boolean isActive() {
return plugin.getSettings().getBoolean("MessageOptions.FakeJoinQuitMessages.BroadcastFakeQuitOnVanish")
|| plugin.getSettings().getBoolean("MessageOptions.FakeJoinQuitMessages" +
".BroadcastFakeQuitOnReappear");
}
@EventHandler
public void onVanish(PostPlayerHideEvent e) {
final Player p = e.getPlayer();
if (plugin.getSettings().getBoolean("MessageOptions.FakeJoinQuitMessages.BroadcastFakeQuitOnVanish")
&& !e.isSilent()) {
for (Player onlinePlayer : Bukkit.getOnlinePlayers()) {
if (!plugin.canSee(onlinePlayer, p)) {
if (!plugin.getSettings().getBoolean(
"MessageOptions.FakeJoinQuitMessages.SendMessageOnlyToAdmins"))
plugin.sendMessage(onlinePlayer, "VanishMessage", p, onlinePlayer);
} else if (!plugin.getSettings().getBoolean(
"MessageOptions.FakeJoinQuitMessages.SendMessageOnlyToUsers"))
if (!plugin.getSettings().getBoolean(
"MessageOptions.FakeJoinQuitMessages.AnnounceVanishReappearToAdmins"))
plugin.sendMessage(onlinePlayer, "VanishMessage", p, onlinePlayer);
else if (onlinePlayer == p && !plugin.getSettings().getBoolean(
"MessageOptions.FakeJoinQuitMessages.SendMessageOnlyToAdmins"))
plugin.sendMessage(onlinePlayer, "VanishMessage", p, onlinePlayer);
else if (onlinePlayer != p)
plugin.sendMessage(onlinePlayer, "VanishMessageWithPermission", p, onlinePlayer);
}
}
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onReappear(PlayerShowEvent e) {
Player p = e.getPlayer();
if (plugin.getSettings().getBoolean(
"MessageOptions.FakeJoinQuitMessages.BroadcastFakeJoinOnReappear") && !e.isSilent()) {
for (Player onlinePlayer : Bukkit.getOnlinePlayers()) {
if (!plugin.canSee(onlinePlayer, p)) {
if (!plugin.getSettings().getBoolean(
"MessageOptions.FakeJoinQuitMessages.SendMessageOnlyToAdmins"))
plugin.sendMessage(onlinePlayer, "ReappearMessage", p, onlinePlayer);
} else if (!plugin.getSettings().getBoolean(
"MessageOptions.FakeJoinQuitMessages.SendMessageOnlyToUsers"))
if (!plugin.getSettings().getBoolean(
"MessageOptions.FakeJoinQuitMessages.AnnounceVanishReappearToAdmins"))
plugin.sendMessage(onlinePlayer, "ReappearMessageWithPermission", p, onlinePlayer);
else if (onlinePlayer == p && !plugin.getSettings().getBoolean(
"MessageOptions.FakeJoinQuitMessages.SendMessageOnlyToAdmins"))
plugin.sendMessage(onlinePlayer, "ReappearMessage", p, onlinePlayer);
else if (onlinePlayer != p)
plugin.sendMessage(onlinePlayer, "ReappearMessageWithPermission", p, onlinePlayer);
}
}
}
}
@@ -20,18 +20,25 @@ public abstract class Feature implements Listener {
protected final SuperVanish plugin;
public Feature(SuperVanish plugin) {
this.plugin = plugin;
}
public void onEnable() {
}
public void onDisable() {
}
public abstract boolean isActive();
protected void delay(Runnable runnable) {
plugin.getServer().getScheduler().runTaskLater(plugin, runnable, 1);
}
}
@@ -8,12 +8,6 @@
package de.myzelyam.supervanish.features;
import de.myzelyam.supervanish.SuperVanish;
import de.myzelyam.supervanish.utils.Requirement;
import org.bukkit.Bukkit;
import org.bukkit.event.HandlerList;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.Collection;
@@ -23,100 +17,149 @@ import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import lombok.Data;
import org.bukkit.Bukkit;
import org.bukkit.event.HandlerList;
import de.myzelyam.supervanish.SuperVanish;
import de.myzelyam.supervanish.utils.Requirement;
public class FeatureMgr {
private static final Requirement<FeatureInfo> protocolLibInstalled = featureInfo -> Bukkit.getPluginManager().isPluginEnabled("ProtocolLib"),
oneDotEightOrHigher = featureInfo -> featureInfo.getPlugin().getVersionUtil().isOneDotXOrHigher(8),
oneDotSeventeenOrHigher = featureInfo -> featureInfo.getPlugin().getVersionUtil().isOneDotXOrHigher(17),
supportedServer = featureInfo -> Bukkit.getServer().getName().equals("Paper") || Bukkit.getServer().getName().equals("Purpur");
private static final Requirement<FeatureInfo> protocolLibInstalled = featureInfo -> Bukkit.getPluginManager()
.isPluginEnabled("ProtocolLib");
private static final Requirement<FeatureInfo> oneDotEightOrHigher = featureInfo -> featureInfo.getPlugin()
.getVersionUtil().isOneDotXOrHigher(8);
private static final Requirement<FeatureInfo> oneDotSeventeenOrHigher = featureInfo -> featureInfo.getPlugin()
.getVersionUtil().isOneDotXOrHigher(17);
private static final Requirement<FeatureInfo> supportedServer = featureInfo -> "Paper"
.equals(Bukkit.getServer().getName()) || "Purpur".equals(Bukkit.getServer().getName());
private final Map<String, FeatureInfo> registeredFeatures = new HashMap<>();
private final Set<Feature> activeFeatures = new HashSet<>();
private final SuperVanish plugin;
public FeatureMgr(SuperVanish plugin) {
this.plugin = plugin;
registeredFeatures.put("SilentOpenChest", new FeatureInfo(SilentOpenChest.class, plugin,
Arrays.asList(protocolLibInstalled, oneDotEightOrHigher)));
registeredFeatures.put("NightVision", new FeatureInfo(NightVision.class, plugin,
Arrays.asList(protocolLibInstalled, oneDotEightOrHigher)));
registeredFeatures.put("NightVision",
new FeatureInfo(NightVision.class, plugin, Arrays.asList(protocolLibInstalled, oneDotEightOrHigher)));
registeredFeatures.put("VanishIndication", new FeatureInfo(VanishIndication.class, plugin,
Arrays.asList(protocolLibInstalled, oneDotEightOrHigher)));
registeredFeatures.put("Broadcast", new FeatureInfo(Broadcast.class, plugin));
registeredFeatures.put("NoSculkSensorDetection", new FeatureInfo(NoSculkSensorDetection.class, plugin,
Collections.singletonList(oneDotSeventeenOrHigher)));
registeredFeatures.put("NoTurtleEggBreaking", new FeatureInfo(NoTurtleEggBreaking.class, plugin,
Collections.singletonList(oneDotSeventeenOrHigher)));
registeredFeatures.put("NoDripLeafTilt", new FeatureInfo(NoDripLeafTilt.class, plugin,
Collections.singletonList(oneDotSeventeenOrHigher)));
registeredFeatures.put("NoRaidTrigger", new FeatureInfo(NoRaidTrigger.class, plugin,
Collections.singletonList(oneDotSeventeenOrHigher)));
registeredFeatures.put("NoMobSpawn", new FeatureInfo(NoMobSpawn.class, plugin,
Collections.singletonList(supportedServer)));
registeredFeatures.put("HideAdvancementMessages", new FeatureInfo(HideAdvancementMessages.class, plugin,
Collections.singletonList(supportedServer)));
registeredFeatures.put("NoTurtleEggBreaking",
new FeatureInfo(NoTurtleEggBreaking.class, plugin, Collections.singletonList(oneDotSeventeenOrHigher)));
registeredFeatures.put("NoDripLeafTilt",
new FeatureInfo(NoDripLeafTilt.class, plugin, Collections.singletonList(oneDotSeventeenOrHigher)));
registeredFeatures.put("NoRaidTrigger",
new FeatureInfo(NoRaidTrigger.class, plugin, Collections.singletonList(oneDotSeventeenOrHigher)));
registeredFeatures.put("NoMobSpawn",
new FeatureInfo(NoMobSpawn.class, plugin, Collections.singletonList(supportedServer)));
}
public void enableFeatures() {
featureLoop:
for (String id : registeredFeatures.keySet()) {
FeatureInfo featureInfo = registeredFeatures.get(id);
featureLoop: for (String id : registeredFeatures.keySet()) {
final FeatureInfo featureInfo = registeredFeatures.get(id);
for (Requirement<FeatureInfo> requirement : featureInfo.getRequirements()) {
if (!requirement.fulfilledBy(featureInfo)) continue featureLoop;
if (!requirement.fulfilledBy(featureInfo)) {
continue featureLoop;
}
Feature feature;
}
final Feature feature;
try {
feature = featureInfo.getFeatureClass().getConstructor(SuperVanish.class).newInstance(plugin);
} catch (NoSuchMethodException | InvocationTargetException
| InstantiationException | IllegalAccessException e) {
} catch (NoSuchMethodException | InvocationTargetException | InstantiationException
| IllegalAccessException e)
{
plugin.logException(e);
continue;
}
if (!feature.isActive()) continue;
if (!feature.isActive()) {
continue;
}
activeFeatures.add(feature);
Bukkit.getPluginManager().registerEvents(feature, plugin);
feature.onEnable();
}
}
public void disableFeatures() {
for (Feature feature : activeFeatures) {
activeFeatures.forEach(feature -> {
feature.onDisable();
HandlerList.unregisterAll(feature);
}
});
activeFeatures.clear();
}
@SuppressWarnings("unchecked")
public <T extends Feature> T getFeature(Class<T> featureClass) {
for (Feature feature : activeFeatures) {
if (feature.getClass().equals(featureClass)) {
//noinspection unchecked
return (T) feature;
}
}
return null;
// noinspection unchecked on purpose
return activeFeatures.stream().filter(feature -> feature.getClass().equals(featureClass)).findFirst()
.map(feature -> (T) feature).orElse(null);
}
public Set<Feature> getActiveFeatures() {
return activeFeatures;
}
@Data
private static class FeatureInfo {
private final Class<? extends Feature> featureClass;
private final Collection<Requirement<FeatureInfo>> requirements;
private final SuperVanish plugin;
FeatureInfo(Class<? extends Feature> featureClass, SuperVanish plugin,
Collection<Requirement<FeatureInfo>> requirements) {
this.featureClass = featureClass;
this.requirements = requirements;
this.plugin = plugin;
}
private record FeatureInfo(Class<? extends Feature> featureClass, SuperVanish plugin,
Collection<Requirement<FeatureInfo>> requirements)
{
FeatureInfo(Class<? extends Feature> featureClass, SuperVanish plugin) {
this(featureClass, plugin, Collections.emptySet());
}
public Class<? extends Feature> getFeatureClass() {
return featureClass;
}
public Collection<Requirement<FeatureInfo>> getRequirements() {
return requirements;
}
public SuperVanish getPlugin() {
return plugin;
}
}
}
@@ -1,40 +0,0 @@
package de.myzelyam.supervanish.features;
import de.myzelyam.supervanish.SuperVanish;
import net.kyori.adventure.text.Component;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.player.PlayerAdvancementDoneEvent;
// This feature is paper-only because the PlayerAdvancementDoneEvent#message() method doesn't exist in Spigot
public class HideAdvancementMessages extends Feature {
private boolean suppressErrors = false;
public HideAdvancementMessages(SuperVanish plugin) {
super(plugin);
}
@EventHandler
public void onAdvancementDone(PlayerAdvancementDoneEvent e) {
try {
Player p = e.getPlayer();
Component message = e.message();
if (message == null) return;
if (!plugin.getVanishStateMgr().isVanished(p.getUniqueId())) return;
if (e.message() == null) return;
e.message(null);
p.sendMessage(message);
} catch (Exception er) {
if (!suppressErrors) {
plugin.logException(er);
suppressErrors = true;
}
}
}
@Override
public boolean isActive() {
return plugin.getSettings().getBoolean("MessageOptions.HideAdvancementMessages", true);
}
}
@@ -33,95 +33,137 @@ public class NightVision extends Feature implements Runnable {
private final Map<UUID, PotionEffect> playerPreviousPotionEffectMap = new HashMap<>();
public NightVision(SuperVanish plugin) {
super(plugin);
}
@Override
public void onEnable() {
plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, this, 20 * 60 * 2, 20 * 60 * 2);
}
@Override
public boolean isActive() {
return plugin.getSettings().getBoolean("InvisibilityFeatures.NightVisionEffect");
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onVanish(PlayerHideEvent e) {
Player p = e.getPlayer();
// getPotionEffect(..) is 1.10+ API
if (plugin.getVersionUtil().isOneDotXOrHigher(10)) {
if (p.getPotionEffect(PotionEffectType.NIGHT_VISION) != null) {
playerPreviousPotionEffectMap.put(p.getUniqueId(), p.getPotionEffect(PotionEffectType.NIGHT_VISION));
}
}
sendAddPotionEffect(p);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onReappear(PlayerShowEvent e) {
Player p = e.getPlayer();
sendRemovePotionEffect(p);
if (playerPreviousPotionEffectMap.containsKey(p.getUniqueId())) {
p.addPotionEffect(playerPreviousPotionEffectMap.remove(p.getUniqueId()));
}
}
@EventHandler
public void onJoin(PlayerJoinEvent e) {
Player p = e.getPlayer();
if (!plugin.getVanishStateMgr().isVanished(p.getUniqueId())) {
if (playerPreviousPotionEffectMap.containsKey(p.getUniqueId())) {
p.addPotionEffect(playerPreviousPotionEffectMap.remove(p.getUniqueId()));
}
} else {
sendAddPotionEffect(p);
}
}
@EventHandler
public void onQuit(PlayerQuitEvent e) {
Player p = e.getPlayer();
if (!plugin.getVanishStateMgr().isVanished(p.getUniqueId()))
return;
sendRemovePotionEffect(p);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onTeleport(PlayerTeleportEvent e) {
Player p = e.getPlayer();
if (!plugin.getVanishStateMgr().isVanished(p.getUniqueId()))
return;
if (e.getFrom().getWorld() == null || e.getTo() == null || e.getTo().getWorld() == null
|| e.getFrom().getWorld().getName().equals(e.getTo().getWorld().getName())) return;
|| e.getFrom().getWorld().getName().equals(e.getTo().getWorld().getName()))
return;
sendRemovePotionEffect(p);
}
@EventHandler
public void onWorldChange(PlayerChangedWorldEvent e) {
Player p = e.getPlayer();
if (!plugin.getVanishStateMgr().isVanished(p.getUniqueId()))
return;
sendAddPotionEffect(p);
}
@Override
public void run() {
// renew every now and then to prevent blinking bug
for (UUID uuid : plugin.getVanishStateMgr().getOnlineVanishedPlayers()) {
Player p = Bukkit.getPlayer(uuid);
if (p == null) continue;
if (p == null)
continue;
sendRemovePotionEffect(p);
sendAddPotionEffect(p);
}
}
private void sendAddPotionEffect(Player p) {
p.addPotionEffect(new PotionEffect(PotionEffectType.NIGHT_VISION,
INFINITE_POTION_EFFECT_LENGTH, 0, true, false));
p.addPotionEffect(
new PotionEffect(PotionEffectType.NIGHT_VISION, INFINITE_POTION_EFFECT_LENGTH, 0, true, false));
}
private void sendRemovePotionEffect(Player p) {
p.removePotionEffect(PotionEffectType.NIGHT_VISION);
}
}
@@ -10,22 +10,29 @@ import org.bukkit.event.player.PlayerInteractEvent;
public class NoDripLeafTilt extends Feature {
public NoDripLeafTilt(SuperVanish plugin) {
super(plugin);
}
@EventHandler(priority = EventPriority.HIGH)
public void onInteract(PlayerInteractEvent e) {
Player p = e.getPlayer();
if (!plugin.getVanishStateMgr().isVanished(p.getUniqueId())) return;
if (!plugin.getVanishStateMgr().isVanished(p.getUniqueId()))
return;
if (!e.getAction().equals(Action.PHYSICAL) || e.getClickedBlock() == null
|| !e.getClickedBlock().getType().toString().equals("BIG_DRIPLEAF"))
return;
e.setCancelled(true);
}
}
@Override
public boolean isActive() {
return plugin.getSettings().getBoolean("InvisibilityFeatures.DisableDripLeaf", true);
}
}
@@ -18,79 +18,123 @@ public class NoMobSpawn extends Feature {
private boolean suppressErrors = false;
public NoMobSpawn(SuperVanish plugin) {
super(plugin);
}
@EventHandler
public void onSkeletonHorseTrap(SkeletonHorseTrapEvent e) {
try {
List<HumanEntity> humans = e.getEligibleHumans();
int humansCount = humans.size();
for (HumanEntity human : humans) {
if (human instanceof Player) {
Player p = (Player) human;
if (plugin.getVanishStateMgr().isVanished(p.getUniqueId())) {
humansCount--;
}
}
}
if (humansCount == 0)
e.setCancelled(true);
} catch (Exception er) {
if (!suppressErrors) {
plugin.logException(er);
suppressErrors = true;
}
}
}
@EventHandler
public void onEntitySpawn(PlayerNaturallySpawnCreaturesEvent e) {
try {
if (plugin.getVanishStateMgr().isVanished(e.getPlayer().getUniqueId()))
e.setCancelled(true);
} catch (Exception er) {
if (!suppressErrors) {
plugin.logException(er);
suppressErrors = true;
}
}
}
@EventHandler
public void onEntitySpawnerSpawn(PreSpawnerSpawnEvent e) {
try {
// First check if a non-spectator vanished player is in range
boolean vanishedPlayerInRange = false;
for (UUID vanishedUUID : plugin.getVanishStateMgr().getOnlineVanishedPlayers()) {
Player p = Bukkit.getPlayer(vanishedUUID);
if (p == null) continue;
if (p.getWorld().equals(e.getSpawnerLocation().getWorld()) &&
p.getLocation().distanceSquared(e.getSpawnerLocation()) <= 256 &&
p.getGameMode() != GameMode.SPECTATOR)
if (p == null)
continue;
if (p.getWorld().equals(e.getSpawnerLocation().getWorld())
&& p.getLocation().distanceSquared(e.getSpawnerLocation()) <= 256
&& p.getGameMode() != GameMode.SPECTATOR)
vanishedPlayerInRange = true;
}
if (!vanishedPlayerInRange) return;
if (!vanishedPlayerInRange)
return;
// If so, only cancel if no non-vanished player is in range
for (Player p : Bukkit.getOnlinePlayers()) {
if (p.getWorld().equals(e.getSpawnerLocation().getWorld()) &&
p.getLocation().distanceSquared(e.getSpawnerLocation()) <= 256 &&
p.getGameMode() != GameMode.SPECTATOR &&
!plugin.getVanishStateMgr().isVanished(p.getUniqueId()))
if (p.getWorld().equals(e.getSpawnerLocation().getWorld())
&& p.getLocation().distanceSquared(e.getSpawnerLocation()) <= 256
&& p.getGameMode() != GameMode.SPECTATOR
&& !plugin.getVanishStateMgr().isVanished(p.getUniqueId()))
return;
}
e.setCancelled(true);
} catch (Exception er) {
if (!suppressErrors) {
plugin.logException(er);
suppressErrors = true;
}
}
}
@Override
public boolean isActive() {
return plugin.getSettings().getBoolean("InvisibilityFeatures.PreventMobSpawning", true);
}
}
@@ -23,56 +23,82 @@ import org.bukkit.scoreboard.Team;
public class NoPush extends Feature {
public NoPush(SuperVanish plugin) {
super(plugin);
}
@Override
public boolean isActive() {
return plugin.getSettings().getBoolean("InvisibilityFeatures.DisablePush");
}
public void setCantPush(Player p) {
Team team = p.getScoreboard().getTeam("Vanished");
if (team == null) {
team = p.getScoreboard().registerNewTeam("Vanished");
}
try {
team.setOption(Team.Option.COLLISION_RULE, Team.OptionStatus.NEVER);
team.addEntry(p.getName());
} catch (NoSuchMethodError | NoClassDefFoundError ignored) {
}
}
public void setCanPush(Player p) {
Team team = p.getScoreboard().getTeam("Vanished");
if (team != null)
team.removeEntry(p.getName());
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onVanish(PlayerHideEvent e) {
setCantPush(e.getPlayer());
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onReappear(PlayerShowEvent e) {
setCanPush(e.getPlayer());
}
@EventHandler
public void onJoin(final PlayerJoinEvent e) {
if (plugin.getVanishStateMgr().isVanished(e.getPlayer().getUniqueId()))
new BukkitRunnable() {
@Override
public void run() {
setCantPush(e.getPlayer());
}
}.runTaskLater(plugin, 5);
}
@EventHandler
public void onQuit(PlayerQuitEvent e) {
setCanPush(e.getPlayer());
}
}
@@ -4,26 +4,31 @@ import de.myzelyam.supervanish.SuperVanish;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.raid.RaidEvent;
import org.bukkit.event.raid.RaidTriggerEvent;
public class NoRaidTrigger extends Feature {
public NoRaidTrigger(SuperVanish plugin) {
super(plugin);
}
@EventHandler(priority = EventPriority.HIGH)
public void onInteract(RaidTriggerEvent e) {
Player p = e.getPlayer();
if (!plugin.getVanishStateMgr().isVanished(p.getUniqueId())) return;
if (!plugin.getVanishStateMgr().isVanished(p.getUniqueId()))
return;
e.setCancelled(true);
}
@Override
public boolean isActive() {
return plugin.getSettings().getBoolean("InvisibilityFeatures.PreventRaidTriggering", true);
}
}
@@ -11,28 +11,42 @@ import org.bukkit.event.player.PlayerInteractEvent;
public class NoSculkSensorDetection extends Feature {
public NoSculkSensorDetection(SuperVanish plugin) {
super(plugin);
}
@EventHandler(priority = EventPriority.HIGH)
public void onSculkSensorTrigger(BlockReceiveGameEvent e) {
if (!(e.getEntity() instanceof Player)) return;
if (!(e.getEntity() instanceof Player))
return;
Player p = (Player) e.getEntity();
if (!plugin.getVanishStateMgr().isVanished(p.getUniqueId())) return;
if (!plugin.getVanishStateMgr().isVanished(p.getUniqueId()))
return;
e.setCancelled(true);
}
@EventHandler(priority = EventPriority.HIGH)
public void onInteract(PlayerInteractEvent e) {
Player p = e.getPlayer();
if (!plugin.getVanishStateMgr().isVanished(p.getUniqueId())) return;
if (!e.getAction().equals(Action.PHYSICAL) || e.getClickedBlock() == null) return;
if (!e.getClickedBlock().getType().toString().matches("SCULK_SENSOR|CALIBRATED_SCULK_SENSOR|SCULK_SHRIEKER")) return;
if (!plugin.getVanishStateMgr().isVanished(p.getUniqueId()))
return;
if (!e.getAction().equals(Action.PHYSICAL) || e.getClickedBlock() == null)
return;
if (!e.getClickedBlock().getType().toString().matches("SCULK_SENSOR|CALIBRATED_SCULK_SENSOR|SCULK_SHRIEKER"))
return;
e.setCancelled(true);
}
@Override
public boolean isActive() {
return plugin.getSettings().getBoolean("InvisibilityFeatures.PreventSculkSensorActivation", true);
}
}
@@ -10,21 +10,29 @@ import org.bukkit.event.player.PlayerInteractEvent;
public class NoTurtleEggBreaking extends Feature {
public NoTurtleEggBreaking(SuperVanish plugin) {
super(plugin);
}
@EventHandler(priority = EventPriority.HIGH)
public void onInteract(PlayerInteractEvent e) {
Player p = e.getPlayer();
if (!plugin.getVanishStateMgr().isVanished(p.getUniqueId())) return;
if (!plugin.getVanishStateMgr().isVanished(p.getUniqueId()))
return;
if (!e.getAction().equals(Action.PHYSICAL) || e.getClickedBlock() == null
|| !e.getClickedBlock().getType().toString().equals("TURTLE_EGG"))
return;
e.setCancelled(true);
}
@Override
public boolean isActive() {
return plugin.getSettings().getBoolean("InvisibilityFeatures.PreventTurtleEggBreaking", true);
}
}
@@ -8,10 +8,34 @@
package de.myzelyam.supervanish.features;
import com.comphenix.protocol.ProtocolLibrary;
import de.myzelyam.api.vanish.PlayerShowEvent;
import de.myzelyam.supervanish.SuperVanish;
import de.myzelyam.supervanish.hooks.OpenInvHook;
import static org.bukkit.Material.BLACK_SHULKER_BOX;
import static org.bukkit.Material.BLUE_SHULKER_BOX;
import static org.bukkit.Material.BROWN_SHULKER_BOX;
import static org.bukkit.Material.CHEST;
import static org.bukkit.Material.CYAN_SHULKER_BOX;
import static org.bukkit.Material.ENDER_CHEST;
import static org.bukkit.Material.GRAY_SHULKER_BOX;
import static org.bukkit.Material.GREEN_SHULKER_BOX;
import static org.bukkit.Material.ITEM_FRAME;
import static org.bukkit.Material.LIGHT_BLUE_SHULKER_BOX;
import static org.bukkit.Material.LIGHT_GRAY_SHULKER_BOX;
import static org.bukkit.Material.LIME_SHULKER_BOX;
import static org.bukkit.Material.MAGENTA_SHULKER_BOX;
import static org.bukkit.Material.ORANGE_SHULKER_BOX;
import static org.bukkit.Material.PINK_SHULKER_BOX;
import static org.bukkit.Material.PURPLE_SHULKER_BOX;
import static org.bukkit.Material.RED_SHULKER_BOX;
import static org.bukkit.Material.SHULKER_BOX;
import static org.bukkit.Material.TRAPPED_CHEST;
import static org.bukkit.Material.WHITE_SHULKER_BOX;
import static org.bukkit.Material.YELLOW_SHULKER_BOX;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.Material;
@@ -23,15 +47,22 @@ import org.bukkit.event.block.Action;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.event.player.*;
import org.bukkit.event.player.PlayerGameModeChangeEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryView;
import org.bukkit.inventory.ItemStack;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.util.Vector;
import java.util.*;
import com.comphenix.protocol.ProtocolLibrary;
import static org.bukkit.Material.*;
import de.myzelyam.api.vanish.PlayerShowEvent;
import de.myzelyam.supervanish.SuperVanish;
import de.myzelyam.supervanish.hooks.OpenInvHook;
public class SilentOpenChest extends Feature {
@@ -40,220 +71,386 @@ public class SilentOpenChest extends Feature {
private final Collection<Material> additionalChestMaterials;
public SilentOpenChest(SuperVanish plugin) {
super(plugin);
additionalChestMaterials = new ArrayList<>();
if (plugin.getVersionUtil().isOneDotXOrHigher(11)) {
try {
//noinspection unused
InventoryType testInvType = InventoryType.SHULKER_BOX;
additionalChestMaterials.addAll(Arrays.asList(BLACK_SHULKER_BOX, BLUE_SHULKER_BOX, BROWN_SHULKER_BOX,
CYAN_SHULKER_BOX, GRAY_SHULKER_BOX, GREEN_SHULKER_BOX, LIGHT_BLUE_SHULKER_BOX,
LIME_SHULKER_BOX, MAGENTA_SHULKER_BOX, ORANGE_SHULKER_BOX, PINK_SHULKER_BOX,
PURPLE_SHULKER_BOX, RED_SHULKER_BOX, WHITE_SHULKER_BOX,
YELLOW_SHULKER_BOX));
CYAN_SHULKER_BOX, GRAY_SHULKER_BOX, GREEN_SHULKER_BOX, LIGHT_BLUE_SHULKER_BOX, LIME_SHULKER_BOX,
MAGENTA_SHULKER_BOX, ORANGE_SHULKER_BOX, PINK_SHULKER_BOX, PURPLE_SHULKER_BOX, RED_SHULKER_BOX,
WHITE_SHULKER_BOX, YELLOW_SHULKER_BOX));
try {
additionalChestMaterials.add(LIGHT_GRAY_SHULKER_BOX);
} catch (NoSuchFieldError e) {
// old name
// Old name.
additionalChestMaterials.add(Material.valueOf("SILVER_SHULKER_BOX"));
}
try {
additionalChestMaterials.add(SHULKER_BOX);
} catch (NoSuchFieldError ignored) {
// no standard shulker box in old versions
}
if (plugin.getVersionUtil().isOneDotXOrHigher(14)) {
additionalChestMaterials.add(Material.valueOf("BARREL"));
}
} catch (NoSuchFieldError | IllegalArgumentException ignored) {
// no shulker box support in very old versions
}
}
}
@Override
public void onDisable() {
for (Player p : playerStateInfoMap.keySet()) {
StateInfo stateInfo = playerStateInfoMap.remove(p);
if (stateInfo == null) continue;
restoreState(stateInfo, p);
final StateInfo stateInfo = playerStateInfoMap.remove(p);
if (stateInfo == null) {
continue;
}
restoreState(stateInfo, p);
}
}
@EventHandler(priority = EventPriority.LOWEST)
public void onSpectatorClick(InventoryClickEvent e) {
if (!(e.getWhoClicked() instanceof Player))
if (!(e.getWhoClicked() instanceof Player)) {
return;
Player p = (Player) e.getWhoClicked();
if (!plugin.getVanishStateMgr().isVanished(p.getUniqueId())) return;
if (!playerStateInfoMap.containsKey(p)) return;
if (p.getGameMode() == GameMode.SPECTATOR) {
e.setCancelled(false);
}
final Player p = (Player) e.getWhoClicked();
if (!plugin.getVanishStateMgr().isVanished(p.getUniqueId())) {
return;
}
if (!playerStateInfoMap.containsKey(p)) {
return;
}
if (p.getGameMode() == GameMode.SPECTATOR) {
e.setCancelled(false);
}
}
@EventHandler(priority = EventPriority.MONITOR)
public void onQuit(PlayerQuitEvent e) {
Player p = e.getPlayer();
StateInfo stateInfo = playerStateInfoMap.remove(p);
if (stateInfo == null) return;
final Player p = e.getPlayer();
final StateInfo stateInfo = playerStateInfoMap.remove(p);
if (stateInfo == null) {
return;
}
restoreState(stateInfo, p);
playerStateInfoMap.remove(p);
}
@EventHandler(priority = EventPriority.HIGH)
public void onTeleport(PlayerTeleportEvent e) {
Player p = e.getPlayer();
if (playerStateInfoMap.containsKey(p)
&& e.getCause() == PlayerTeleportEvent.TeleportCause.SPECTATE) {
final Player p = e.getPlayer();
if (playerStateInfoMap.containsKey(p) && e.getCause() == PlayerTeleportEvent.TeleportCause.SPECTATE) {
e.setCancelled(true);
}
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onReappear(PlayerShowEvent e) {
Player p = e.getPlayer();
StateInfo stateInfo = playerStateInfoMap.remove(p);
if (stateInfo == null) return;
final Player p = e.getPlayer();
final StateInfo stateInfo = playerStateInfoMap.remove(p);
if (stateInfo == null) {
return;
}
p.closeInventory();
restoreState(stateInfo, p);
playerStateInfoMap.remove(p);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onMove(PlayerMoveEvent e) {
Player p = e.getPlayer();
if (playerStateInfoMap.containsKey(p)) {
Location loc = e.getTo() != null ? e.getTo() : e.getFrom();
if (playerStateInfoMap.get(p).openLoc.distance(loc) > .5) {
p.closeInventory();
restoreState(playerStateInfoMap.get(p), p);
playerStateInfoMap.remove(p);
}
}
final Player p = e.getPlayer();
if (!playerStateInfoMap.containsKey(p)) {
return;
}
@EventHandler(priority = EventPriority.HIGH)
public void onGameModeChange(PlayerGameModeChangeEvent e) {
Player p = e.getPlayer();
if (playerStateInfoMap.containsKey(p) && e.getNewGameMode() != GameMode.SPECTATOR) {
// Don't let low-priority event listeners cancel the gamemode change
if (e.isCancelled()) e.setCancelled(false);
final Location loc = e.getTo() != null ? e.getTo() : e.getFrom();
if (playerStateInfoMap.get(p).openLoc.distance(loc) <= .5) {
return;
}
p.closeInventory();
restoreState(playerStateInfoMap.get(p), p);
playerStateInfoMap.remove(p);
}
@EventHandler(priority = EventPriority.NORMAL)
public void onGameModeChange(PlayerGameModeChangeEvent e) {
final Player p = e.getPlayer();
final boolean condition = playerStateInfoMap.containsKey(p) && e.getNewGameMode() != GameMode.SPECTATOR
&& e.isCancelled();
// Don't let low-priority event listeners cancel the gamemode change.
if (condition) {
e.setCancelled(false);
}
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onChestInteract(PlayerInteractEvent e) {
Player p = e.getPlayer();
if (!plugin.getVanishStateMgr().isVanished(p.getUniqueId())
|| !p.hasPermission("sv.silentchest")) return;
if (e.getAction() != Action.RIGHT_CLICK_BLOCK) return;
if (p.getGameMode() == GameMode.SPECTATOR) return;
// Remember to keep "p.getItemInHand() != null" as we can't ensure that older Spigot versions will always return a non-null value
//noinspection deprecation,ConstantConditions
if (p.isSneaking() && p.getItemInHand() != null
&& (p.getItemInHand().getType().isBlock() || p.getItemInHand().getType() == ITEM_FRAME)
&& p.getItemInHand().getType() != Material.AIR)
final Player p = e.getPlayer();
if (!plugin.getVanishStateMgr().isVanished(p.getUniqueId()) || !p.hasPermission("sv.silentchest")) {
return;
Block block = e.getClickedBlock();
if (block == null) return;
}
if (e.getAction() != Action.RIGHT_CLICK_BLOCK) {
return;
}
if (p.getGameMode() == GameMode.SPECTATOR) {
return;
}
final ItemStack heldItem = p.getInventory().getItemInMainHand();
if (p.isSneaking() && heldItem != null && (heldItem.getType().isBlock() || heldItem.getType() == ITEM_FRAME)
&& heldItem.getType() != Material.AIR)
{
return;
}
final Block block = e.getClickedBlock();
if (block == null) {
return;
}
if (block.getType() == ENDER_CHEST) {
e.setCancelled(true);
p.openInventory(p.getEnderChest());
return;
}
if (!(block.getType() == CHEST || block.getType() == TRAPPED_CHEST
|| plugin.getVersionUtil().isOneDotXOrHigher(11) && additionalChestMaterials.contains(block.getType())))
{
return;
StateInfo stateInfo = StateInfo.extract(p);
}
final StateInfo stateInfo = StateInfo.extract(p);
p.setVelocity(new Vector(0, 0, 0));
playerStateInfoMap.put(p, stateInfo);
p.setGameMode(GameMode.SPECTATOR);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onChestClose(InventoryCloseEvent e) {
if (!(e.getPlayer() instanceof Player))
if (!(e.getPlayer() instanceof Player)) {
return;
}
final Player p = (Player) e.getPlayer();
if (!playerStateInfoMap.containsKey(p)) return;
if (!playerStateInfoMap.containsKey(p)) {
return;
}
if (!isSilentOpenInventory(e.getView())) {
return;
}
new BukkitRunnable() {
@Override
public void run() {
StateInfo stateInfo = playerStateInfoMap.get(p);
if (stateInfo == null) return;
restoreState(stateInfo, p);
playerStateInfoMap.remove(p);
final StateInfo stateInfo = playerStateInfoMap.get(p);
if (stateInfo == null) {
return;
}
restoreState(stateInfo, p);
playerStateInfoMap.remove(p);
}
}.runTaskLater(plugin, 1);
}
private void restoreState(StateInfo stateInfo, Player p) {
p.setGameMode(stateInfo.gameMode);
p.teleport(p.getLocation().add(0, 0.2, 0));
new BukkitRunnable() {
@Override
public void run() {
p.setAllowFlight(stateInfo.canFly);
p.setFlying(stateInfo.isFlying);
}
}.runTaskLater(plugin, 1);
}
@Override
public boolean isActive() {
return plugin.getSettings().getBoolean("InvisibilityFeatures.OpenChestsSilently")
&& !(plugin.getPluginHookMgr() != null && plugin.getPluginHookMgr().isHookActive(OpenInvHook.class));
}
private boolean isShulkerBox(Inventory inv) {
try {
return inv.getType() == InventoryType.SHULKER_BOX;
} catch (NoSuchFieldError e) {
return false;
}
}
private boolean isShulkerBox(InventoryView inv) {
try {
return inv.getType() == InventoryType.SHULKER_BOX;
} catch (NoSuchFieldError e) {
return false;
return isShulkerBox(inv.getTopInventory());
}
private boolean isBarrel(InventoryView inv) {
try {
return inv.getType() == InventoryType.BARREL;
} catch (NoSuchFieldError e) {
return false;
}
}
private boolean isSilentOpenInventory(InventoryView inv) {
return inv.getType() == InventoryType.CHEST || inv.getType() == InventoryType.ENDER_CHEST || isShulkerBox(inv)
|| isBarrel(inv);
}
public boolean hasSilentlyOpenedChest(Player p) {
return playerStateInfoMap.containsKey(p);
}
@Override
public void onEnable() {
if (!plugin.getVersionUtil().isOneDotXOrHigher(19)) {
SilentOpenChestPacketAdapter packetAdapter = new SilentOpenChestPacketAdapter(this);
// ProtocolLib 5.0 maps PLAYER_INFO to ClientboundPlayerInfoUpdatePacket
final SilentOpenChestPacketAdapter packetAdapter = new SilentOpenChestPacketAdapter(this);
ProtocolLibrary.getProtocolManager().addPacketListener(packetAdapter);
}
}
private static class StateInfo {
private final boolean canFly, isFlying;
private final GameMode gameMode;
private final Location openLoc;
StateInfo(boolean canFly, boolean isFlying, GameMode gameMode, Location openLoc) {
this.canFly = canFly;
this.isFlying = isFlying;
this.gameMode = gameMode;
this.openLoc = openLoc;
}
private record StateInfo(boolean canFly, boolean isFlying, GameMode gameMode, Location openLoc) {
static StateInfo extract(Player p) {
return new StateInfo(
p.getAllowFlight(),
p.isFlying(),
p.getGameMode(),
p.getLocation()
);
return new StateInfo(p.getAllowFlight(), p.isFlying(), p.getGameMode(), p.getLocation());
}
}
}
@@ -2,7 +2,7 @@
* Copyright © 2015, Leon Mangler and the SuperVanish contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* license, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
@@ -20,11 +20,17 @@ import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import static com.comphenix.protocol.PacketType.Play.Server.*;
/**
* This is currently unused on Minecraft 1.19 or higher
* Adapter that masks a vanished player's silent-chest spectator state from
* other clients. On Purpur 1.19.4 ProtocolLib's PLAYER_INFO type maps to
* ClientboundPlayerInfoUpdatePacket which carries a Set<Action> instead of the
* legacy single action; ViaSuite handles client-side re-encoding for connected
* versions 1.8-1.21.x.
*/
public class SilentOpenChestPacketAdapter extends PacketAdapter {
@@ -33,90 +39,167 @@ public class SilentOpenChestPacketAdapter extends PacketAdapter {
private boolean suppressErrors = false;
public SilentOpenChestPacketAdapter(SilentOpenChest silentOpenChest) {
super(silentOpenChest.plugin, ListenerPriority.LOW, PLAYER_INFO, ABILITIES,
ENTITY_METADATA);
super(silentOpenChest.plugin, ListenerPriority.LOW, PLAYER_INFO, ABILITIES, ENTITY_METADATA);
this.silentOpenChest = silentOpenChest;
}
@Override
public void onPacketSending(PacketEvent event) {
try {
Player receiver = event.getPlayer();
if (receiver == null) return;
if (receiver == null)
return;
if (event.getPacketType() == PLAYER_INFO) {
// multiple events share same packet object
event.setPacket(event.getPacket().shallowClone());
List<PlayerInfoData> infoDataList = new ArrayList<>(
event.getPacket().getPlayerInfoDataLists().read(0));
if (!isGameModeUpdate(event))
return;
List<PlayerInfoData> infoDataList = new ArrayList<>(event.getPacket().getPlayerInfoDataLists().read(0));
boolean modified = false;
for (PlayerInfoData infoData : ImmutableList.copyOf(infoDataList)) {
if (!silentOpenChest.plugin.getVisibilityChanger().getHider()
.isHidden(infoData.getProfile().getUUID(), receiver)
&& silentOpenChest.plugin.getVanishStateMgr()
.isVanished(infoData.getProfile().getUUID())) {
Player vanishedTabPlayer = Bukkit.getPlayer(infoData.getProfile().getUUID());
if (infoData.getGameMode() == EnumWrappers.NativeGameMode.SPECTATOR
&& silentOpenChest.hasSilentlyOpenedChest(vanishedTabPlayer)
&& event.getPacket().getPlayerInfoAction().read(0)
== EnumWrappers.PlayerInfoAction.UPDATE_GAME_MODE) {
int latency;
try {
latency = infoData.getLatency();
} catch (NoSuchMethodError e) {
latency = 21;
}
PlayerInfoData newData = new PlayerInfoData(infoData.getProfile(),
latency, EnumWrappers.NativeGameMode.SURVIVAL,
infoData.getDisplayName());
infoDataList.remove(infoData);
infoDataList.add(newData);
}
}
UUID uuid = extractUUID(infoData);
if (uuid == null)
continue;
if (silentOpenChest.plugin.getVisibilityChanger().getHider().isHidden(uuid, receiver))
continue;
if (!silentOpenChest.plugin.getVanishStateMgr().isVanished(uuid))
continue;
if (infoData.getGameMode() != EnumWrappers.NativeGameMode.SPECTATOR)
continue;
Player vanishedTabPlayer = Bukkit.getPlayer(uuid);
if (vanishedTabPlayer == null || !silentOpenChest.hasSilentlyOpenedChest(vanishedTabPlayer))
continue;
PlayerInfoData newData = new PlayerInfoData(uuid, infoData.getLatency(), infoData.isListed(),
EnumWrappers.NativeGameMode.SURVIVAL, infoData.getProfile(), infoData.getDisplayName(),
infoData.getRemoteChatSessionData());
int index = infoDataList.indexOf(infoData);
infoDataList.set(index, newData);
modified = true;
}
if (modified)
event.getPacket().getPlayerInfoDataLists().write(0, infoDataList);
} else if (event.getPacketType() == GAME_STATE_CHANGE) {
// Currently unused due to ProtocolLib class loading bug
if (silentOpenChest.plugin.getVanishStateMgr().isVanished(
receiver.getUniqueId())) {
if (silentOpenChest.plugin.getVanishStateMgr().isVanished(receiver.getUniqueId())) {
try {
if (event.getPacket().getIntegers().read(0) != 3) return;
if (event.getPacket().getIntegers().read(0) != 3)
return;
} catch (FieldAccessException e) {
// TODO
}
if (!silentOpenChest.hasSilentlyOpenedChest(receiver)) return;
if (!silentOpenChest.hasSilentlyOpenedChest(receiver))
return;
event.setCancelled(true);
}
} else if (event.getPacketType() == ABILITIES) {
if (silentOpenChest.plugin.getVanishStateMgr().isVanished(
receiver.getUniqueId())) {
if (!silentOpenChest.hasSilentlyOpenedChest(receiver)) return;
if (silentOpenChest.plugin.getVanishStateMgr().isVanished(receiver.getUniqueId())) {
if (!silentOpenChest.hasSilentlyOpenedChest(receiver))
return;
event.setCancelled(true);
}
} else if (event.getPacketType() == ENTITY_METADATA) {
int entityID = event.getPacket().getIntegers().read(0);
if (entityID == receiver.getEntityId()) {
if (silentOpenChest.plugin.getVanishStateMgr().isVanished(
receiver.getUniqueId())) {
if (!silentOpenChest.hasSilentlyOpenedChest(receiver)) return;
if (silentOpenChest.plugin.getVanishStateMgr().isVanished(receiver.getUniqueId())) {
if (!silentOpenChest.hasSilentlyOpenedChest(receiver))
return;
event.setCancelled(true);
}
}
}
} catch (Exception | NoClassDefFoundError e) {
if (!suppressErrors) {
if (e.getMessage() == null
|| !e.getMessage().endsWith("is not supported for temporary players.")) {
if (e.getMessage() == null || !e.getMessage().endsWith("is not supported for temporary players.")) {
silentOpenChest.plugin.logException(e);
silentOpenChest.plugin.getLogger().warning("IMPORTANT: Please make sure that you are using the latest " +
"dev-build of ProtocolLib and that your server is up-to-date! This error likely " +
"happened inside of ProtocolLib code which is out of SuperVanish's control. It's part " +
"of an optional feature module and can be removed safely by disabling " +
"OpenChestsSilently in the config file. Please report this " +
"error if you can reproduce it on an up-to-date server with only latest " +
"ProtocolLib and latest SV installed.");
silentOpenChest.plugin.getLogger()
.warning("IMPORTANT: Please make sure that you are using the latest "
+ "dev-build of ProtocolLib and that your server is up-to-date! This error likely "
+ "happened inside of ProtocolLib code which is out of SuperVanish's control. It's part "
+ "of an optional feature module and can be removed safely by disabling "
+ "OpenChestsSilently in the config file. Please report this "
+ "error if you can reproduce it on an up-to-date server with only latest "
+ "ProtocolLib and latest SV installed.");
suppressErrors = true;
}
}
}
}
private boolean isGameModeUpdate(PacketEvent event) {
// 1.19.3+: ClientboundPlayerInfoUpdatePacket exposes Set<Action>
try {
Set<EnumWrappers.PlayerInfoAction> actions = event.getPacket().getPlayerInfoActions().read(0);
if (actions != null)
return actions.contains(EnumWrappers.PlayerInfoAction.UPDATE_GAME_MODE);
} catch (FieldAccessException | IllegalArgumentException ignored) {
// legacy single-action packet on pre-1.19.3
}
try {
EnumWrappers.PlayerInfoAction action = event.getPacket().getPlayerInfoAction().read(0);
return action == EnumWrappers.PlayerInfoAction.UPDATE_GAME_MODE;
} catch (FieldAccessException | IllegalArgumentException ignored) {
return false;
}
}
private UUID extractUUID(PlayerInfoData infoData) {
UUID uuid = infoData.getProfileId();
if (uuid != null)
return uuid;
if (infoData.getProfile() != null)
return infoData.getProfile().getUUID();
return null;
}
}
@@ -14,158 +14,290 @@ import com.comphenix.protocol.events.ListenerPriority;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.wrappers.EnumWrappers;
import com.comphenix.protocol.wrappers.PlayerInfoData;
import com.comphenix.protocol.wrappers.WrappedChatComponent;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
import com.comphenix.protocol.wrappers.WrappedRemoteChatSessionData;
import com.google.common.collect.ImmutableList;
import de.myzelyam.api.vanish.PlayerShowEvent;
import de.myzelyam.api.vanish.PostPlayerHideEvent;
import de.myzelyam.supervanish.SuperVanish;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.player.PlayerJoinEvent;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
import static com.comphenix.protocol.PacketType.Play.Server.PLAYER_INFO;
/**
* This is currently unused on Minecraft 1.19 or higher
* Marks visible vanished players as spectators in player info updates. On
* Purpur 1.19.4 this operates on the server-native
* ClientboundPlayerInfoUpdatePacket format and ViaSuite handles translation for
* connected 1.8-1.21.x clients.
*/
public class VanishIndication extends Feature {
private boolean suppressErrors = false;
public VanishIndication(SuperVanish plugin) {
super(plugin);
}
@Override
public boolean isActive() {
return !plugin.getVersionUtil().isOneDotXOrHigher(19)
&& plugin.getSettings().getBoolean("IndicationFeatures.MarkVanishedPlayersAsSpectators");
return plugin.getSettings().getBoolean("IndicationFeatures.MarkVanishedPlayersAsSpectators");
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onVanish(PostPlayerHideEvent e) {
Player p = e.getPlayer();
for (Player onlinePlayer : Bukkit.getOnlinePlayers()) {
if (!plugin.getVisibilityChanger().getHider().isHidden(p, onlinePlayer) && p != onlinePlayer) {
sendPlayerInfoChangeGameModePacket(onlinePlayer, p, true);
}
}
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onReappear(PlayerShowEvent e) {
final Player p = e.getPlayer();
for (final Player onlinePlayer : Bukkit.getOnlinePlayers()) {
if (!plugin.getVisibilityChanger().getHider().isHidden(p, onlinePlayer) && p != onlinePlayer) {
delay(() -> sendPlayerInfoChangeGameModePacket(onlinePlayer, p, false));
}
}
}
@EventHandler
public void onJoin(PlayerJoinEvent e) {
final Player p = e.getPlayer();
delay(() -> {
// tell others that p is a spectator
if (plugin.getVanishStateMgr().isVanished(p.getUniqueId()))
for (Player onlinePlayer : Bukkit.getOnlinePlayers()) {
if (!plugin.getVisibilityChanger().getHider().isHidden(p, onlinePlayer)
&& p != onlinePlayer) {
if (!plugin.getVisibilityChanger().getHider().isHidden(p, onlinePlayer) && p != onlinePlayer) {
sendPlayerInfoChangeGameModePacket(onlinePlayer, p, true);
}
}
// tell p that others are spectators
for (Player onlinePlayer : Bukkit.getOnlinePlayers()) {
if (!plugin.getVanishStateMgr().isVanished(onlinePlayer.getUniqueId())) continue;
if (!plugin.getVisibilityChanger().getHider().isHidden(onlinePlayer, p)
&& p != onlinePlayer) {
if (!plugin.getVanishStateMgr().isVanished(onlinePlayer.getUniqueId()))
continue;
if (!plugin.getVisibilityChanger().getHider().isHidden(onlinePlayer, p) && p != onlinePlayer) {
sendPlayerInfoChangeGameModePacket(p, onlinePlayer, true);
}
}
});
}
@Override
public void onEnable() {
ProtocolLibrary.getProtocolManager().addPacketListener(
new PacketAdapter(plugin, ListenerPriority.NORMAL, PacketType.Play.Server.PLAYER_INFO) {
new PacketAdapter(plugin, ListenerPriority.NORMAL, PacketType.Play.Server.PLAYER_INFO)
{
@Override
public void onPacketSending(PacketEvent event) {
try {
// multiple events share same packet object
event.setPacket(event.getPacket().shallowClone());
List<PlayerInfoData> infoDataList = new ArrayList<>(
event.getPacket().getPlayerInfoDataLists().read(0));
Player receiver = event.getPlayer();
for (PlayerInfoData infoData : ImmutableList.copyOf(infoDataList)) {
try {
if (!VanishIndication.this.plugin.getVisibilityChanger().getHider()
.isHidden(infoData.getProfile().getUUID(), receiver)
&& VanishIndication.this.plugin.getVanishStateMgr()
.isVanished(infoData.getProfile().getUUID())) {
if (!receiver.getUniqueId().equals(infoData.getProfile().getUUID()))
if (infoData.getGameMode() != EnumWrappers.NativeGameMode.SPECTATOR) {
int latency;
try {
latency = infoData.getLatency();
} catch (NoSuchMethodError e) {
latency = 21;
}
if (event.getPacket().getPlayerInfoAction().read(0)
!= EnumWrappers.PlayerInfoAction.UPDATE_GAME_MODE) {
if (!isGameModeUpdate(event))
break;
if (extractUUID(infoData) == null)
continue;
if (!VanishIndication.this.plugin.getVisibilityChanger().getHider()
.isHidden(extractUUID(infoData), receiver)
&& VanishIndication.this.plugin.getVanishStateMgr()
.isVanished(extractUUID(infoData)))
{
if (!receiver.getUniqueId().equals(extractUUID(infoData)))
if (infoData.getGameMode() != EnumWrappers.NativeGameMode.SPECTATOR) {
PlayerInfoData newData = createGameModeUpdate(infoData,
EnumWrappers.NativeGameMode.SPECTATOR);
int index = infoDataList.indexOf(infoData);
infoDataList.set(index, newData);
}
PlayerInfoData newData = new PlayerInfoData(infoData.getProfile(),
latency, EnumWrappers.NativeGameMode.SPECTATOR,
infoData.getDisplayName());
infoDataList.remove(infoData);
infoDataList.add(newData);
}
}
} catch (UnsupportedOperationException ignored) {
}
}
event.getPacket().getPlayerInfoDataLists().write(0, infoDataList);
} catch (Exception | NoClassDefFoundError e) {
if (!suppressErrors) {
VanishIndication.this.plugin.logException(e);
plugin.getLogger().warning("IMPORTANT: Please make sure that you are using the latest " +
"dev-build of ProtocolLib and that your server is up-to-date! This error likely " +
"happened inside of ProtocolLib code which is out of SuperVanish's control. It's part " +
"of an optional feature module and can be removed safely by disabling " +
"MarkVanishedPlayersAsSpectators in the config file. Please report this " +
"error if you can reproduce it on an up-to-date server with only latest " +
"ProtocolLib and latest SV installed.");
plugin.getLogger().warning("IMPORTANT: Please make sure that you are using the latest "
+ "dev-build of ProtocolLib and that your server is up-to-date! This error likely "
+ "happened inside of ProtocolLib code which is out of SuperVanish's control. It's part "
+ "of an optional feature module and can be removed safely by disabling "
+ "MarkVanishedPlayersAsSpectators in the config file. Please report this "
+ "error if you can reproduce it on an up-to-date server with only latest "
+ "ProtocolLib and latest SV installed.");
suppressErrors = true;
}
}
}
});
}
private void sendPlayerInfoChangeGameModePacket(Player p, Player change, boolean spectator) {
PacketContainer packet = new PacketContainer(PLAYER_INFO);
if (plugin.getVersionUtil().isOneDotXOrHigher(19)) {
packet.getPlayerInfoActions().write(0, EnumSet.of(EnumWrappers.PlayerInfoAction.UPDATE_GAME_MODE));
} else {
packet.getPlayerInfoAction().write(0, EnumWrappers.PlayerInfoAction.UPDATE_GAME_MODE);
}
List<PlayerInfoData> data = new ArrayList<>();
int ping = ThreadLocalRandom.current().nextInt(20) + 15;
data.add(new PlayerInfoData(WrappedGameProfile.fromPlayer(change), ping,
spectator ? EnumWrappers.NativeGameMode.SPECTATOR
: EnumWrappers.NativeGameMode.fromBukkit(change.getGameMode()),
WrappedChatComponent.fromText(change.getPlayerListName())));
EnumWrappers.NativeGameMode gameMode = spectator ? EnumWrappers.NativeGameMode.SPECTATOR
: EnumWrappers.NativeGameMode.fromBukkit(change.getGameMode());
WrappedChatComponent displayName = WrappedChatComponent
.fromText(LegacyComponentSerializer.legacySection().serialize(change.playerListName()));
WrappedGameProfile profile = WrappedGameProfile.fromPlayer(change);
if (plugin.getVersionUtil().isOneDotXOrHigher(19)) {
data.add(new PlayerInfoData(change.getUniqueId(), ping, true, gameMode, profile, displayName,
(WrappedRemoteChatSessionData) null));
} else {
data.add(new PlayerInfoData(profile, ping, gameMode, displayName));
}
packet.getPlayerInfoDataLists().write(0, data);
try {
ProtocolLibrary.getProtocolManager().sendServerPacket(p, packet);
} catch (InvocationTargetException e) {
} catch (Exception e) {
throw new RuntimeException("Cannot send packet", e);
}
}
private boolean isGameModeUpdate(PacketEvent event) {
try {
Set<EnumWrappers.PlayerInfoAction> actions = event.getPacket().getPlayerInfoActions().read(0);
if (actions != null)
return actions.contains(EnumWrappers.PlayerInfoAction.UPDATE_GAME_MODE);
} catch (FieldAccessException | IllegalArgumentException ignored) {
// legacy single-action packet on pre-1.19.3
}
try {
EnumWrappers.PlayerInfoAction action = event.getPacket().getPlayerInfoAction().read(0);
return action == EnumWrappers.PlayerInfoAction.UPDATE_GAME_MODE;
} catch (FieldAccessException | IllegalArgumentException ignored) {
return false;
}
}
private UUID extractUUID(PlayerInfoData infoData) {
UUID uuid = infoData.getProfileId();
if (uuid != null)
return uuid;
if (infoData.getProfile() != null)
return infoData.getProfile().getUUID();
return null;
}
private PlayerInfoData createGameModeUpdate(PlayerInfoData infoData, EnumWrappers.NativeGameMode gameMode) {
UUID uuid = extractUUID(infoData);
if (plugin.getVersionUtil().isOneDotXOrHigher(19)) {
return new PlayerInfoData(uuid, infoData.getLatency(), infoData.isListed(), gameMode, infoData.getProfile(),
infoData.getDisplayName(), infoData.getRemoteChatSessionData());
}
return new PlayerInfoData(infoData.getProfile(), infoData.getLatency(), gameMode, infoData.getDisplayName());
}
}
@@ -26,44 +26,62 @@ import java.util.UUID;
public class CitizensHook extends PluginHook {
public CitizensHook(SuperVanish superVanish) {
super(superVanish);
}
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void onNPCTarget(NPCSpeechEvent e) {
e.setCancelled(!checkContext(e.getContext()));
}
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void onSpeechTargeted(SpeechEvent e) {
e.setCancelled(!checkContext(e.getContext()));
}
/**
* @return FALSE if the event should be cancelled, TRUE otherwise
*/
private boolean checkContext(SpeechContext context) {
// don't let it talk about hidden players
for (UUID uuid : superVanish.getVanishStateMgr().getOnlineVanishedPlayers()) {
if (context.getMessage().contains(Bukkit.getPlayer(uuid).getName())) {
return false;
}
}
// don't let hidden players receive it
Iterator<Talkable> iterator = context.iterator();
while (iterator.hasNext()) {
Talkable recipient = iterator.next();
Entity entity = recipient.getEntity();
if (!(entity instanceof Player))
continue;
Player player = (Player) entity;
if (superVanish.getVanishStateMgr().isVanished(player.getUniqueId())) {
iterator.remove();
}
}
// cancel contexts with only hidden recipients
return context.hasRecipients();
}
}
@@ -24,52 +24,64 @@ public class DynmapHook extends PluginHook {
private final SuperVanish superVanish;
public DynmapHook(SuperVanish superVanish) {
super(superVanish);
this.superVanish = superVanish;
sendJoinLeave
= superVanish.getSettings().getBoolean("HookOptions.DynmapSendJoinLeaveMessages")
sendJoinLeave = superVanish.getSettings().getBoolean("HookOptions.DynmapSendJoinLeaveMessages")
&& !superVanish.getMessage("DynmapFakeJoin").equals("");
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onVanish(PlayerHideEvent e) {
Player p = e.getPlayer();
DynmapAPI dynmap = (DynmapAPI) plugin;
dynmap.setPlayerVisiblity(p, false);
if (sendJoinLeave)
dynmap.sendBroadcastToWeb("",
superVanish.replacePlaceholders(superVanish.getMessage("DynmapFakeQuit"), p));
dynmap.sendBroadcastToWeb("", superVanish.replacePlaceholders(superVanish.getMessage("DynmapFakeQuit"), p));
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onReappear(PlayerShowEvent e) {
Player p = e.getPlayer();
DynmapAPI dynmap = (DynmapAPI) plugin;
dynmap.setPlayerVisiblity(p, true);
if (sendJoinLeave)
dynmap.sendBroadcastToWeb("",
superVanish.replacePlaceholders(superVanish.getMessage("DynmapFakeJoin"), p));
dynmap.sendBroadcastToWeb("", superVanish.replacePlaceholders(superVanish.getMessage("DynmapFakeJoin"), p));
}
@EventHandler(priority = EventPriority.LOWEST)
public void onJoin(PlayerJoinEvent e) {
Player p = e.getPlayer();
DynmapAPI dynmap = (DynmapAPI) plugin;
if (superVanish.getVanishStateMgr().isVanished(p.getUniqueId())) {
dynmap.setPlayerVisiblity(p, false);
}
}
@EventHandler(priority = EventPriority.MONITOR)
public void onQuit(PlayerQuitEvent e) {
Player p = e.getPlayer();
DynmapAPI dynmap = (DynmapAPI) plugin;
if (superVanish.getVanishStateMgr().isVanished(p.getUniqueId())) {
dynmap.setPlayerVisiblity(p, true);
}
}
}
@@ -37,87 +37,129 @@ public class EssentialsHook extends PluginHook {
@Override
public void run() {
try {
if (!Bukkit.getPluginManager().isPluginEnabled("Essentials")) return;
if (!Bukkit.getPluginManager().isPluginEnabled("Essentials"))
return;
for (UUID uuid : superVanish.getVanishStateMgr().getOnlineVanishedPlayers()) {
Player p = Bukkit.getPlayer(uuid);
User user = essentials.getUser(p);
if (user == null) continue;
if (user == null)
continue;
if (!user.isHidden())
user.setHidden(true);
}
} catch (Exception e) {
cancel();
superVanish.logException(e);
}
}
};
private BukkitTask forcedInvisibilityTask;
public EssentialsHook(SuperVanish superVanish) {
super(superVanish);
}
@Override
public void onPluginEnable(Plugin plugin) {
essentials = (Essentials) plugin;
forcedInvisibilityTask = forcedInvisibilityRunnable.runTaskTimer(superVanish, 0, 100);
forcedInvisibilityRunnable.run();
}
@Override
public void onPluginDisable(Plugin plugin) {
essentials = null;
forcedInvisibilityTask.cancel();
}
@EventHandler(priority = EventPriority.LOW)
public void onJoin(PlayerJoinEvent e) {
User user = essentials.getUser(e.getPlayer());
if (user == null) return;
if (user == null)
return;
if (superVanish.getVanishStateMgr().isVanished(e.getPlayer().getUniqueId()) && !user.isHidden())
user.setHidden(true);
else user.setHidden(false);
else
user.setHidden(false);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onVanish(PlayerHideEvent e) {
User user = essentials.getUser(e.getPlayer());
if (user == null) return;
if (user.isVanished()) user.setVanished(false);
if (user == null)
return;
if (user.isVanished())
user.setVanished(false);
preVanishHiddenPlayers.remove(e.getPlayer().getUniqueId());
user.setHidden(true);
}
@EventHandler
public void onReappear(PostPlayerShowEvent e) {
User user = essentials.getUser(e.getPlayer());
if (user == null) return;
if (user == null)
return;
user.setHidden(false);
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onCommand(final PlayerCommandPreprocessEvent e) {
if (!CommandAction.VANISH_SELF.checkPermission(e.getPlayer(), superVanish)) return;
if (superVanish.getVanishStateMgr().isVanished(e.getPlayer().getUniqueId())) return;
if (!CommandAction.VANISH_SELF.checkPermission(e.getPlayer(), superVanish))
return;
if (superVanish.getVanishStateMgr().isVanished(e.getPlayer().getUniqueId()))
return;
String command = e.getMessage().toLowerCase(Locale.ENGLISH).split(" ")[0].replace("/", "")
.toLowerCase(Locale.ENGLISH);
if (command.split(":").length > 1) command = command.split(":")[1];
if (command.equals("supervanish") || command.equals("sv")
|| command.equals("v") || command.equals("vanish")) {
if (command.split(":").length > 1)
command = command.split(":")[1];
if (command.equals("supervanish") || command.equals("sv") || command.equals("v") || command.equals("vanish")) {
final User user = essentials.getUser(e.getPlayer());
if (user == null || !user.isAfk()) return;
if (user == null || !user.isAfk())
return;
user.setHidden(true);
preVanishHiddenPlayers.add(e.getPlayer().getUniqueId());
superVanish.getServer().getScheduler().runTaskLater(superVanish, new Runnable() {
@Override
public void run() {
if (preVanishHiddenPlayers.remove(e.getPlayer().getUniqueId())) {
user.setHidden(false);
}
}
}, 1);
}
}
}
@@ -10,14 +10,22 @@ package de.myzelyam.supervanish.hooks;
public class InvalidPluginHookException extends RuntimeException {
private static final long serialVersionUID = 1L;
public InvalidPluginHookException() {
}
public InvalidPluginHookException(String message) {
super(message);
}
public InvalidPluginHookException(Throwable cause) {
super(cause);
}
}
@@ -1,143 +0,0 @@
/*
* Copyright © 2015, Leon Mangler and the SuperVanish contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package de.myzelyam.supervanish.hooks;
import de.myzelyam.supervanish.SuperVanish;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.UUID;
import be.maximvdw.placeholderapi.PlaceholderAPI;
import be.maximvdw.placeholderapi.PlaceholderReplaceEvent;
import be.maximvdw.placeholderapi.PlaceholderReplacer;
public class MVdWPlaceholderAPIHook extends PluginHook {
private final String yes, no, prefix, suffix;
public MVdWPlaceholderAPIHook(SuperVanish superVanish) {
super(superVanish);
yes = superVanish.getMessage("PlaceholderIsVanishedYes");
no = superVanish.getMessage("PlaceholderIsVanishedNo");
prefix = superVanish.getMessage("PlaceholderVanishPrefix");
suffix = superVanish.getMessage("PlaceholderVanishSuffix");
}
@Override
public void onPluginEnable(Plugin plugin) {
PlaceholderAPI.registerPlaceholder(superVanish, "supervanish_isvanished",
e -> {
try {
Player p = e.getPlayer();
if (p == null) return no;
try {
return superVanish.getVanishStateMgr().isVanished(p.getUniqueId()) ? yes
: no;
} catch (Exception er) {
superVanish.logException(er);
}
return null;
} catch (Throwable t) {
if (!(t instanceof NoClassDefFoundError || t instanceof
ConcurrentModificationException))
superVanish.logException(t);
return no;
}
});
PlaceholderAPI.registerPlaceholder(superVanish, "supervanish_vanishprefix",
e -> {
try {
Player p = e.getPlayer();
if (p == null) return "";
try {
return superVanish.getVanishStateMgr().isVanished(p.getUniqueId()) ? prefix : "";
} catch (Exception er) {
superVanish.logException(er);
}
return null;
} catch (Throwable t) {
if (!(t instanceof NoClassDefFoundError || t instanceof
ConcurrentModificationException))
superVanish.logException(t);
return "";
}
});
PlaceholderAPI.registerPlaceholder(superVanish, "supervanish_vanishsuffix",
e -> {
try {
Player p = e.getPlayer();
if (p == null) return "";
try {
return superVanish.getVanishStateMgr().isVanished(p.getUniqueId()) ? suffix : "";
} catch (Exception er) {
superVanish.logException(er);
}
return null;
} catch (Throwable t) {
if (!(t instanceof NoClassDefFoundError || t instanceof
ConcurrentModificationException))
superVanish.logException(t);
return "";
}
});
PlaceholderAPI.registerPlaceholder(superVanish, "supervanish_vanishedplayers",
e -> {
try {
Player p = e.getPlayer();
Collection<UUID> onlineVanishedPlayers = superVanish.getVanishStateMgr()
.getOnlineVanishedPlayers();
String playerListMessage = "";
for (UUID uuid : onlineVanishedPlayers) {
Player onlineVanished = Bukkit.getPlayer(uuid);
if (onlineVanished == null) continue;
if (p != null && superVanish.getSettings().getBoolean(
"IndicationFeatures.LayeredPermissions.HideInvisibleInCommands", false)
&& !superVanish.hasPermissionToSee(p, onlineVanished)) {
continue;
}
playerListMessage = playerListMessage + onlineVanished.getName() + ", ";
}
return playerListMessage.length() > 3
? playerListMessage.substring(0, playerListMessage.length() - 2)
: playerListMessage;
} catch (Throwable t) {
if (!(t instanceof NoClassDefFoundError || t instanceof
ConcurrentModificationException))
superVanish.logException(t);
return "";
}
});
PlaceholderAPI.registerPlaceholder(superVanish, "supervanish_playercount",
e -> {
try {
Player p = e.getPlayer();
int playercount = Bukkit.getOnlinePlayers().size();
for (UUID uuid : superVanish.getVanishStateMgr()
.getOnlineVanishedPlayers()) {
Player onlineVanished = Bukkit.getPlayer(uuid);
if (onlineVanished == null) continue;
if (p == null || !superVanish.canSee(p, onlineVanished))
playercount--;
}
return playercount + "";
} catch (Throwable t) {
if (!(t instanceof NoClassDefFoundError || t instanceof
ConcurrentModificationException))
superVanish.logException(t);
return Bukkit.getOnlinePlayers().size() + "";
}
});
}
}
@@ -0,0 +1,152 @@
/*
* Copyright © 2015, Leon Mangler and the SuperVanish contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package de.myzelyam.supervanish.hooks;
import de.myzelyam.supervanish.SuperVanish;
import io.github.miniplaceholders.api.Expansion;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.tag.Tag;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import java.util.Collection;
import java.util.UUID;
public class MiniPlaceholdersHook extends PluginHook {
private static final LegacyComponentSerializer LEGACY_SERIALIZER = LegacyComponentSerializer.legacyAmpersand();
private final String yes, no, prefix, suffix;
private Expansion expansion;
public MiniPlaceholdersHook(SuperVanish superVanish) {
super(superVanish);
yes = superVanish.getMessage("PlaceholderIsVanishedYes");
no = superVanish.getMessage("PlaceholderIsVanishedNo");
prefix = superVanish.getMessage("PlaceholderVanishPrefix");
suffix = superVanish.getMessage("PlaceholderVanishSuffix");
}
@Override
public void onPluginEnable(Plugin plugin) {
expansion = Expansion.builder("supervanish")
.audiencePlaceholder("isvanished", (audience, queue, ctx) -> textTag(isVanishedText(audience)))
.audiencePlaceholder("vanishprefix",
(audience, queue, ctx) -> componentTag(isVanished(audience) ? prefix : ""))
.audiencePlaceholder("vanishsuffix",
(audience, queue, ctx) -> componentTag(isVanished(audience) ? suffix : ""))
.audiencePlaceholder("vanishedplayers", (audience, queue, ctx) -> textTag(getVanishedPlayers(audience)))
.audiencePlaceholder("playercount",
(audience, queue, ctx) -> textTag(String.valueOf(getPlayerCount(audience))))
.build();
expansion.register();
}
@Override
public void onPluginDisable(Plugin plugin) {
if (expansion != null && expansion.registered()) {
expansion.unregister();
}
expansion = null;
}
private String isVanishedText(Object audience) {
return isVanished(audience) ? yes : no;
}
private boolean isVanished(Object audience) {
if (!(audience instanceof Player))
return false;
return superVanish.getVanishStateMgr().isVanished(((Player) audience).getUniqueId());
}
private String getVanishedPlayers(Object audience) {
Player viewer = audience instanceof Player ? (Player) audience : null;
Collection<UUID> onlineVanishedPlayers = superVanish.getVanishStateMgr().getOnlineVanishedPlayers();
StringBuilder playerListMessage = new StringBuilder();
for (UUID uuid : onlineVanishedPlayers) {
Player onlineVanished = Bukkit.getPlayer(uuid);
if (onlineVanished == null)
continue;
if (viewer != null
&& superVanish.getSettings()
.getBoolean("IndicationFeatures.LayeredPermissions.HideInvisibleInCommands", false)
&& !superVanish.hasPermissionToSee(viewer, onlineVanished))
{
continue;
}
if (playerListMessage.length() > 0) {
playerListMessage.append(", ");
}
playerListMessage.append(onlineVanished.getName());
}
return playerListMessage.toString();
}
private int getPlayerCount(Object audience) {
Player viewer = audience instanceof Player ? (Player) audience : null;
int playerCount = Bukkit.getOnlinePlayers().size();
for (UUID uuid : superVanish.getVanishStateMgr().getOnlineVanishedPlayers()) {
Player onlineVanished = Bukkit.getPlayer(uuid);
if (onlineVanished == null)
continue;
if (viewer == null || !superVanish.canSee(viewer, onlineVanished)) {
playerCount--;
}
}
return playerCount;
}
private Tag textTag(String value) {
return Tag.selfClosingInserting(Component.text(value));
}
private Tag componentTag(String value) {
return Tag.selfClosingInserting(LEGACY_SERIALIZER.deserialize(value));
}
}
@@ -1,6 +1,5 @@
package de.myzelyam.supervanish.hooks;
import com.lishid.openinv.IOpenInv;
import de.myzelyam.api.vanish.PlayerShowEvent;
import de.myzelyam.api.vanish.PostPlayerHideEvent;
import de.myzelyam.supervanish.SuperVanish;
@@ -9,7 +8,9 @@ import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.inventory.Inventory;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
@@ -21,98 +22,174 @@ public class OpenInvHook extends PluginHook {
private final Set<UUID> alreadyHiddenBeforeVanishing = new HashSet<>();
public OpenInvHook(SuperVanish superVanish) {
super(superVanish);
}
@EventHandler
public void onVanish(PostPlayerHideEvent e) {
try {
Player p = e.getPlayer();
IOpenInv openInv = (IOpenInv) plugin;
if (openInv.getSilentContainerStatus(p)) {
try {
Player p = e.getPlayer();
if (getSilentContainerStatus(p)) {
alreadyHiddenBeforeVanishing.add(p.getUniqueId());
} else {
if (!p.hasPermission("sv.silentchest")) return;
openInv.setSilentContainerStatus(p, true);
if (!p.hasPermission("sv.silentchest"))
return;
setSilentContainerStatus(p, true);
}
} catch (Exception | NoSuchMethodError | NoClassDefFoundError er) {
if (!errorLogged) {
superVanish.logException(er);
errorLogged = true;
}
}
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onReappear(PlayerShowEvent e) {
try {
Player p = e.getPlayer();
IOpenInv openInv = (IOpenInv) plugin;
if (!alreadyHiddenBeforeVanishing.remove(p.getUniqueId())) {
openInv.setSilentContainerStatus(p, false);
setSilentContainerStatus(p, false);
}
} catch (Exception | NoSuchMethodError | NoClassDefFoundError er) {
if (!errorLogged) {
superVanish.logException(er);
errorLogged = true;
}
}
}
@EventHandler(priority = EventPriority.LOW)
public void onJoin(PlayerJoinEvent e) {
try {
Player p = e.getPlayer();
IOpenInv openInv = (IOpenInv) plugin;
if (superVanish.getVanishStateMgr().isVanished(p.getUniqueId())) {
if (openInv.getSilentContainerStatus(p)) {
if (getSilentContainerStatus(p)) {
alreadyHiddenBeforeVanishing.add(p.getUniqueId());
} else {
if (!p.hasPermission("sv.silentchest")) return;
openInv.setSilentContainerStatus(p, true);
if (!p.hasPermission("sv.silentchest"))
return;
setSilentContainerStatus(p, true);
}
}
} catch (Exception | NoSuchMethodError | NoClassDefFoundError er) {
if (!errorLogged) {
superVanish.logException(er);
errorLogged = true;
}
}
}
@EventHandler(priority = EventPriority.MONITOR)
public void onQuit(PlayerQuitEvent e) {
try {
Player p = e.getPlayer();
IOpenInv openInv = (IOpenInv) plugin;
if (superVanish.getVanishStateMgr().isVanished(p.getUniqueId())) {
if (!alreadyHiddenBeforeVanishing.remove(p.getUniqueId())) {
openInv.setSilentContainerStatus(p, false);
setSilentContainerStatus(p, false);
}
}
} catch (Exception | NoSuchMethodError | NoClassDefFoundError er) {
if (!errorLogged) {
superVanish.logException(er);
errorLogged = true;
}
}
}
public boolean openPlayerInventory(Player vanished, Player target) {
IOpenInv openInv = (IOpenInv) plugin;
try {
openInv.openInventory(vanished, openInv.getSpecialInventory(target, true));
Method getSpecialInventoryMethod = plugin.getClass().getMethod("getSpecialInventory", Player.class,
boolean.class);
Inventory specialInventory = (Inventory) getSpecialInventoryMethod.invoke(plugin, target, true);
Method openInventoryMethod = plugin.getClass().getMethod("openInventory", Player.class, Inventory.class);
openInventoryMethod.invoke(plugin, vanished, specialInventory);
} catch (Exception | NoSuchMethodError | NoClassDefFoundError e) {
if (!errorLogged) {
superVanish.logException(e);
errorLogged = true;
}
return false;
}
return true;
}
private boolean getSilentContainerStatus(Player player) throws ReflectiveOperationException {
Method method = plugin.getClass().getMethod("getSilentContainerStatus", Player.class);
return (Boolean) method.invoke(plugin, player);
}
private void setSilentContainerStatus(Player player, boolean status) throws ReflectiveOperationException {
Method method = plugin.getClass().getMethod("setSilentContainerStatus", Player.class, boolean.class);
method.invoke(plugin, player, status);
}
}
@@ -1,124 +0,0 @@
/*
* Copyright © 2015, Leon Mangler and the SuperVanish contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package de.myzelyam.supervanish.hooks;
import de.myzelyam.supervanish.SuperVanish;
import me.clip.placeholderapi.PlaceholderAPI;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import java.util.Collection;
import java.util.UUID;
public class PlaceholderAPIHook extends PluginHook {
private final String yes, no, prefix, suffix;
public PlaceholderAPIHook(SuperVanish superVanish) {
super(superVanish);
yes = superVanish.getMessage("PlaceholderIsVanishedYes");
no = superVanish.getMessage("PlaceholderIsVanishedNo");
prefix = superVanish.getMessage("PlaceholderVanishPrefix");
suffix = superVanish.getMessage("PlaceholderVanishSuffix");
new SVPlaceholderExpansion().register();
}
public static String translatePlaceholders(String msg, Player p) {
return PlaceholderAPI.setPlaceholders((OfflinePlayer) p, msg);
}
public class SVPlaceholderExpansion extends PlaceholderExpansion {
@Override
public boolean persist() {
return true;
}
@Override
public boolean canRegister() {
return true;
}
@Override
public String getAuthor() {
return superVanish.getDescription().getAuthors().toString();
}
@Override
public String getIdentifier() {
return "supervanish";
}
@Override
public String getVersion() {
return superVanish.getDescription().getVersion();
}
@Override
public String onRequest(OfflinePlayer op, String id) {
try {
Player p;
if (op instanceof Player)
p = (Player) op;
else
p = null;
if (id.equalsIgnoreCase("isvanished")
|| id.equalsIgnoreCase("isinvisible")
|| id.equalsIgnoreCase("vanished")
|| id.equalsIgnoreCase("invisible"))
return p != null && superVanish.getVanishStateMgr().isVanished(p.getUniqueId())
? yes : no;
if (id.equalsIgnoreCase("vanishprefix"))
return p != null && superVanish.getVanishStateMgr().isVanished(p.getUniqueId())
? prefix : "";
if (id.equalsIgnoreCase("vanishsuffix"))
return p != null && superVanish.getVanishStateMgr().isVanished(p.getUniqueId())
? suffix : "";
if (id.equalsIgnoreCase("onlinevanishedplayers")
|| id.equalsIgnoreCase("onlinevanished")
|| id.equalsIgnoreCase("invisibleplayers")
|| id.equalsIgnoreCase("vanishedplayers")
|| id.equalsIgnoreCase("hiddenplayers")) {
Collection<UUID> onlineVanishedPlayers = superVanish.getVanishStateMgr()
.getOnlineVanishedPlayers();
String playerListMessage = "";
for (UUID uuid : onlineVanishedPlayers) {
Player onlineVanished = Bukkit.getPlayer(uuid);
if (onlineVanished == null) continue;
if (superVanish.getSettings().getBoolean(
"IndicationFeatures.LayeredPermissions.HideInvisibleInCommands", false)
&& !superVanish.hasPermissionToSee(p, onlineVanished)) {
continue;
}
playerListMessage = playerListMessage + onlineVanished.getName() + ", ";
}
return playerListMessage.length() > 3
? playerListMessage.substring(0, playerListMessage.length() - 2)
: playerListMessage;
}
if (id.equalsIgnoreCase("playercount")
|| id.equalsIgnoreCase("onlineplayers")) {
int playercount = Bukkit.getOnlinePlayers().size();
for (UUID uuid : superVanish.getVanishStateMgr()
.getOnlineVanishedPlayers()) {
Player onlineVanished = Bukkit.getPlayer(uuid);
if (onlineVanished == null) continue;
if (p == null || !superVanish.canSee(p, onlineVanished)) playercount--;
}
return playercount + "";
}
} catch (Exception e) {
superVanish.logException(e);
}
return null;
}
}
}
@@ -14,8 +14,8 @@ import org.bukkit.event.Listener;
import org.bukkit.plugin.Plugin;
/**
* Represents a hook into a plugin to support its functionality or make it support SuperVanish's
* functionality
* Represents a hook into a plugin to support its functionality or make it
* support SuperVanish's functionality
*/
public abstract class PluginHook implements Listener {
@@ -23,20 +23,29 @@ public abstract class PluginHook implements Listener {
protected Plugin plugin;
public PluginHook(SuperVanish superVanish) {
this.superVanish = superVanish;
}
public void onPluginEnable(Plugin plugin) {
}
public void onPluginDisable(Plugin plugin) {
}
public Plugin getPlugin() {
return plugin;
}
void setPlugin(Plugin plugin) {
this.plugin = plugin;
}
}
@@ -25,98 +25,140 @@ import java.util.logging.Level;
public class PluginHookMgr implements Listener {
private static final Map<String, Class<? extends PluginHook>> REGISTERED_HOOKS
= new HashMap<String, Class<? extends PluginHook>>() {{
private static final Map<String, Class<? extends PluginHook>> REGISTERED_HOOKS = new HashMap<String, Class<? extends PluginHook>>() {
{
put("Essentials", EssentialsHook.class);
put("Citizens", CitizensHook.class);
put("PlaceholderAPI", PlaceholderAPIHook.class);
put("Utilities-OG", MiniPlaceholdersHook.class);
put("dynmap", DynmapHook.class);
put("TrailGUI", TrailGUIHook.class);
put("MVdWPlaceholderAPI", MVdWPlaceholderAPIHook.class);
put("OpenInv", OpenInvHook.class);
}};
}
};
private final SuperVanish plugin;
private Set<PluginHook> activeHooks = new HashSet<>();
public PluginHookMgr(SuperVanish plugin) {
this.plugin = plugin;
plugin.getServer().getPluginManager().registerEvents(this, plugin);
for (Plugin alreadyEnabledPlugin : Bukkit.getPluginManager().getPlugins())
if (alreadyEnabledPlugin.isEnabled())
onPluginEnable(new PluginEnableEvent(alreadyEnabledPlugin));
}
@EventHandler
public void onPluginEnable(PluginEnableEvent e) {
Plugin plugin = e.getPlugin();
if (REGISTERED_HOOKS.get(plugin.getName()) == null) return;
if (isHookDisabled(plugin.getName())) return;
if (REGISTERED_HOOKS.get(plugin.getName()) == null)
return;
if (isHookDisabled(plugin.getName()))
return;
PluginHook hook = null;
try {
hook = REGISTERED_HOOKS.get(plugin.getName()).getConstructor(SuperVanish.class).
newInstance(PluginHookMgr.this.plugin);
hook = REGISTERED_HOOKS.get(plugin.getName()).getConstructor(SuperVanish.class)
.newInstance(PluginHookMgr.this.plugin);
hook.setPlugin(plugin);
hook.onPluginEnable(plugin);
Bukkit.getPluginManager().registerEvents(hook, plugin);
activeHooks.add(hook);
PluginHookMgr.this.plugin.log(
Level.INFO, "Hooked into " + plugin.getName());
PluginHookMgr.this.plugin.log(Level.INFO, "Hooked into " + plugin.getName());
} catch (NoClassDefFoundError er) {
Bukkit.getLogger().warning("NoClassDefFoundError for SV-Hook(v"
+ this.plugin.getDescription().getVersion() + ") "
+ (hook != null ? hook.getClass().getSimpleName() : "?") + " of plugin "
+ plugin.getName() + " v" + plugin.getDescription().getVersion()
+ ", please report this if you are using the latest version of that" +
" plugin!");
Bukkit.getLogger()
.warning("NoClassDefFoundError for SV-Hook(v" + this.plugin.getPluginMeta().getVersion() + ") "
+ (hook != null ? hook.getClass().getSimpleName() : "?") + " of plugin " + plugin.getName()
+ " v" + plugin.getPluginMeta().getVersion()
+ ", please report this if you are using the latest version of that" + " plugin!");
} catch (Exception er) {
if (er.getMessage() != null
&& er.getMessage().contains("Unable to find handler list for event")) {
this.plugin.log(Level.WARNING, "" + er.getMessage()
+ "; This is not an issue with SuperVanish");
if (er.getMessage() != null && er.getMessage().contains("Unable to find handler list for event")) {
this.plugin.log(Level.WARNING, "" + er.getMessage() + "; This is not an issue with SuperVanish");
return;
} else if (er.getCause() != null && er.getCause().getMessage() != null && er.getCause()
.getMessage().contains("Unable to find handler list for event ")) {
this.plugin.log(Level.WARNING, "" + er.getCause()
.getMessage() + "; This is not an issue with SuperVanish");
} else if (er.getCause() != null && er.getCause().getMessage() != null
&& er.getCause().getMessage().contains("Unable to find handler list for event "))
{
this.plugin.log(Level.WARNING,
"" + er.getCause().getMessage() + "; This is not an issue with SuperVanish");
return;
}
this.plugin.logException(new InvalidPluginHookException(er));
this.plugin.log(Level.WARNING, "Affected by this error is only the "
+ plugin.getName() + "-Hook, all other hooks and features aren't affected.");
this.plugin.log(Level.WARNING, "Affected by this error is only the " + plugin.getName()
+ "-Hook, all other hooks and features aren't affected.");
}
}
private boolean isHookDisabled(String pluginName) {
if (pluginName.equalsIgnoreCase("dynmap"))
return !plugin.getSettings().getBoolean("HookOptions.EnableDynmapHook");
if (pluginName.equalsIgnoreCase("Utilities-OG"))
return !plugin.getSettings().getBoolean("HookOptions.EnableUtilitiesOGHook", true);
return !plugin.getSettings().getBoolean("HookOptions.Enable" + pluginName + "Hook", true);
}
@EventHandler
public void onPluginDisable(PluginDisableEvent e) {
Plugin plugin = e.getPlugin();
PluginHook hook = getActiveHook(plugin);
if (hook == null) return;
if (hook == null)
return;
try {
hook.onPluginDisable(plugin);
} catch (Exception e1) {
this.plugin.logException(new InvalidPluginHookException(e1));
}
hook.setPlugin(null);
activeHooks.remove(hook);
}
private PluginHook getActiveHook(Plugin plugin) {
for (PluginHook hook : activeHooks) if (hook.getPlugin() == plugin) return hook;
for (PluginHook hook : activeHooks)
if (hook.getPlugin() == plugin)
return hook;
return null;
}
public boolean isHookActive(Class<? extends PluginHook> hookClass) {
for (PluginHook hook : activeHooks) {
if (hook.getClass().equals(hookClass)) {
return true;
}
}
return false;
}
}
@@ -23,28 +23,37 @@ import ca.jamiesinn.trailgui.TrailGUI;
public class TrailGUIHook extends PluginHook {
public TrailGUIHook(SuperVanish superVanish) {
super(superVanish);
}
@Override
public void onPluginEnable(Plugin plugin) {
if (!(plugin instanceof TrailGUI)) {
superVanish.log(Level.WARNING,
"Failed to hook into TrailGUI. (PluginNotFound)");
superVanish.log(Level.WARNING, "Failed to hook into TrailGUI. (PluginNotFound)");
return;
}
PlayerMoveEvent.getHandlerList().unregister(plugin);
final Listeners trailGUIListeners = new Listeners((TrailGUI) plugin);
plugin.getServer().getPluginManager().registerEvents(new Listener() {
@EventHandler
public void onPlayerMove(PlayerMoveEvent event) {
if (superVanish.getVanishStateMgr()
.getOnlineVanishedPlayers().contains(
event.getPlayer().getUniqueId()))
if (superVanish.getVanishStateMgr().getOnlineVanishedPlayers()
.contains(event.getPlayer().getUniqueId()))
return;
trailGUIListeners.onPlayerMove(event);
}
}, plugin);
}
}
@@ -10,7 +10,6 @@ package de.myzelyam.supervanish.listeners;
import de.myzelyam.supervanish.SuperVanish;
import de.myzelyam.supervanish.VanishPlayer;
import de.myzelyam.supervanish.features.Broadcast;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
@@ -19,8 +18,6 @@ import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.entity.*;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerPickupItemEvent;
public class GeneralListener implements Listener {
@@ -29,110 +26,158 @@ public class GeneralListener implements Listener {
private final FileConfiguration config;
public GeneralListener(SuperVanish plugin) {
this.plugin = plugin;
config = plugin.getSettings();
}
@EventHandler(priority = EventPriority.HIGH)
public void onDamage(EntityDamageByEntityEvent e) {
try {
if (!(e.getDamager() instanceof Player)) return;
if (e.getEntity() == null) return;
if (!(e.getDamager() instanceof Player))
return;
if (e.getEntity() == null)
return;
Player p = (Player) e.getDamager();
if (plugin.getVanishStateMgr().isVanished(p.getUniqueId())) {
if (config.getBoolean("RestrictiveOptions.PreventHittingEntities")
&& !p.hasPermission("sv.damageentities") && !p.hasPermission("sv.damage")) {
&& !p.hasPermission("sv.damageentities") && !p.hasPermission("sv.damage"))
{
plugin.sendMessage(p, "EntityHitDenied", p);
e.setCancelled(true);
}
}
} catch (Exception er) {
plugin.logException(er);
}
}
@EventHandler(priority = EventPriority.HIGH)
public void onPlayerDeath(PlayerDeathEvent e) {
try {
Player p = e.getEntity();
if (plugin.getVanishStateMgr().isVanished(p.getUniqueId())) {
String deathMessage = e.getDeathMessage();
e.setDeathMessage(null);
if (deathMessage != null)
Broadcast.announceSilentDeath(p, plugin, deathMessage);
}
} catch (Exception er) {
plugin.logException(er);
}
}
@EventHandler(priority = EventPriority.HIGH)
public void onFoodLevelChange(FoodLevelChangeEvent e) {
try {
if (e.getEntity() instanceof Player && config.getBoolean("InvisibilityFeatures.DisableHunger")) {
Player p = (Player) e.getEntity();
if (plugin.getVanishStateMgr().isVanished(p.getUniqueId())
&& e.getFoodLevel() <= p.getFoodLevel())
if (plugin.getVanishStateMgr().isVanished(p.getUniqueId()) && e.getFoodLevel() <= p.getFoodLevel())
e.setCancelled(true);
}
} catch (Exception er) {
plugin.logException(er);
}
}
@EventHandler(priority = EventPriority.HIGH)
public void onDamage(EntityDamageEvent e) {
try {
if (!(e.getEntity() instanceof Player)) return;
if (!(e.getEntity() instanceof Player))
return;
Player p = (Player) e.getEntity();
if (!config.getBoolean("InvisibilityFeatures.DisableDamage")) return;
if (!config.getBoolean("InvisibilityFeatures.DisableDamage"))
return;
if (plugin.getVanishStateMgr().isVanished(p.getUniqueId())) {
e.setCancelled(true);
}
} catch (Exception er) {
plugin.logException(er);
}
}
@EventHandler(priority = EventPriority.HIGH)
public void onTarget(EntityTargetEvent e) {
try {
if (!(e.getTarget() instanceof Player)) return;
if (!config.getBoolean("InvisibilityFeatures.DisableMobTarget")) return;
if (!(e.getTarget() instanceof Player))
return;
if (!config.getBoolean("InvisibilityFeatures.DisableMobTarget"))
return;
Player p = (Player) e.getTarget();
if (plugin.getVanishStateMgr().isVanished(p.getUniqueId())) {
e.setCancelled(true);
}
} catch (Exception er) {
plugin.logException(er);
}
}
@EventHandler(priority = EventPriority.HIGH)
public void onItemPickUp(PlayerPickupItemEvent e) {
public void onItemPickUp(EntityPickupItemEvent e) {
try {
VanishPlayer vanishPlayer = plugin.getVanishPlayer(e.getPlayer());
if (vanishPlayer == null || !vanishPlayer.isOnlineVanished()) return;
if (!(e.getEntity() instanceof Player player))
return;
VanishPlayer vanishPlayer = plugin.getVanishPlayer(player);
if (vanishPlayer == null || !vanishPlayer.isOnlineVanished())
return;
if (!vanishPlayer.hasItemPickUpsEnabled())
e.setCancelled(true);
if (plugin.getSettings().getBoolean("RestrictiveOptions.PreventModifyingOwnInventory")
&& !e.getPlayer().hasPermission("sv.modifyowninv")) {
&& !player.hasPermission("sv.modifyowninv"))
{
e.setCancelled(true);
}
} catch (Exception er) {
plugin.logException(er);
}
}
@EventHandler
public void onPlayerCropTrample(PlayerInteractEvent e) {
try {
if (!plugin.getVanishStateMgr().isVanished(e.getPlayer().getUniqueId())) return;
if (e.getAction() != Action.PHYSICAL) return;
if (!plugin.getVanishStateMgr().isVanished(e.getPlayer().getUniqueId()))
return;
if (e.getAction() != Action.PHYSICAL)
return;
if (e.getClickedBlock() != null && e.getClickedBlock().getType().toString().matches("SOIL|FARMLAND"))
e.setCancelled(true);
} catch (Exception er) {
plugin.logException(er);
}
}
}
@@ -9,7 +9,6 @@
package de.myzelyam.supervanish.listeners;
import de.myzelyam.supervanish.SuperVanish;
import de.myzelyam.supervanish.features.Broadcast;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
@@ -25,13 +24,18 @@ public class JoinListener implements EventExecutor, Listener {
private final SuperVanish plugin;
public JoinListener(SuperVanish plugin) {
this.plugin = plugin;
}
@Override
public void execute(Listener l, Event event) {
try {
if (event instanceof PlayerJoinEvent) {
PlayerJoinEvent e = (PlayerJoinEvent) event;
final Player p = e.getPlayer();
// hide others
@@ -41,57 +45,79 @@ public class JoinListener implements EventExecutor, Listener {
plugin.getVisibilityChanger().getHider().setHidden(onlinePlayer, p, true);
// vanished:
if (plugin.getVanishStateMgr().isVanished(p.getUniqueId())) {
// hide self
for (Player onlinePlayer : Bukkit.getOnlinePlayers())
if (!plugin.hasPermissionToSee(onlinePlayer, p))
plugin.getVisibilityChanger().getHider().setHidden(p, onlinePlayer, true);
// Join message
if (plugin.getSettings().getBoolean("MessageOptions.HideRealJoinQuitMessages")) {
e.setJoinMessage(null);
Broadcast.announceSilentJoin(p, plugin);
}
// reminding message
if (plugin.getSettings().getBoolean("MessageOptions.RemindVanishedOnJoin")) {
plugin.sendMessage(p, "RemindingMessage", p);
}
// re-add action bar
if (plugin.getActionBarMgr() != null && plugin.getSettings().getBoolean(
"MessageOptions.DisplayActionBar")) {
if (plugin.getActionBarMgr() != null
&& plugin.getSettings().getBoolean("MessageOptions.DisplayActionBar"))
{
plugin.getActionBarMgr().addActionBar(p);
}
// sleep state
p.setSleepingIgnored(true);
// adjust fly
if (plugin.getSettings().getBoolean("InvisibilityFeatures.Fly.Enable")) {
p.setAllowFlight(true);
}
// metadata
p.setMetadata("vanished", new FixedMetadataValue(plugin, true));
} else {
// not vanished:
// metadata
p.removeMetadata("vanished", plugin);
}
// not necessarily vanished:
// recreate files msg
if ((p.hasPermission("sv.recreatecfg") || p.hasPermission("sv.recreatefiles"))
&& (plugin.getConfigMgr().isSettingsUpdateRequired()
|| plugin.getConfigMgr().isMessagesUpdateRequired())) {
String currentVersion = plugin.getDescription().getVersion();
boolean isDismissed =
plugin.getPlayerData().getBoolean("PlayerData." + p.getUniqueId() + ".dismissed."
+ currentVersion.replace(".", "_"), false);
|| plugin.getConfigMgr().isMessagesUpdateRequired()))
{
String currentVersion = plugin.getPluginMeta().getVersion();
boolean isDismissed = plugin.getPlayerData().getBoolean(
"PlayerData." + p.getUniqueId() + ".dismissed." + currentVersion.replace(".", "_"), false);
if (!isDismissed)
new BukkitRunnable() {
@Override
public void run() {
plugin.sendMessage(p, "RecreationRequiredMsg", p);
}
}.runTaskLater(plugin, 1);
}
}
} catch (Exception er) {
plugin.logException(er);
}
}
}
@@ -18,39 +18,52 @@ import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerLoginEvent;
public class LoginListener implements Listener {
private final SuperVanish plugin;
public LoginListener(SuperVanish plugin) {
this.plugin = plugin;
}
@EventHandler(priority = EventPriority.MONITOR)
public void onLogin(PlayerLoginEvent e) {
try {
if (e.getResult() != PlayerLoginEvent.Result.ALLOWED) return;
if (e.getResult() != PlayerLoginEvent.Result.ALLOWED)
return;
final Player p = e.getPlayer();
boolean vanished = plugin.getVanishStateMgr().isVanished(p.getUniqueId());
boolean itemPickUps = plugin.getPlayerData().getBoolean(
"PlayerData." + p.getUniqueId() + ".itemPickUps",
boolean itemPickUps = plugin.getPlayerData().getBoolean("PlayerData." + p.getUniqueId() + ".itemPickUps",
plugin.getSettings().getBoolean("InvisibilityFeatures.DefaultPickUpItemsOption"));
plugin.createVanishPlayer(p, itemPickUps);
if (vanished && plugin.getSettings().getBoolean("VanishStateFeatures.CheckPermissionOnLogin", false)
&& !CommandAction.VANISH_SELF.checkPermission(p, plugin)) {
&& !CommandAction.VANISH_SELF.checkPermission(p, plugin))
{
vanished = false;
}
if (!vanished && p.hasPermission("sv.joinvanished")
&& plugin.getSettings().getBoolean("VanishStateFeatures.AutoVanishOnJoin", false)) {
&& plugin.getSettings().getBoolean("VanishStateFeatures.AutoVanishOnJoin", false))
{
plugin.getVanishStateMgr().setVanishedState(p.getUniqueId(), p.getName(), true, null);
vanished = true;
}
if (vanished) {
// hide self
for (Player onlinePlayer : Bukkit.getOnlinePlayers())
if (!plugin.hasPermissionToSee(onlinePlayer, p))
plugin.getVisibilityChanger().getHider().setHidden(p, onlinePlayer, true);
}
// hide others
@@ -58,8 +71,13 @@ public class LoginListener implements Listener {
if (plugin.getVanishStateMgr().isVanished(onlinePlayer.getUniqueId())
&& !plugin.hasPermissionToSee(p, onlinePlayer))
plugin.getVisibilityChanger().getHider().setHidden(onlinePlayer, p, true);
} catch (Exception er) {
plugin.logException(er);
}
}
}
@@ -29,58 +29,91 @@ public class PlayerBlockModifyListener implements Listener {
private final SuperVanish plugin;
public PlayerBlockModifyListener(SuperVanish plugin) {
this.plugin = plugin;
}
@EventHandler(priority = EventPriority.HIGH)
public void onInteract(PlayerInteractEvent e) {
try {
if (!plugin.getVanishStateMgr().isVanished(e.getPlayer().getUniqueId()))
return;
if (e.getAction().equals(Action.PHYSICAL) && e.getClickedBlock() != null) {
if (!plugin.getSettings().getBoolean("InvisibilityFeatures.DisablePressurePlates"))
return;
String material = e.getClickedBlock().getType().toString();
List<String> disallowedMaterials = Arrays.asList("STONE_PLATE", "GOLD_PLATE", "IRON_PLATE",
"WOOD_PLATE"/* <- LEGACY*/, "TRIPWIRE", "PRESSURE_PLATE");
"WOOD_PLATE"/* <- LEGACY */, "TRIPWIRE", "PRESSURE_PLATE");
for (String disallowedMaterial : disallowedMaterials)
if (material.equals(disallowedMaterial) || material.contains(disallowedMaterial)) {
e.setCancelled(true);
}
}
} catch (Exception er) {
plugin.logException(er);
}
}
@EventHandler(priority = EventPriority.HIGH)
public void onPlace(BlockPlaceEvent e) {
try {
if (plugin.getSettings().getBoolean("RestrictiveOptions.PreventBlockPlacing")) {
Player p = e.getPlayer();
Collection<UUID> vanishedPlayers = plugin.getVanishStateMgr().getOnlineVanishedPlayers();
if (vanishedPlayers.contains(p.getUniqueId()) && !p.hasPermission("sv.placeblocks")) {
e.setCancelled(true);
plugin.sendMessage(e.getPlayer(), "BlockPlaceDenied", e.getPlayer());
}
}
} catch (Exception er) {
plugin.logException(er);
}
}
@EventHandler(priority = EventPriority.HIGH)
public void onBreak(BlockBreakEvent e) {
try {
if (plugin.getSettings().getBoolean("RestrictiveOptions.PreventBlockBreaking")) {
Player p = e.getPlayer();
if (plugin.getVanishStateMgr().isVanished(p.getUniqueId()) && !p.hasPermission("sv.breakblocks")) {
e.setCancelled(true);
plugin.sendMessage(e.getPlayer(), "BlockBreakDenied", e.getPlayer());
}
}
} catch (Exception er) {
plugin.logException(er);
}
}
}
@@ -10,9 +10,7 @@ package de.myzelyam.supervanish.listeners;
import de.myzelyam.supervanish.SuperVanish;
import de.myzelyam.supervanish.commands.CommandAction;
import de.myzelyam.supervanish.features.Broadcast;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.Listener;
@@ -24,44 +22,55 @@ public class QuitListener implements EventExecutor, Listener {
private final SuperVanish plugin;
public QuitListener(SuperVanish plugin) {
this.plugin = plugin;
}
@Override
public void execute(Listener l, Event event) {
try {
if (event instanceof PlayerQuitEvent) {
PlayerQuitEvent e = (PlayerQuitEvent) event;
FileConfiguration config = plugin.getConfig();
Player p = e.getPlayer();
// if is invisible
if (plugin.getVanishStateMgr().isVanished(p.getUniqueId())) {
// remove action bar
if (plugin.getActionBarMgr() != null && plugin.getSettings().getBoolean(
"MessageOptions.DisplayActionBar")) {
if (plugin.getActionBarMgr() != null
&& plugin.getSettings().getBoolean("MessageOptions.DisplayActionBar"))
{
plugin.getActionBarMgr().removeActionBar(p);
}
// check auto-reappear-option
boolean noMsg = false;
if (plugin.getSettings().getBoolean("VanishStateFeatures.ReappearOnQuit")
|| plugin.getSettings().getBoolean("VanishStateFeatures.CheckPermissionOnQuit")
&& !CommandAction.VANISH_SELF.checkPermission(p, plugin)) {
&& !CommandAction.VANISH_SELF.checkPermission(p, plugin))
{
plugin.getVanishStateMgr().setVanishedState(p.getUniqueId(), p.getName(), false, null);
// check if it should handle the quit msg
if (!config.getBoolean("MessageOptions.ReappearOnQuitHideLeaveMsg"))
noMsg = true;
}
// check remove-quit-msg option
if (!noMsg && config.getBoolean("MessageOptions.HideRealJoinQuitMessages")) {
e.setQuitMessage(null);
Broadcast.announceSilentQuit(p, plugin);
}
}
// remove VanishPlayer
plugin.removeVanishPlayer(plugin.getVanishPlayer(p));
}
} catch (Exception er) {
plugin.logException(er);
}
}
}
@@ -21,7 +21,6 @@ import java.util.Locale;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class TabCompleteListener implements Listener {
@@ -30,39 +29,56 @@ public class TabCompleteListener implements Listener {
private boolean errorLogged = false;
public TabCompleteListener(SuperVanish plugin) {
this.plugin = plugin;
}
@EventHandler
public void onTabComplete(TabCompleteEvent e) {
try {
if (!(e.getSender() instanceof Player)) return;
if (!(e.getSender() instanceof Player))
return;
Player p = (Player) e.getSender();
Set<String> hiddenNames = plugin.getVanishStateMgr().getOnlineVanishedPlayers().stream()
.map(Bukkit::getPlayer).filter(Objects::nonNull)
.filter(vanishedPlayer -> !plugin.canSee(p, vanishedPlayer))
.map(HumanEntity::getName)
.map(name -> name.toLowerCase(Locale.ENGLISH))
.collect(Collectors.toSet());
.filter(vanishedPlayer -> !plugin.canSee(p, vanishedPlayer)).map(HumanEntity::getName)
.map(name -> name.toLowerCase(Locale.ENGLISH)).collect(Collectors.toSet());
Iterator<String> it = e.getCompletions().iterator();
while (it.hasNext()) {
String completion = it.next();
boolean allowedCompletion = !hiddenNames.contains(completion.toLowerCase(Locale.ENGLISH));
if (!allowedCompletion) {
it.remove();
}
}
} catch (UnsupportedOperationException uoe) {
if (!errorLogged) {
plugin.getLogger().warning("UnsupportedOperationException while modifying TabCompleteEvent");
errorLogged = true;
}
} catch (Exception er) {
if (!errorLogged) {
plugin.logException(er);
errorLogged = true;
}
}
}
}
@@ -22,23 +22,35 @@ public class WorldChangeListener implements Listener {
private final SuperVanish plugin;
public WorldChangeListener(SuperVanish plugin) {
this.plugin = plugin;
}
@EventHandler(priority = EventPriority.MONITOR)
public void onWorldChange(PlayerChangedWorldEvent e) {
try {
final Player p = e.getPlayer();
if (!plugin.getVanishStateMgr().isVanished(p.getUniqueId()))
return;
// check auto-reappear option
if (plugin.getSettings().getBoolean("VanishStateFeatures.ReappearOnWorldChange")
|| plugin.getSettings().getBoolean("VanishStateFeatures.CheckPermissionOnWorldChange")
&& !CommandAction.VANISH_SELF.checkPermission(p, plugin)) {
&& !CommandAction.VANISH_SELF.checkPermission(p, plugin))
{
plugin.getVisibilityChanger().showPlayer(p);
}
} catch (Exception er) {
plugin.logException(er);
}
}
}
@@ -1,143 +0,0 @@
/*
* Copyright © 2015, Leon Mangler and the SuperVanish contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package de.myzelyam.supervanish.net;
import de.myzelyam.supervanish.SuperVanish;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.scheduler.BukkitTask;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.logging.Level;
import javax.net.ssl.HttpsURLConnection;
import lombok.Getter;
public class UpdateNotifier {
// 20 minutes
private static final long CHECK_INTERVAL = 20 * 60 * 20;
private final SuperVanish plugin;
private final BukkitTask checkTask;
@Getter
private final String currentVersion;
private final Set<UUID> notifiedPlayers = new HashSet<>();
@Getter
private volatile String latestVersion;
private boolean notifiedConsole = false;
public UpdateNotifier(final SuperVanish plugin) {
this.plugin = plugin;
currentVersion = plugin.getDescription().getVersion();
checkTask = start();
Bukkit.getPluginManager().registerEvents(new Listener() {
@EventHandler
public void onJoin(final PlayerJoinEvent e) {
new BukkitRunnable() {
@Override
public void run() {
if (!isUpToDate())
notifyPlayer(e.getPlayer());
}
}.runTaskLater(plugin, 2);
}
}, plugin);
}
public boolean isUpToDate() {
try {
if (latestVersion == null) throw new NumberFormatException();
int comparision = plugin.getVersionUtil().compareVersions(currentVersion, latestVersion);
return comparision >= 0;
} catch (NumberFormatException e) {
return currentVersion.equals(latestVersion);
}
}
private void notifyPlayer(Player p) {
if (p.hasPermission("sv.notify") && latestVersion != null) {
if (notifiedPlayers.contains(p.getUniqueId())) return;
notifiedPlayers.add(p.getUniqueId());
plugin.sendMessage(p,
plugin.getMessage("PluginOutdated").replace("%new%", latestVersion)
.replace("%current%", currentVersion), p);
}
}
private void notifyAdmins() {
for (Player p : plugin.getServer().getOnlinePlayers()) {
notifyPlayer(p);
}
}
private void notifyConsole() {
if (!notifiedConsole) {
plugin.log(Level.INFO, "Your current version of SuperVanish is outdated" +
" - New version: '" + latestVersion + "'; Currently: '" + currentVersion + "'");
notifiedConsole = true;
}
}
private BukkitTask start() {
if (checkTask != null) throw new IllegalStateException("Task is already running");
return new BukkitRunnable() {
@Override
public void run() {
String latestVersion = fetchLatestVersion();
UpdateNotifier.this.latestVersion = latestVersion.equals("Error")
? UpdateNotifier.this.latestVersion == null
? currentVersion
: UpdateNotifier.this.latestVersion
: latestVersion;
if (!isUpToDate())
new BukkitRunnable() {
@Override
public void run() {
notifyConsole();
if (plugin.getSettings().getBoolean(
"MiscellaneousOptions.UpdateChecker.NotifyAdmins")) notifyAdmins();
}
}.runTask(plugin);
}
}.runTaskTimerAsynchronously(plugin, 0, CHECK_INTERVAL);
}
private String fetchLatestVersion() {
try {
HttpsURLConnection con = (HttpsURLConnection) new URL(
"https://api.spigotmc.org/legacy/update.php?resource=1331").openConnection();
con.setConnectTimeout(20 * 1000);
con.setReadTimeout(20 * 1000);
String version;
try (BufferedReader reader = new BufferedReader(new InputStreamReader(con.getInputStream()))) {
version = reader.readLine();
}
con.disconnect();
if (version.length() <= 7)
return version;
else throw new RuntimeException("'" + version + "' is not a valid version");
} catch (Exception e) {
plugin.log(Level.WARNING, "Failed to check for an update: "
+ e.getMessage());
return "Error";
}
}
}
@@ -7,25 +7,59 @@ import org.bukkit.entity.Player;
public class BukkitPlayerHidingUtil {
private BukkitPlayerHidingUtil() {
}
public static void hidePlayer(Player player, Player viewer, SuperVanish plugin) {
if (isNewPlayerHidingAPISupported(plugin))
viewer.hidePlayer(plugin, player);
else
//noinspection deprecation
viewer.hidePlayer(player);
try {
if (isNewPlayerHidingAPISupported(plugin)) {
Player.class.getMethod("hidePlayer", org.bukkit.plugin.Plugin.class, Player.class).invoke(viewer,
plugin, player);
} else {
Player.class.getMethod("hidePlayer", Player.class).invoke(viewer, player);
}
} catch (ReflectiveOperationException e) {
throw new RuntimeException("Failed to hide player " + player.getName() + " from " + viewer.getName(), e);
}
}
public static void showPlayer(Player player, Player viewer, SuperVanish plugin) {
if (isNewPlayerHidingAPISupported(plugin))
viewer.showPlayer(plugin, player);
else
//noinspection deprecation
viewer.showPlayer(player);
try {
if (isNewPlayerHidingAPISupported(plugin)) {
Player.class.getMethod("showPlayer", org.bukkit.plugin.Plugin.class, Player.class).invoke(viewer,
plugin, player);
} else {
Player.class.getMethod("showPlayer", Player.class).invoke(viewer, player);
}
} catch (ReflectiveOperationException e) {
throw new RuntimeException("Failed to show player " + player.getName() + " to " + viewer.getName(), e);
}
}
public static boolean isNewPlayerHidingAPISupported(SuperVanish plugin) {
return plugin.getVersionUtil().isOneDotXOrHigher(19);
}
}
@@ -3,4 +3,5 @@ package de.myzelyam.supervanish.utils;
public interface ExceptionHandler {
void handle(Exception e);
}
@@ -10,70 +10,98 @@ import java.util.logging.Level;
public class ExceptionLogger {
private ExceptionLogger() {
}
public static void logException(Throwable e, SuperVanish plugin) {
try {
final boolean realEx = e != null;
Level loggingLevel = realEx ? Level.WARNING : Level.INFO;
if (realEx) plugin.log(loggingLevel, "Unknown exception occurred!");
else plugin.log(loggingLevel, "Printing information...");
if (plugin.getConfigMgr().isSettingsUpdateRequired()
|| plugin.getConfigMgr().isMessagesUpdateRequired()) {
if (realEx) plugin.log(loggingLevel, "You have an outdated configuration, " +
"regenerating it by using '/sv recreatefiles' might fix this problem!");
else plugin.log(loggingLevel, "Configuration is outdated.");
} else if (realEx) plugin.log(loggingLevel, "Please report this issue!");
if (!realEx) e = new RuntimeException("Information for reporting an issue");
if (realEx)
plugin.log(loggingLevel, "Unknown exception occurred!");
else
plugin.log(loggingLevel, "Printing information...");
if (plugin.getConfigMgr().isSettingsUpdateRequired() || plugin.getConfigMgr().isMessagesUpdateRequired()) {
if (realEx)
plugin.log(loggingLevel, "Your config files may need to be recreated. "
+ "Running '/sv recreatefiles' might fix this problem.");
else
plugin.log(loggingLevel, "Config file recreation may be required.");
} else if (realEx)
plugin.log(loggingLevel, "Please report this issue!");
if (!realEx)
e = new RuntimeException("Information for reporting an issue");
plugin.log(loggingLevel, "Message: ");
plugin.log(loggingLevel, " " + e.getMessage());
plugin.log(loggingLevel, "General information: ");
StringBuilder plugins = new StringBuilder();
for (Plugin pl : Bukkit.getServer().getPluginManager().getPlugins()) {
if (pl.getName().equalsIgnoreCase("SuperVanish")) continue;
plugins.append(pl.getName()).append(" v")
.append(pl.getDescription().getVersion()).append(", ");
if (pl.getName().equalsIgnoreCase("SuperVanish"))
continue;
plugins.append(pl.getName()).append(" v").append(pl.getPluginMeta().getVersion()).append(", ");
}
plugins = new StringBuilder(plugins.substring(0, plugins.length() - 1));
plugin.log(loggingLevel, " ServerVersion: " + plugin.getServer().getVersion());
plugin.log(loggingLevel, " PluginVersion: "
+ plugin.getDescription().getVersion());
plugin.log(loggingLevel, " PluginVersion: " + plugin.getPluginMeta().getVersion());
plugin.log(loggingLevel, " ServerPlugins: " + plugins);
try {
plugin.log(loggingLevel, "Settings:");
plugin.log(loggingLevel, " MsgsVersion: "
+ plugin.getMessages().getString("MessagesVersion"));
plugin.log(loggingLevel, " MsgsVersion: " + plugin.getMessages().getString("MessagesVersion"));
StringBuilder settings = new StringBuilder("||");
for (String key : plugin.getSettings().getKeys(true)) {
if (!plugin.getSettings().getString(key).contains("MemorySection"))
settings.append(key).append(">")
.append(plugin.getSettings().getString(key)).append("||");
settings.append(key).append(">").append(plugin.getSettings().getString(key)).append("||");
}
plugin.log(loggingLevel, " Settings: " + settings);
} catch (Exception er) {
plugin.log(Level.SEVERE, ">> Error occurred while trying to print config info <<");
}
plugin.log(loggingLevel, "StackTrace: ");
e.printStackTrace();
if (realEx) {
if (plugin.getConfigMgr().isSettingsUpdateRequired()
|| plugin.getConfigMgr().isMessagesUpdateRequired())
plugin.log(loggingLevel, "You have an outdated configuration, " +
"regenerating it by using '/sv recreatefiles' might fix this problem!");
plugin.log(loggingLevel, "Your config files may need to be recreated. "
+ "Running '/sv recreatefiles' might fix this problem.");
plugin.log(loggingLevel, "Please include this information");
plugin.log(loggingLevel, "if you report the issue.");
} else
plugin.log(loggingLevel, "End of information.");
} catch (Exception e2) {
plugin.log(Level.WARNING,
"An exception occurred while trying to print a detailed stacktrace, " +
"printing an undetailed stacktrace of both exceptions:");
plugin.log(Level.WARNING, "An exception occurred while trying to print a detailed stacktrace, "
+ "printing an undetailed stacktrace of both exceptions:");
plugin.log(Level.SEVERE, "ORIGINAL EXCEPTION:");
if (e != null) {
e.printStackTrace();
}
plugin.log(Level.SEVERE, "SECOND EXCEPTION:");
e2.printStackTrace();
}
}
}
@@ -9,5 +9,7 @@
package de.myzelyam.supervanish.utils;
public interface Requirement<T> {
boolean fulfilledBy(T testSubject);
}
@@ -7,40 +7,62 @@ import java.util.logging.Level;
public abstract class Validation {
public static void checkNotNull(Object... objects) {
checkNotNull(null, objects);
}
public static void checkIsTrue(boolean... bool) {
checkIsTrue(null, bool);
}
public static void checkNotNull(String message, Object... objects) {
for (Object obj : objects) {
if (obj == null) {
if (message != null)
log(Level.SEVERE, message);
throw new IllegalArgumentException(message == null ? "" : message);
}
}
}
}
}
public static void checkIsTrue(String message, boolean... booleans) {
for (boolean bool : booleans) {
if (!bool) {
if (message != null)
log(Level.SEVERE, message);
throw new IllegalArgumentException(message == null ? "" : message);
}
}
}
private static void log(Level level, String message) {
try {
Class.forName("org.bukkit.plugin.java.JavaPlugin");
Bukkit.getLogger().log(level, message);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
@@ -1,42 +1,65 @@
package de.myzelyam.supervanish.utils;
import de.myzelyam.supervanish.SuperVanish;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
import de.myzelyam.supervanish.SuperVanish;
public class VersionUtil {
private final SuperVanish plugin;
private final String minecraftVersion;
public VersionUtil(SuperVanish plugin) {
this.plugin = plugin;
// saves versions in the format x.x.x (e.g. 1.20.1)
// Saves versions in the format x.x.x (e.g. 1.20.1).
minecraftVersion = plugin.getServer().getBukkitVersion().split(Pattern.quote("-"))[0];
}
public int compareVersions(String version1, String version2) {
String[] levels1 = version1.split("\\.");
String[] levels2 = version2.split("\\.");
int length = Math.max(levels1.length, levels2.length);
final String[] levels1 = version1.split("\\.");
final String[] levels2 = version2.split("\\.");
final int length = Math.max(levels1.length, levels2.length);
for (int i = 0; i < length; i++) {
Integer v1 = i < levels1.length ? Integer.parseInt(levels1[i]) : 0;
Integer v2 = i < levels2.length ? Integer.parseInt(levels2[i]) : 0;
int compare = v1.compareTo(v2);
final Integer v1 = i < levels1.length ? Integer.parseInt(levels1[i]) : 0;
final Integer v2 = i < levels2.length ? Integer.parseInt(levels2[i]) : 0;
final int compare = v1.compareTo(v2);
if (compare != 0) {
return compare;
}
}
return 0;
}
public boolean isOneDotX(int majorRelease) {
return minecraftVersion.equals("1." + majorRelease) || minecraftVersion.startsWith("1." + majorRelease + ".");
return minecraftVersion.equals("1." + majorRelease)
|| StringUtils.startsWith(minecraftVersion, "1." + majorRelease + ".");
}
public boolean isOneDotXOrHigher(int majorRelease) {
for (int i = majorRelease; i < 40; i++)
if (minecraftVersion.equals("1." + i) || minecraftVersion.startsWith("1." + i + ".")) return true;
return minecraftVersion.startsWith("2.");
for (int i = majorRelease; i < 40; i++) {
if (minecraftVersion.equals("1." + i) || StringUtils.startsWith(minecraftVersion, "1." + i + ".")) {
return true;
}
}
return StringUtils.startsWith(minecraftVersion, "2.");
}
}
@@ -14,13 +14,11 @@ import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.wrappers.EnumWrappers;
import com.comphenix.protocol.wrappers.WrappedChatComponent;
import de.myzelyam.supervanish.SuperVanish;
import net.md_5.bungee.api.ChatMessageType;
import net.md_5.bungee.api.chat.TextComponent;
import org.bukkit.ChatColor;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
@@ -30,65 +28,95 @@ public class ActionBarMgr {
private final List<Player> actionBars = new ArrayList<>();
public ActionBarMgr(SuperVanish plugin) {
this.plugin = plugin;
startTask();
}
private void startTask() {
new BukkitRunnable() {
@Override
public void run() {
for (Player p : actionBars) {
try {
sendActionBar(p, plugin.replacePlaceholders(plugin.getMessage("ActionBarMessage"), p));
sendActionBar(p, plugin.expandPlaceholders(p, plugin.getMessage("ActionBarMessage"), p));
} catch (Exception | NoSuchMethodError | NoClassDefFoundError e) {
cancel();
plugin.logException(e);
plugin.getLogger().warning("IMPORTANT: Please make sure that you are using the latest " +
"dev-build of ProtocolLib and that your server is up-to-date! This error likely " +
"happened inside of ProtocolLib code which is out of SuperVanish's control. It's part " +
"of an optional feature module and can be removed safely by disabling " +
"DisplayActionBar in the config file. Please report this " +
"error if you can reproduce it on an up-to-date server with only latest " +
"ProtocolLib and latest SV installed.");
}
}
}
}.runTaskTimer(plugin, 0, 2 * 20);
plugin.getLogger().warning("IMPORTANT: Please make sure that you are using the latest "
+ "dev-build of ProtocolLib and that your server is up-to-date! This error likely "
+ "happened inside of ProtocolLib code which is out of SuperVanish's control. It's part "
+ "of an optional feature module and can be removed safely by disabling "
+ "DisplayActionBar in the config file. Please report this "
+ "error if you can reproduce it on an up-to-date server with only latest "
+ "ProtocolLib and latest SV installed.");
}
private void sendActionBar(Player p, String bar) {
}
}
}.runTaskTimer(plugin, 0, 2 * 20);
}
private void sendActionBar(Player p, Component bar) {
try {
Class.forName("net.md_5.bungee.api.chat.ComponentBuilder");
p.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(bar));
} catch (ClassNotFoundException | NoSuchMethodError | NoClassDefFoundError er) {
String json = "{\"text\": \"" + ChatColor.translateAlternateColorCodes('&', bar) + "\"}";
p.sendActionBar(bar);
} catch (NoSuchMethodError | NoClassDefFoundError er) {
String json = GsonComponentSerializer.gson().serialize(bar);
WrappedChatComponent msg = WrappedChatComponent.fromJson(json);
PacketContainer chatMsg = new PacketContainer(PacketType.Play.Server.CHAT);
chatMsg.getChatComponents().write(0, msg);
if (plugin.getVersionUtil().isOneDotXOrHigher(12))
try {
chatMsg.getChatTypes().write(0, EnumWrappers.ChatType.GAME_INFO);
} catch (NoSuchMethodError e) {
p.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText
("SuperVanish: Please update ProtocolLib"));
p.sendActionBar(Component.text("SuperVanish: Please update ProtocolLib"));
}
else
chatMsg.getBytes().write(0, (byte) 2);
try {
ProtocolLibrary.getProtocolManager().sendServerPacket(p, chatMsg);
} catch (InvocationTargetException e) {
} catch (Exception e) {
throw new RuntimeException("Cannot send packet " + chatMsg, e);
}
}
}
public void addActionBar(Player p) {
actionBars.add(p);
}
public void removeActionBar(Player p) {
actionBars.remove(p);
}
}
@@ -8,11 +8,6 @@
package de.myzelyam.supervanish.visibility;
import de.myzelyam.api.vanish.PlayerVanishStateChangeEvent;
import de.myzelyam.supervanish.SuperVanish;
import org.bukkit.Bukkit;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
@@ -21,85 +16,121 @@ import java.util.Set;
import java.util.UUID;
import java.util.logging.Level;
import org.apache.commons.lang3.StringUtils;
import org.bukkit.Bukkit;
import de.myzelyam.api.vanish.PlayerVanishStateChangeEvent;
import de.myzelyam.supervanish.SuperVanish;
public class FileVanishStateMgr extends VanishStateMgr {
private final SuperVanish plugin;
public FileVanishStateMgr(SuperVanish plugin) {
super(plugin);
this.plugin = plugin;
}
@Override
public boolean isVanished(final UUID uuid) {
return getVanishedPlayersOnFile().contains(uuid);
}
@Override
public void setVanishedState(final UUID uuid, String name, boolean hide, String causeName) {
PlayerVanishStateChangeEvent event = new PlayerVanishStateChangeEvent(uuid, name, hide, causeName);
final PlayerVanishStateChangeEvent event = new PlayerVanishStateChangeEvent(uuid, name, hide, causeName);
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) return;
List<String> vanishedPlayerUUIDStrings = plugin.getPlayerData().getStringList("InvisiblePlayers");
if (hide)
vanishedPlayerUUIDStrings.add(uuid.toString());
else
vanishedPlayerUUIDStrings.remove(uuid.toString());
plugin.getPlayerData().set("InvisiblePlayers", vanishedPlayerUUIDStrings);
if (hide)
if (event.isCancelled()) {
return;
}
final Set<UUID> vanishedPlayers = getVanishedPlayersOnFile();
if (hide) {
vanishedPlayers.add(uuid);
} else {
vanishedPlayers.remove(uuid);
}
setVanishedPlayersOnFile(vanishedPlayers);
if (hide) {
plugin.getPlayerData().set("PlayerData." + uuid + ".information.name", name);
plugin.getConfigMgr().getPlayerDataFile().save();
}
}
@Override
public Set<UUID> getVanishedPlayers() {
return getVanishedPlayersOnFile();
}
@Override
public Collection<UUID> getOnlineVanishedPlayers() {
Set<UUID> onlineVanishedPlayers = new HashSet<>();
for (UUID vanishedUUID : getVanishedPlayers()) {
if (Bukkit.getPlayer(vanishedUUID) != null)
onlineVanishedPlayers.add(vanishedUUID);
}
final Set<UUID> onlineVanishedPlayers = new HashSet<>();
getVanishedPlayers().stream().filter(vanishedUUID -> Bukkit.getPlayer(vanishedUUID) != null)
.forEach(onlineVanishedPlayers::add);
return onlineVanishedPlayers;
}
public UUID getVanishedUUIDFromNameOnFile(String name) {
for (UUID uuid : getVanishedPlayersOnFile()) {
if (plugin.getPlayerData().getString("PlayerData." + uuid + ".information.name")
.equalsIgnoreCase(name)) {
return uuid;
}
}
return null;
return getVanishedPlayersOnFile().stream()
.filter(uuid -> StringUtils.equalsIgnoreCase(
plugin.getPlayerData().getString("PlayerData." + uuid + ".information.name"), name))
.findFirst().orElse(null);
}
private Set<UUID> getVanishedPlayersOnFile() {
List<String> vanishedPlayerUUIDStrings = plugin.getPlayerData().getStringList("InvisiblePlayers");
Set<UUID> vanishedPlayerUUIDs = new HashSet<>();
for (String uuidString : new ArrayList<>(vanishedPlayerUUIDStrings)) {
final List<String> vanishedPlayerUUIDStrings = plugin.getPlayerData().getStringList("InvisiblePlayers");
final Set<UUID> vanishedPlayerUUIDs = new HashSet<>();
new ArrayList<>(vanishedPlayerUUIDStrings).forEach(uuidString -> {
try {
vanishedPlayerUUIDs.add(UUID.fromString(uuidString));
} catch (IllegalArgumentException e) {
vanishedPlayerUUIDStrings.remove(uuidString);
plugin.log(Level.WARNING,
"The data.yml file contains an invalid player uuid," +
" deleting it.");
plugin.log(Level.WARNING, "The data.yml file contains an invalid player uuid," + " deleting it.");
plugin.getPlayerData().set("InvisiblePlayers", vanishedPlayerUUIDStrings);
plugin.getConfigMgr().getPlayerDataFile().save();
}
}
});
return vanishedPlayerUUIDs;
}
private void setVanishedPlayersOnFile(Set<UUID> vanishedPlayers) {
List<String> vanishedPlayerUUIDStrings = new ArrayList<>();
for (UUID uuid : vanishedPlayers)
vanishedPlayerUUIDStrings.add(uuid.toString());
plugin.getPlayerData().set("InvisiblePlayers",
vanishedPlayerUUIDStrings);
final List<String> vanishedPlayerUUIDStrings = new ArrayList<>();
vanishedPlayers.forEach(uuid -> vanishedPlayerUUIDStrings.add(uuid.toString()));
plugin.getPlayerData().set("InvisiblePlayers", vanishedPlayerUUIDStrings);
plugin.getConfigMgr().getPlayerDataFile().save();
}
}
@@ -20,32 +20,47 @@ public class PaperServerPingListener implements Listener {
private final SuperVanish plugin;
public PaperServerPingListener(SuperVanish plugin) {
this.plugin = plugin;
}
@EventHandler(priority = EventPriority.HIGH)
public void onServerListPing(PaperServerListPingEvent e) {
try {
final FileConfiguration settings = plugin.getSettings();
if (!settings.getBoolean("ExternalInvisibility.ServerList.AdjustAmountOfOnlinePlayers")
&& !settings.getBoolean("ExternalInvisibility.ServerList.AdjustListOfLoggedInPlayers"))
return;
Collection<UUID> onlineVanishedPlayers = plugin.getVanishStateMgr().getOnlineVanishedPlayers();
int vanishedPlayersCount = onlineVanishedPlayers.size(),
playerCount = Bukkit.getOnlinePlayers().size();
int vanishedPlayersCount = onlineVanishedPlayers.size(), playerCount = Bukkit.getOnlinePlayers().size();
if (settings.getBoolean("ExternalInvisibility.ServerList.AdjustAmountOfOnlinePlayers")) {
e.setNumPlayers(playerCount - vanishedPlayersCount);
}
if (settings.getBoolean("ExternalInvisibility.ServerList.AdjustListOfLoggedInPlayers")) {
List<PlayerProfile> playerSample = e.getPlayerSample();
playerSample.removeIf(profile -> onlineVanishedPlayers.contains(profile.getId()));
}
} catch (Exception er) {
if (!errorLogged) {
plugin.logException(er);
errorLogged = true;
}
}
}
}
@@ -35,46 +35,75 @@ public class ServerListPacketListener extends PacketAdapter {
private boolean errorLogged = false;
public ServerListPacketListener(SuperVanish plugin) {
super(plugin, ListenerPriority.NORMAL, PacketType.Status.Server.SERVER_INFO);
this.plugin = plugin;
}
/**
* Constructor for legacy ProtocolLib API
*/
public ServerListPacketListener(SuperVanish plugin, boolean use_legacy) {
//noinspection deprecation
super(plugin, ListenerPriority.NORMAL, PacketType.Status.Server.OUT_SERVER_INFO);
super(plugin, ListenerPriority.NORMAL, getLegacyServerInfoPacketType());
this.plugin = plugin;
}
private static PacketType getLegacyServerInfoPacketType() {
try {
return (PacketType) PacketType.Status.Server.class.getField("OUT_SERVER_INFO").get(null);
} catch (ReflectiveOperationException e) {
throw new IllegalStateException("Legacy ProtocolLib server info packet type is unavailable", e);
}
}
public static void register(SuperVanish plugin) {
// Use Paper event listener if available
try {
Class.forName("com.destroystokyo.paper.event.server.PaperServerListPingEvent");
plugin.getLogger().log(Level.INFO, "Hooked into PaperSpigot for server list ping support");
plugin.getServer().getPluginManager().registerEvents(new PaperServerPingListener(plugin), plugin);
} catch (ClassNotFoundException ignored) {
// Otherwise use ProtocolLib
if (plugin.getVersionUtil().isOneDotXOrHigher(19)) {
ProtocolLibrary.getProtocolManager().addPacketListener(new ServerListPacketListener(plugin));
} else {
ProtocolLibrary.getProtocolManager().addPacketListener(new ServerListPacketListener(plugin, true));
}
}
}
public static boolean isEnabled(SuperVanish plugin) {
final FileConfiguration config = plugin.getSettings();
return config.getBoolean(
"ExternalInvisibility.ServerList.AdjustAmountOfOnlinePlayers")
|| config.getBoolean(
"ExternalInvisibility.ServerList.AdjustListOfLoggedInPlayers");
return config.getBoolean("ExternalInvisibility.ServerList.AdjustAmountOfOnlinePlayers")
|| config.getBoolean("ExternalInvisibility.ServerList.AdjustListOfLoggedInPlayers");
}
@Override
public void onPacketSending(PacketEvent e) {
try {
final FileConfiguration settings = plugin.getSettings();
if (!settings.getBoolean("ExternalInvisibility.ServerList.AdjustAmountOfOnlinePlayers")
&& !settings.getBoolean("ExternalInvisibility.ServerList.AdjustListOfLoggedInPlayers"))
@@ -84,30 +113,62 @@ public class ServerListPacketListener extends PacketAdapter {
int vanishedPlayersCount = plugin.getVanishStateMgr().getOnlineVanishedPlayers().size(),
playerCount = Bukkit.getOnlinePlayers().size();
if (settings.getBoolean("ExternalInvisibility.ServerList.AdjustAmountOfOnlinePlayers")) {
ping.setPlayersOnline(playerCount - vanishedPlayersCount);
}
if (settings.getBoolean("ExternalInvisibility.ServerList.AdjustListOfLoggedInPlayers")) {
List<WrappedGameProfile> wrappedGameProfiles = new ArrayList<>(ping.getPlayers());
Iterator<WrappedGameProfile> iterator = wrappedGameProfiles.iterator();
while (iterator.hasNext()) {
if (onlineVanishedPlayers.contains(iterator.next().getUUID())) {
iterator.remove();
}
}
ping.setPlayers(wrappedGameProfiles);
}
e.getPacket().getServerPings().write(0, ping);
} catch (Exception er) {
if (!errorLogged) {
if (er.getMessage() != null && er.getMessage().contains("Unable to construct new instance using public net.minecraft.network.protocol.status.ServerPing")) {
plugin.getLogger().warning("The spigot-sided serverlist features are not supported by ProtocolLib on your server. Please make sure you are using the latest ProtocolLib dev build. (" + er.getMessage() + ")\n");
} else if (er.getMessage() != null && er.getMessage().contains("Cannot assign field \"online\" because \"this.playerSample\" is null")) {
plugin.getLogger().warning("The spigot-sided serverlist features are not supported yet by ProtocolLib. Please make sure you are using the latest ProtocolLib dev build.");
if (er.getMessage() != null && er.getMessage().contains(
"Unable to construct new instance using public net.minecraft.network.protocol.status.ServerPing"))
{
plugin.getLogger().warning(
"The spigot-sided serverlist features are not supported by ProtocolLib on your server. Please make sure you are using the latest ProtocolLib dev build. ("
+ er.getMessage() + ")\n");
} else if (er.getMessage() != null && er.getMessage()
.contains("Cannot assign field \"online\" because \"this.playerSample\" is null"))
{
plugin.getLogger().warning(
"The spigot-sided serverlist features are not supported yet by ProtocolLib. Please make sure you are using the latest ProtocolLib dev build.");
} else {
plugin.logException(er);
}
errorLogged = true;
}
}
}
}
@@ -19,7 +19,9 @@ public abstract class VanishStateMgr {
protected final SuperVanishPlugin plugin;
public VanishStateMgr(SuperVanishPlugin plugin) {
this.plugin = plugin;
}
public abstract boolean isVanished(final UUID uuid);
@@ -27,10 +29,13 @@ public abstract class VanishStateMgr {
public abstract void setVanishedState(final UUID uuid, String name, boolean hide, String causeName);
public final void setVanishedState(final UUID uuid, String name, boolean hide) {
setVanishedState(uuid, name, hide, null);
}
public abstract Set<UUID> getVanishedPlayers();
public abstract Collection<UUID> getOnlineVanishedPlayers();
}
@@ -15,7 +15,6 @@ import de.myzelyam.api.vanish.PostPlayerShowEvent;
import de.myzelyam.supervanish.SuperVanish;
import de.myzelyam.supervanish.utils.Validation;
import de.myzelyam.supervanish.visibility.hiders.PlayerHider;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.configuration.file.FileConfiguration;
@@ -27,49 +26,63 @@ import java.util.logging.Level;
public class VisibilityChanger {
@Getter
private final PlayerHider hider;
private final SuperVanish plugin;
private final FileConfiguration config;
public VisibilityChanger(PlayerHider hider, SuperVanish plugin) {
this.plugin = plugin;
this.hider = hider;
config = plugin.getSettings();
}
public void hidePlayer(Player player) {
hidePlayer(player, null, false);
}
public void showPlayer(Player player) {
showPlayer(player, null, false);
}
public void hidePlayer(Player player, String hiderName) {
hidePlayer(player, hiderName, false);
}
public void showPlayer(Player player, String hiderName) {
showPlayer(player, hiderName, false);
}
public void hidePlayer(final Player player, final String hiderName, boolean silent) {
try {
Validation.checkNotNull("player cannot be null", player);
if (plugin.getVanishStateMgr().isVanished(player.getUniqueId())) {
plugin.log(Level.WARNING, "Failed to hide " + player.getName()
+ " since that player is already invisible.");
plugin.log(Level.WARNING,
"Failed to hide " + player.getName() + " since that player is already invisible.");
return;
}
// call event
PlayerHideEvent e = new PlayerHideEvent(player, silent);
plugin.getServer().getPluginManager().callEvent(e);
if (e.isCancelled()) return;
if (e.isCancelled())
return;
silent = e.isSilent();
// state
plugin.getVanishStateMgr().setVanishedState(player.getUniqueId(),
player.getName(), true, hiderName);
plugin.getVanishStateMgr().setVanishedState(player.getUniqueId(), player.getName(), true, hiderName);
// metadata
player.setMetadata("vanished", new FixedMetadataValue(plugin, true));
// hide
@@ -78,12 +91,18 @@ public class VisibilityChanger {
plugin.getVisibilityChanger().getHider().setHidden(player, onlinePlayer, true);
// fly check
if (config.getBoolean("InvisibilityFeatures.Fly.Enable")) {
player.setAllowFlight(true);
}
// action bars
if (plugin.getActionBarMgr() != null && config.getBoolean("MessageOptions.DisplayActionBar")) {
plugin.getActionBarMgr().addActionBar(player);
}
// sleep state
player.setSleepingIgnored(true);
// chat message
@@ -93,33 +112,44 @@ public class VisibilityChanger {
plugin.sendMessage(player, "OnVanishCausedByOtherPlayer", player, hiderName);
// stop player from being a mob target
if (config.getBoolean("InvisibilityFeatures.DisableMobTarget")) {
player.getWorld().getEntities().stream()
.filter(ent -> ent instanceof Creature)
.map(ent -> (Creature) ent)
.filter(mob -> mob.getTarget() != null)
player.getWorld().getEntities().stream().filter(ent -> ent instanceof Creature)
.map(ent -> (Creature) ent).filter(mob -> mob.getTarget() != null)
.filter(mob -> player.getUniqueId().equals(mob.getTarget().getUniqueId()))
.forEach(mob -> mob.setTarget(null));
}
// call post event
PostPlayerHideEvent e2 = new PostPlayerHideEvent(player, silent);
plugin.getServer().getPluginManager().callEvent(e2);
} catch (Exception e) {
plugin.logException(e);
}
}
public void showPlayer(final Player player, final String showerName, boolean silent) {
try {
Validation.checkNotNull("player cannot be null", player);
if (!plugin.getVanishStateMgr().isVanished(player.getUniqueId())) {
plugin.log(Level.WARNING,
"Failed to show " + player.getName() + " since that player is already visible.");
return;
}
// call event
PlayerShowEvent e = new PlayerShowEvent(player, silent);
plugin.getServer().getPluginManager().callEvent(e);
if (e.isCancelled()) return;
if (e.isCancelled())
return;
silent = e.isSilent();
// metadata
player.removeMetadata("vanished", plugin);
@@ -129,13 +159,15 @@ public class VisibilityChanger {
plugin.getVisibilityChanger().getHider().setHidden(player, onlinePlayer, false);
// action bars
if (plugin.getActionBarMgr() != null && config.getBoolean("MessageOptions.DisplayActionBar")) {
plugin.getActionBarMgr().removeActionBar(player);
}
// sleep state
player.setSleepingIgnored(false);
// state
plugin.getVanishStateMgr().setVanishedState(player.getUniqueId(),
player.getName(), false, showerName);
plugin.getVanishStateMgr().setVanishedState(player.getUniqueId(), player.getName(), false, showerName);
// chat message
if (showerName == null)
plugin.sendMessage(player, "OnReappear", player);
@@ -143,18 +175,31 @@ public class VisibilityChanger {
plugin.sendMessage(player, "OnReappearCausedByOtherPlayer", player, showerName);
// fly
// remove fly if not spectator or creative and no perm
if (config.getBoolean("InvisibilityFeatures.Fly.DisableOnReappear")
&& !player.hasPermission("sv.keepfly")
&& (player.getGameMode() == GameMode.SURVIVAL
|| player.getGameMode() == GameMode.ADVENTURE)
&& config.getBoolean("InvisibilityFeatures.Fly.Enable")) {
if (config.getBoolean("InvisibilityFeatures.Fly.DisableOnReappear") && !player.hasPermission("sv.keepfly")
&& (player.getGameMode() == GameMode.SURVIVAL || player.getGameMode() == GameMode.ADVENTURE)
&& config.getBoolean("InvisibilityFeatures.Fly.Enable"))
{
player.setAllowFlight(false);
}
// call post event
PostPlayerShowEvent e2 = new PostPlayerShowEvent(player, silent);
plugin.getServer().getPluginManager().callEvent(e2);
} catch (Exception e) {
plugin.logException(e);
}
}
public PlayerHider getHider() {
return hider;
}
}
@@ -30,75 +30,116 @@ public abstract class PlayerHider implements Listener {
protected final Map<Player, Set<Player>> playerHiddenFromPlayersMap = new ConcurrentHashMap<>();
public PlayerHider(SuperVanish plugin) {
this.plugin = plugin;
registerQuitListener();
}
public abstract String getName();
public boolean isHidden(Player player, Player viewer) {
return !player.getUniqueId().equals(viewer.getUniqueId())
&& playerHiddenFromPlayersMap.containsKey(player)
return !player.getUniqueId().equals(viewer.getUniqueId()) && playerHiddenFromPlayersMap.containsKey(player)
&& playerHiddenFromPlayersMap.get(player).contains(viewer);
}
public boolean isHidden(UUID playerUUID, Player viewer) {
if (playerUUID.equals(viewer.getUniqueId())) return false;
for (Player p : playerHiddenFromPlayersMap.keySet()) {
if (p.getUniqueId().equals(playerUUID)) {
return playerHiddenFromPlayersMap.get(p).contains(viewer);
}
}
if (playerUUID.equals(viewer.getUniqueId()))
return false;
for (Player p : playerHiddenFromPlayersMap.keySet()) {
if (p.getUniqueId().equals(playerUUID)) {
return playerHiddenFromPlayersMap.get(p).contains(viewer);
}
}
return false;
}
public boolean isHidden(String playerName, Player viewer) {
if (playerName.equalsIgnoreCase(viewer.getName())) return false;
for (Player p : playerHiddenFromPlayersMap.keySet()) {
if (p.getName().equalsIgnoreCase(playerName)) {
return playerHiddenFromPlayersMap.get(p).contains(viewer);
}
}
if (playerName.equalsIgnoreCase(viewer.getName()))
return false;
for (Player p : playerHiddenFromPlayersMap.keySet()) {
if (p.getName().equalsIgnoreCase(playerName)) {
return playerHiddenFromPlayersMap.get(p).contains(viewer);
}
}
return false;
}
/**
* @return TRUE if the operation changed the state, FALSE if it did not
*/
public boolean setHidden(Player player, Player viewer, boolean hidden) {
if (!playerHiddenFromPlayersMap.containsKey(player))
playerHiddenFromPlayersMap.put(player, new HashSet<>());
if (viewer == player) return false;
if (viewer == player)
return false;
Set<Player> hiddenFromPlayers = playerHiddenFromPlayersMap.get(player);
if (hidden && !hiddenFromPlayers.contains(viewer)) {
hiddenFromPlayers.add(viewer);
return true;
} else if (!hidden && hiddenFromPlayers.contains(viewer)) {
hiddenFromPlayers.remove(viewer);
return true;
}
return false;
}
public Set<Player> getHiddenPlayerKeys() {
return playerHiddenFromPlayersMap.keySet();
}
private void registerQuitListener() {
plugin.getServer().getPluginManager().registerEvents(new Listener() {
@EventHandler(priority = EventPriority.MONITOR)
public void onQuit(final PlayerQuitEvent e) {
plugin.getServer().getScheduler().runTaskLater(plugin, new Runnable() {
@Override
public void run() {
playerHiddenFromPlayersMap.remove(e.getPlayer());
for (Player p : ImmutableSet.copyOf(playerHiddenFromPlayersMap.keySet())) {
playerHiddenFromPlayersMap.get(p).remove(e.getPlayer());
}
}
}, 1);
}
}, plugin);
}
}
@@ -19,47 +19,76 @@ public class PreventionHider extends PlayerHider implements Runnable {
private int taskId;
public PreventionHider(SuperVanish plugin) {
super(plugin);
if (!BukkitPlayerHidingUtil.isNewPlayerHidingAPISupported(plugin)) {
taskId = plugin.getServer().getScheduler().runTaskTimer(plugin, this, 2, 2).getTaskId();
}
// Server is Purpur 1.19.4 and ProtocolLib 5.0+ maps PLAYER_INFO to the new
// ClientboundPlayerInfoUpdatePacket; ViaSuite re-encodes for clients on
// 1.8-1.21.x.
if (plugin.isUseProtocolLib() && plugin.getVersionUtil().isOneDotXOrHigher(8)
&& !plugin.getVersionUtil().isOneDotXOrHigher(19)
&& plugin.getSettings().getBoolean("InvisibilityFeatures.ModifyTablistPackets", true))
PlayerInfoModule.register(plugin, this);
if (plugin.isUseProtocolLib()
&& plugin.getSettings().getBoolean("InvisibilityFeatures.ModifyTabCompletePackets", true)
&& !plugin.getVersionUtil().isOneDotXOrHigher(21)) {
&& !plugin.getVersionUtil().isOneDotXOrHigher(21))
{
// Not supported anymore on 1.21 and above (ProtocolLib broken)
TabCompleteModule.register(plugin, this);
}
}
@Override
public boolean setHidden(Player player, Player viewer, boolean hidden) {
if (super.setHidden(player, viewer, hidden) || BukkitPlayerHidingUtil.isNewPlayerHidingAPISupported(plugin)) {
if (hidden) BukkitPlayerHidingUtil.hidePlayer(player, viewer, plugin);
else BukkitPlayerHidingUtil.showPlayer(player, viewer, plugin);
if (hidden)
BukkitPlayerHidingUtil.hidePlayer(player, viewer, plugin);
else
BukkitPlayerHidingUtil.showPlayer(player, viewer, plugin);
return true;
}
return false;
}
@Override
public String getName() {
return "Prevention";
}
@Override
public void run() {
for (Player hidden : playerHiddenFromPlayersMap.keySet()) {
if (BukkitPlayerHidingUtil.isNewPlayerHidingAPISupported(plugin)) {
plugin.getServer().getScheduler().cancelTask(taskId);
return;
}
for (Player viewer : playerHiddenFromPlayersMap.get(hidden)) {
BukkitPlayerHidingUtil.hidePlayer(hidden, viewer, plugin);
}
}
}
}
@@ -21,9 +21,14 @@ import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
* This is currently unused on Minecraft 1.19 or higher
* Filters vanished players out of the server-bound PLAYER_INFO packet. On
* Purpur 1.19.4 the server emits ClientboundPlayerInfoUpdatePacket which
* ProtocolLib 5.0 maps to PacketType.Play.Server.PLAYER_INFO. ViaSuite handles
* re-encoding for clients on 1.8-1.21.x, so this listener only needs to operate
* on the server-native packet.
*/
public class PlayerInfoModule extends PacketAdapter {
@@ -33,46 +38,84 @@ public class PlayerInfoModule extends PacketAdapter {
private boolean errorLogged = false;
public PlayerInfoModule(SuperVanish plugin, PlayerHider hider) {
super(plugin, ListenerPriority.HIGH, PacketType.Play.Server.PLAYER_INFO);
this.plugin = plugin;
this.hider = hider;
}
public static void register(SuperVanish plugin, PlayerHider hider) {
ProtocolLibrary.getProtocolManager().addPacketListener(new PlayerInfoModule(plugin, hider));
}
@Override
public void onPacketSending(PacketEvent event) {
// multiple events share same packet object
event.setPacket(event.getPacket().shallowClone());
try {
List<PlayerInfoData> infoDataList = new ArrayList<>(event.getPacket().getPlayerInfoDataLists().read(0));
Player receiver = event.getPlayer();
for (PlayerInfoData infoData : ImmutableList.copyOf(infoDataList)) {
if (hider.isHidden(infoData.getProfile().getUUID(), receiver)) {
UUID uuid = extractUUID(infoData);
if (uuid == null)
continue;
if (hider.isHidden(uuid, receiver)) {
infoDataList.remove(infoData);
}
}
if (infoDataList.isEmpty()) {
event.setCancelled(true);
}
event.getPacket().getPlayerInfoDataLists().write(0, infoDataList);
} catch (Exception | NoClassDefFoundError e) {
if (e.getMessage() == null
|| !e.getMessage().endsWith("is not supported for temporary players.")) {
if (errorLogged) return;
if (e.getMessage() == null || !e.getMessage().endsWith("is not supported for temporary players.")) {
if (errorLogged)
return;
plugin.logException(e);
plugin.getLogger().warning("IMPORTANT: Please make sure that you are using the latest " +
"dev-build of ProtocolLib and that your server is up-to-date! This error likely " +
"happened inside of ProtocolLib code which is out of SuperVanish's control. It's part " +
"of an optional invisibility module and can be removed safely by disabling " +
"ModifyTablistPackets in the config. Please report this " +
"error if you can reproduce it on an up-to-date server with only latest " +
"ProtocolLib and latest SV installed.");
plugin.getLogger().warning("IMPORTANT: Please make sure that you are using the latest "
+ "dev-build of ProtocolLib and that your server is up-to-date! This error likely "
+ "happened inside of ProtocolLib code which is out of SuperVanish's control. It's part "
+ "of an optional invisibility module and can be removed safely by disabling "
+ "ModifyTablistPackets in the config. Please report this "
+ "error if you can reproduce it on an up-to-date server with only latest "
+ "ProtocolLib and latest SV installed.");
errorLogged = true;
}
}
}
private UUID extractUUID(PlayerInfoData infoData) {
// On 1.19.3+ the per-entry profile is null for non-ADD_PLAYER actions, so
// prefer
// profileId which the wrapper exposes for both legacy and update packets.
UUID uuid = infoData.getProfileId();
if (uuid != null)
return uuid;
if (infoData.getProfile() != null)
return infoData.getProfile().getUUID();
return null;
}
}
Loaded 100 of 104 files, more files were not shown because too many files have changed in this diff. Show more