mirror of
https://github.com/WiIIiam278/Velocitab.git
synced 2025-03-13 13:30:01 +01:00
feat: Add support for Minecraft 1.21.2/3 (#228)
* First step for 1.21.2 * fix * feat: start preparing 1.21.2 support bumps gradle and various build deps * build: now requires Velocity 3.4.0 * build: use Velocity 3.4.0 from maven * refactor: cleanup, fix wrong protocol ver in 765 * refactor: minor code cleanup & reformat * refactor: further code cleanup * refactor: more minor refactoring work * docs: document prerequisites for using the plugin message API * Fixed team packet mapping problem Fixed problems with SortingOrder packet Changed scoreboard logic to skip team packets for 1.21.2+ players if nametag is empty * docs: further grammar fixes to plugin message API docs * refactor: adjust PPB version checking logic * build: simplify PPB test logic * refactor: remove unused code * refactor: adjust formatting * refactor: make nametag empty by default * refactor: suppress warning * fix: `ConfigurationException` deserializing minimum PPB version string * refactor: remove unused import * Bug fixes * Removed tablist order from all TabPlayer instances when a player leaves * Fixed problem with data structure * Removed synchronized * fix: subscriber order not taking effect * refactor: minor code style tweaks --------- Co-authored-by: AlexDev_ <56083016+alexdev03@users.noreply.github.com>
This commit is contained in:
parent
73de08eea9
commit
6f140e4708
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
@ -65,7 +65,7 @@ jobs:
|
|||||||
hangar-token: ${{ secrets.HANGAR_API_KEY }}
|
hangar-token: ${{ secrets.HANGAR_API_KEY }}
|
||||||
hangar-version-type: Alpha
|
hangar-version-type: Alpha
|
||||||
hangar-game-versions: |
|
hangar-game-versions: |
|
||||||
3.3
|
3.4
|
||||||
files: target/Velocitab-*.jar
|
files: target/Velocitab-*.jar
|
||||||
name: Velocitab v${{ env.version_name }}
|
name: Velocitab v${{ env.version_name }}
|
||||||
version: ${{ env.version_name }}
|
version: ${{ env.version_name }}
|
||||||
@ -124,4 +124,5 @@ jobs:
|
|||||||
1.20.6
|
1.20.6
|
||||||
1.21
|
1.21
|
||||||
1.21.1
|
1.21.1
|
||||||
|
1.21.2
|
||||||
java: 17
|
java: 17
|
3
.github/workflows/release.yml
vendored
3
.github/workflows/release.yml
vendored
@ -54,7 +54,7 @@ jobs:
|
|||||||
hangar-token: ${{ secrets.HANGAR_API_KEY }}
|
hangar-token: ${{ secrets.HANGAR_API_KEY }}
|
||||||
hangar-version-type: Release
|
hangar-version-type: Release
|
||||||
hangar-game-versions: |
|
hangar-game-versions: |
|
||||||
3.3
|
3.4
|
||||||
files: target/Velocitab-*.jar
|
files: target/Velocitab-*.jar
|
||||||
name: Velocitab v${{ github.event.release.tag_name }}
|
name: Velocitab v${{ github.event.release.tag_name }}
|
||||||
version: ${{ github.event.release.tag_name }}
|
version: ${{ github.event.release.tag_name }}
|
||||||
@ -113,4 +113,5 @@ jobs:
|
|||||||
1.20.6
|
1.20.6
|
||||||
1.21
|
1.21
|
||||||
1.21.1
|
1.21.1
|
||||||
|
1.21.2
|
||||||
java: 17
|
java: 17
|
17
build.gradle
17
build.gradle
@ -2,15 +2,15 @@ import org.apache.tools.ant.filters.ReplaceTokens
|
|||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id 'xyz.jpenilla.run-velocity' version '2.3.1'
|
id 'xyz.jpenilla.run-velocity' version '2.3.1'
|
||||||
id 'com.github.johnrengelman.shadow' version '8.1.1'
|
id 'com.gradleup.shadow' version '8.3.3'
|
||||||
id 'org.cadixdev.licenser' version '0.6.1'
|
id 'org.cadixdev.licenser' version '0.6.1'
|
||||||
id 'org.ajoberstar.grgit' version '5.2.2'
|
id 'org.ajoberstar.grgit' version '5.3.0'
|
||||||
id 'maven-publish'
|
id 'maven-publish'
|
||||||
id 'java'
|
id 'java'
|
||||||
}
|
}
|
||||||
|
|
||||||
group 'net.william278'
|
group 'net.william278'
|
||||||
version "$ext.plugin_version${versionMetadata()}"
|
version "$ext.plugin_version"
|
||||||
description "$ext.plugin_description"
|
description "$ext.plugin_description"
|
||||||
defaultTasks 'licenseFormat', 'build'
|
defaultTasks 'licenseFormat', 'build'
|
||||||
|
|
||||||
@ -20,23 +20,24 @@ ext {
|
|||||||
|
|
||||||
set 'velocity_api_version', velocity_api_version.toString()
|
set 'velocity_api_version', velocity_api_version.toString()
|
||||||
set 'velocity_minimum_build', velocity_minimum_build.toString()
|
set 'velocity_minimum_build', velocity_minimum_build.toString()
|
||||||
|
set 'papi_proxy_bridge_minimum_version', papi_proxy_bridge_minimum_version.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
maven { url = 'https://repo.william278.net/velocity/' }
|
maven { url = 'https://repo.william278.net/velocity/' }
|
||||||
maven { url = 'https://repo.papermc.io/repository/maven-public/' }
|
|
||||||
maven { url = 'https://jitpack.io' }
|
|
||||||
maven { url = 'https://repo.william278.net/releases/' }
|
maven { url = 'https://repo.william278.net/releases/' }
|
||||||
maven { url = 'https://repo.william278.net/snapshots/' }
|
maven { url = 'https://repo.william278.net/snapshots/' }
|
||||||
|
maven { url = 'https://repo.papermc.io/repository/maven-public/' }
|
||||||
maven { url = 'https://repo.minebench.de/' }
|
maven { url = 'https://repo.minebench.de/' }
|
||||||
|
maven { url = 'https://jitpack.io' }
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compileOnly "com.velocitypowered:velocity-api:${velocity_api_version}-SNAPSHOT"
|
compileOnly "com.velocitypowered:velocity-api:${velocity_api_version}-SNAPSHOT"
|
||||||
compileOnly "com.velocitypowered:velocity-proxy:${velocity_api_version}-SNAPSHOT"
|
compileOnly "com.velocitypowered:velocity-proxy:${velocity_api_version}-SNAPSHOT"
|
||||||
|
|
||||||
compileOnly 'io.netty:netty-codec-http:4.1.112.Final'
|
compileOnly 'io.netty:netty-codec-http:4.1.114.Final'
|
||||||
compileOnly 'org.projectlombok:lombok:1.18.34'
|
compileOnly 'org.projectlombok:lombok:1.18.34'
|
||||||
compileOnly 'net.luckperms:api:5.4'
|
compileOnly 'net.luckperms:api:5.4'
|
||||||
compileOnly 'io.github.miniplaceholders:miniplaceholders-api:2.2.3'
|
compileOnly 'io.github.miniplaceholders:miniplaceholders-api:2.2.3'
|
||||||
@ -71,7 +72,6 @@ license {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.lifecycle("Building Velocitab ${version} by William278")
|
logger.lifecycle("Building Velocitab ${version} by William278")
|
||||||
|
|
||||||
version rootProject.version
|
version rootProject.version
|
||||||
archivesBaseName = "${rootProject.name}"
|
archivesBaseName = "${rootProject.name}"
|
||||||
|
|
||||||
@ -160,11 +160,12 @@ publishing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tasks {
|
tasks {
|
||||||
|
var papi = papi_proxy_bridge_minimum_version
|
||||||
runVelocity {
|
runVelocity {
|
||||||
velocityVersion("${velocity_api_version}-SNAPSHOT")
|
velocityVersion("${velocity_api_version}-SNAPSHOT")
|
||||||
|
|
||||||
downloadPlugins {
|
downloadPlugins {
|
||||||
modrinth ("papiproxybridge", "1.6.1")
|
modrinth ("papiproxybridge", papi)
|
||||||
modrinth ("miniplaceholders", "2.2.4")
|
modrinth ("miniplaceholders", "2.2.4")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,17 @@
|
|||||||
Velocitab provides a plugin message API.
|
Velocitab provides a plugin message API, to let you do things with Velocitab from your backend servers.
|
||||||
|
|
||||||
|
> **Note:** This feature requires sending Update Teams packets. `send_scoreboard_packets` must be enabled in the [`config.yml` file](config-file) for this to work. [More details...](sorting#compatibility-issues)
|
||||||
|
>
|
||||||
|
## Prerequisites
|
||||||
|
To use the Velocitab plugin message API, you must first turn it on and ensure the following:
|
||||||
|
|
||||||
|
* That `enable_plugin_message_api` and `send_scoreboard_packets` is set to `true` in your Velocitab [[config file]]
|
||||||
|
* That `bungee-plugin-message-channel` is set to `true` in your **Velocity proxy config** TOML (see [Velocity config reference](https://docs.papermc.io/velocity/configuration)).
|
||||||
|
|
||||||
## API Requests from Backend Plugins
|
## API Requests from Backend Plugins
|
||||||
|
|
||||||
### 1 Changing player's username in the TAB List
|
### 1 Changing player's username in the TAB list
|
||||||
To change a player's username in the tablist, you can send a plugin message with the channel `velocitab:update_custom_name` and as data `customName`.
|
To change a player's username in the TAB list, you can send a plugin message on the channel `velocitab:update_custom_name` with a `customName` string, where `customName` is the new desired display name.
|
||||||
Remember to replace `customName` with the desired name.
|
|
||||||
<details>
|
<details>
|
||||||
<summary>Example — Changing player's username in the TAB List</summary>
|
<summary>Example — Changing player's username in the TAB List</summary>
|
||||||
|
|
||||||
@ -13,10 +20,10 @@ player.sendPluginMessage(plugin, "velocitab:update_custom_name", "Steve".getByte
|
|||||||
```
|
```
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
### 2 Update team color
|
### 2 Update color of player's nametag
|
||||||
To change a player's team color in the TAB List, you can send a plugin message with the channel `velocitab:update_team_color` and as data `teamColor`.
|
To change player's [nametag](nametags) color, you can send a plugin message on the channel `velocitab:update_team_color` with `teamColor` string, where `teamColor` is the new desired name tag color.
|
||||||
You can only use legacy color codes, for example `a` for green, `b` for aqua, etc.
|
|
||||||
This option overrides the glow effect if set
|
You can only use legacy color codes, for example `a` for green, `b` for aqua, etc. Please note this option overrides the color of the glow potion effect if set. [Check here](https://wiki.vg/index.php?title=Text_formatting&oldid=18983#Colors) for a list of supported colors (The value under the "Code" header on the table is what you need).
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>Example — Changing player's team color</summary>
|
<summary>Example — Changing player's team color</summary>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
This page will walk you through installing Velocitab on a Velocity proxy server.
|
This page will walk you through installing Velocitab on a Velocity proxy server.
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
* A Velocity proxy server (running Velocity 3.3.0 or newer)
|
* A Velocity proxy server (running Velocity 3.4.0 or newer)
|
||||||
* Backend Minecraft servers. The following Minecraft server versions are fully supported:
|
* Backend Minecraft servers. The following Minecraft server versions are fully supported:
|
||||||
- Minecraft 1.8—1.8.9
|
- Minecraft 1.8—1.8.9
|
||||||
- Minecraft 1.12.2—latest
|
- Minecraft 1.12.2—latest
|
||||||
|
@ -3,9 +3,10 @@ javaVersion=17
|
|||||||
org.gradle.jvmargs='-Dfile.encoding=UTF-8'
|
org.gradle.jvmargs='-Dfile.encoding=UTF-8'
|
||||||
org.gradle.daemon=true
|
org.gradle.daemon=true
|
||||||
|
|
||||||
plugin_version=1.7.1
|
plugin_version=1.7.2
|
||||||
plugin_archive=velocitab
|
plugin_archive=velocitab
|
||||||
plugin_description=A beautiful and versatile TAB list plugin for Velocity proxies
|
plugin_description=A beautiful and versatile TAB list plugin for Velocity proxies
|
||||||
|
|
||||||
velocity_api_version=3.3.0
|
velocity_api_version=3.4.0
|
||||||
velocity_minimum_build=400
|
velocity_minimum_build=444
|
||||||
|
papi_proxy_bridge_minimum_version=1.7
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,6 +1,6 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
20
gradlew.bat
vendored
20
gradlew.bat
vendored
@ -43,11 +43,11 @@ set JAVA_EXE=java.exe
|
|||||||
%JAVA_EXE% -version >NUL 2>&1
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
if %ERRORLEVEL% equ 0 goto execute
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
echo location of your Java installation.
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
|||||||
|
|
||||||
if exist "%JAVA_EXE%" goto execute
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
echo location of your Java installation.
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
|
@ -59,7 +59,6 @@ import org.slf4j.event.Level;
|
|||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
@Plugin(id = "velocitab")
|
@Plugin(id = "velocitab")
|
||||||
@Getter
|
@Getter
|
||||||
@ -144,9 +143,9 @@ public class Velocitab implements ConfigProvider, ScoreboardProvider, LoggerProv
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@Override
|
||||||
public Optional<ScoreboardManager> getScoreboardManager() {
|
public ScoreboardManager getScoreboardManager() {
|
||||||
return Optional.ofNullable(scoreboardManager);
|
return scoreboardManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void prepareAPI() {
|
private void prepareAPI() {
|
||||||
|
@ -19,6 +19,8 @@
|
|||||||
|
|
||||||
package net.william278.velocitab.config;
|
package net.william278.velocitab.config;
|
||||||
|
|
||||||
|
import com.velocitypowered.api.plugin.PluginContainer;
|
||||||
|
import com.velocitypowered.api.plugin.PluginDescription;
|
||||||
import de.exlll.configlib.NameFormatters;
|
import de.exlll.configlib.NameFormatters;
|
||||||
import de.exlll.configlib.YamlConfigurationProperties;
|
import de.exlll.configlib.YamlConfigurationProperties;
|
||||||
import de.exlll.configlib.YamlConfigurations;
|
import de.exlll.configlib.YamlConfigurations;
|
||||||
@ -129,16 +131,24 @@ public interface ConfigProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("OptionalIsPresent")
|
||||||
default void checkCompatibility() {
|
default void checkCompatibility() {
|
||||||
if (getSkipCompatibilityCheck().orElse(false)) {
|
if (getSkipCompatibilityCheck().orElse(false)) {
|
||||||
getPlugin().getLogger().warn("Skipping compatibility check");
|
getPlugin().getLogger().warn("Skipping compatibility checks");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate Velocity platform version
|
||||||
final Metadata metadata = getMetadata();
|
final Metadata metadata = getMetadata();
|
||||||
final Version proxyVersion = getVelocityVersion();
|
final Version proxyVersion = getVelocityVersion();
|
||||||
metadata.validateApiVersion(proxyVersion);
|
metadata.validateApiVersion(proxyVersion);
|
||||||
metadata.validateBuild(proxyVersion);
|
metadata.validateBuild(proxyVersion);
|
||||||
|
|
||||||
|
// Validate PAPIProxyBridge hook version
|
||||||
|
final Optional<Version> papiProxyBridgeVersion = getPapiProxyBridgeVersion();
|
||||||
|
if (papiProxyBridgeVersion.isPresent()) {
|
||||||
|
metadata.validatePapiProxyBridgeVersion(papiProxyBridgeVersion.get());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@ -151,6 +161,12 @@ public interface ConfigProvider {
|
|||||||
.findFirst();
|
.findFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default Optional<Version> getPapiProxyBridgeVersion() {
|
||||||
|
return getPlugin().getServer().getPluginManager()
|
||||||
|
.getPlugin("papiproxybridge").map(PluginContainer::getDescription)
|
||||||
|
.flatMap(PluginDescription::getVersion).map(Version::fromString);
|
||||||
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
Version getVelocityVersion();
|
Version getVelocityVersion();
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
|||||||
import net.william278.velocitab.Velocitab;
|
import net.william278.velocitab.Velocitab;
|
||||||
import net.william278.velocitab.player.TabPlayer;
|
import net.william278.velocitab.player.TabPlayer;
|
||||||
import net.william278.velocitab.util.QuadFunction;
|
import net.william278.velocitab.util.QuadFunction;
|
||||||
import net.william278.velocitab.util.SerializerUtil;
|
import net.william278.velocitab.util.SerializationUtil;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
@ -57,11 +57,11 @@ public enum Formatter {
|
|||||||
MiniMessage.miniMessage()::serialize
|
MiniMessage.miniMessage()::serialize
|
||||||
),
|
),
|
||||||
LEGACY(
|
LEGACY(
|
||||||
(text, player, viewer, plugin) -> SerializerUtil.LEGACY_SERIALIZER.deserialize(text),
|
(text, player, viewer, plugin) -> SerializationUtil.LEGACY_SERIALIZER.deserialize(text),
|
||||||
Function.identity(),
|
Function.identity(),
|
||||||
"Legacy Text",
|
"Legacy Text",
|
||||||
SerializerUtil.LEGACY_SERIALIZER::deserialize,
|
SerializationUtil.LEGACY_SERIALIZER::deserialize,
|
||||||
SerializerUtil.LEGACY_SERIALIZER::serialize
|
SerializationUtil.LEGACY_SERIALIZER::serialize
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
@ -37,6 +37,7 @@ public class Metadata {
|
|||||||
|
|
||||||
private String velocityApiVersion;
|
private String velocityApiVersion;
|
||||||
private int velocityMinimumBuild;
|
private int velocityMinimumBuild;
|
||||||
|
private String papiProxyBridgeMinimumVersion;
|
||||||
|
|
||||||
public void validateApiVersion(@NotNull Version version) {
|
public void validateApiVersion(@NotNull Version version) {
|
||||||
if (version.compareTo(Version.fromString(velocityApiVersion)) < 0) {
|
if (version.compareTo(Version.fromString(velocityApiVersion)) < 0) {
|
||||||
@ -56,6 +57,14 @@ public class Metadata {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void validatePapiProxyBridgeVersion(@NotNull Version version) {
|
||||||
|
if (version.compareTo(Version.fromString(papiProxyBridgeMinimumVersion)) < 0) {
|
||||||
|
final String serverVersion = version.toStringWithoutMetadata();
|
||||||
|
throw new IllegalStateException("Your PAPIProxyBridge version (" + serverVersion + ") is not supported! " +
|
||||||
|
"Disabling Velocitab. Please update to at least PAPIProxyBridge v" + papiProxyBridgeMinimumVersion + ".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private int getBuildNumber(@NotNull String proxyVersion) {
|
private int getBuildNumber(@NotNull String proxyVersion) {
|
||||||
final Matcher matcher = Pattern.compile(".*-b(\\d+).*").matcher(proxyVersion);
|
final Matcher matcher = Pattern.compile(".*-b(\\d+).*").matcher(proxyVersion);
|
||||||
if (matcher.find(1)) {
|
if (matcher.find(1)) {
|
||||||
|
@ -45,7 +45,7 @@ public record ServerUrl(
|
|||||||
(type) -> CompletableFuture.completedFuture(ServerLink.serverLink(type, url()))
|
(type) -> CompletableFuture.completedFuture(ServerLink.serverLink(type, url()))
|
||||||
).orElseGet(
|
).orElseGet(
|
||||||
() -> Placeholder.replace(label(), plugin, player)
|
() -> Placeholder.replace(label(), plugin, player)
|
||||||
.thenApply(replaced -> plugin.getFormatter().format(replaced, player, plugin))
|
.thenApply(replaced -> plugin.getFormatter().format(replaced, player, plugin))
|
||||||
.thenApply(formatted -> ServerLink.serverLink(formatted, url()))
|
.thenApply(formatted -> ServerLink.serverLink(formatted, url()))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ public class TabGroups implements ConfigValidator {
|
|||||||
List.of("<rainbow:!2>Running Velocitab by William278 & AlexDev_</rainbow>"),
|
List.of("<rainbow:!2>Running Velocitab by William278 & AlexDev_</rainbow>"),
|
||||||
List.of("<gray>There are currently %players_online%/%max_players_online% players online</gray>"),
|
List.of("<gray>There are currently %players_online%/%max_players_online% players online</gray>"),
|
||||||
"<gray>[%server%] %prefix%%username%</gray>",
|
"<gray>[%server%] %prefix%%username%</gray>",
|
||||||
new Nametag("<white>%prefix%</white>", "<white>%suffix%</white>"),
|
new Nametag("", ""),
|
||||||
Set.of("lobby", "survival", "creative", "minigames", "skyblock", "prison", "hub"),
|
Set.of("lobby", "survival", "creative", "minigames", "skyblock", "prison", "hub"),
|
||||||
List.of("%role_weight%", "%username_lower%"),
|
List.of("%role_weight%", "%username_lower%"),
|
||||||
false,
|
false,
|
||||||
@ -63,11 +63,12 @@ public class TabGroups implements ConfigValidator {
|
|||||||
public List<Group> groups = List.of(DEFAULT_GROUP);
|
public List<Group> groups = List.of(DEFAULT_GROUP);
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
|
@SuppressWarnings("unused")
|
||||||
public Group getGroupFromName(@NotNull String name) {
|
public Group getGroupFromName(@NotNull String name) {
|
||||||
return groups.stream()
|
return groups.stream()
|
||||||
.filter(group -> group.name().equals(name))
|
.filter(group -> group.name().equals(name))
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElseThrow(() -> new IllegalStateException("No group with name " + name + " found"));
|
.orElseThrow(() -> new IllegalStateException("No group with name %s found".formatted(name)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<Group> getGroup(@NotNull String name) {
|
public Optional<Group> getGroup(@NotNull String name) {
|
||||||
|
@ -49,10 +49,10 @@ public class MiniConditionManager {
|
|||||||
|
|
||||||
public final static Map<String, String> REPLACE_2 = Map.of(
|
public final static Map<String, String> REPLACE_2 = Map.of(
|
||||||
"*LESS3*", "<",
|
"*LESS3*", "<",
|
||||||
"*GREATER3*",">",
|
"*GREATER3*", ">",
|
||||||
"*LESS2*", "<",
|
"*LESS2*", "<",
|
||||||
"*GREATER2*", ">"
|
"*GREATER2*", ">"
|
||||||
);
|
);
|
||||||
|
|
||||||
private final static Map<String, String> REPLACE_3 = Map.of(
|
private final static Map<String, String> REPLACE_3 = Map.of(
|
||||||
"?dp?", ":"
|
"?dp?", ":"
|
||||||
|
@ -138,20 +138,31 @@ public final class PacketRegistration<P extends MinecraftPacket> {
|
|||||||
final MethodHandles.Lookup lookup = MethodHandles.lookup();
|
final MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||||
try {
|
try {
|
||||||
final MethodHandles.Lookup stateRegistryLookup = MethodHandles.privateLookupIn(StateRegistry.class, lookup);
|
final MethodHandles.Lookup stateRegistryLookup = MethodHandles.privateLookupIn(StateRegistry.class, lookup);
|
||||||
STATE_REGISTRY$clientBound = stateRegistryLookup.findGetter(StateRegistry.class, "clientbound", StateRegistry.PacketRegistry.class);
|
STATE_REGISTRY$clientBound = stateRegistryLookup.findGetter(
|
||||||
STATE_REGISTRY$serverBound = stateRegistryLookup.findGetter(StateRegistry.class, "serverbound", StateRegistry.PacketRegistry.class);
|
StateRegistry.class, "clientbound", StateRegistry.PacketRegistry.class);
|
||||||
|
STATE_REGISTRY$serverBound = stateRegistryLookup.findGetter(
|
||||||
|
StateRegistry.class, "serverbound", StateRegistry.PacketRegistry.class);
|
||||||
|
|
||||||
final MethodType mapType = MethodType.methodType(StateRegistry.PacketMapping.class, Integer.TYPE, ProtocolVersion.class, Boolean.TYPE);
|
final MethodType mapType = MethodType.methodType(
|
||||||
PACKET_MAPPING$map = stateRegistryLookup.findStatic(StateRegistry.class, "map", mapType);
|
StateRegistry.PacketMapping.class, Integer.TYPE, ProtocolVersion.class, Boolean.TYPE);
|
||||||
|
PACKET_MAPPING$map = stateRegistryLookup.findStatic(
|
||||||
|
StateRegistry.class, "map", mapType);
|
||||||
|
|
||||||
final MethodHandles.Lookup packetRegistryLookup = MethodHandles.privateLookupIn(StateRegistry.PacketRegistry.class, lookup);
|
final MethodHandles.Lookup packetRegistryLookup = MethodHandles.privateLookupIn(
|
||||||
final MethodType registerType = MethodType.methodType(void.class, Class.class, Supplier.class, StateRegistry.PacketMapping[].class);
|
StateRegistry.PacketRegistry.class, lookup);
|
||||||
PACKET_REGISTRY$register = packetRegistryLookup.findVirtual(StateRegistry.PacketRegistry.class, "register", registerType);
|
final MethodType registerType = MethodType.methodType(
|
||||||
PACKET_REGISTRY$versions = packetRegistryLookup.findGetter(StateRegistry.PacketRegistry.class, "versions", Map.class);
|
void.class, Class.class, Supplier.class, StateRegistry.PacketMapping[].class);
|
||||||
|
PACKET_REGISTRY$register = packetRegistryLookup.findVirtual(
|
||||||
|
StateRegistry.PacketRegistry.class, "register", registerType);
|
||||||
|
PACKET_REGISTRY$versions = packetRegistryLookup.findGetter(
|
||||||
|
StateRegistry.PacketRegistry.class, "versions", Map.class);
|
||||||
|
|
||||||
final MethodHandles.Lookup protocolRegistryLookup = MethodHandles.privateLookupIn(StateRegistry.PacketRegistry.ProtocolRegistry.class, lookup);
|
final MethodHandles.Lookup protocolRegistryLookup = MethodHandles.privateLookupIn(
|
||||||
PACKET_REGISTRY$packetIdToSupplier = protocolRegistryLookup.findGetter(StateRegistry.PacketRegistry.ProtocolRegistry.class, "packetIdToSupplier", IntObjectMap.class);
|
StateRegistry.PacketRegistry.ProtocolRegistry.class, lookup);
|
||||||
PACKET_REGISTRY$packetClassToId = protocolRegistryLookup.findGetter(StateRegistry.PacketRegistry.ProtocolRegistry.class, "packetClassToId", Object2IntMap.class);
|
PACKET_REGISTRY$packetIdToSupplier = protocolRegistryLookup.findGetter(
|
||||||
|
StateRegistry.PacketRegistry.ProtocolRegistry.class, "packetIdToSupplier", IntObjectMap.class);
|
||||||
|
PACKET_REGISTRY$packetClassToId = protocolRegistryLookup.findGetter(
|
||||||
|
StateRegistry.PacketRegistry.ProtocolRegistry.class, "packetClassToId", Object2IntMap.class);
|
||||||
|
|
||||||
|
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
|
@ -45,8 +45,8 @@ public class PlayerChannelHandler extends ChannelDuplexHandler {
|
|||||||
@Override
|
@Override
|
||||||
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
|
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
|
||||||
if (msg instanceof final UpdateTeamsPacket updateTeamsPacket && plugin.getSettings().isSendScoreboardPackets()) {
|
if (msg instanceof final UpdateTeamsPacket updateTeamsPacket && plugin.getSettings().isSendScoreboardPackets()) {
|
||||||
final Optional<ScoreboardManager> scoreboardManager = plugin.getScoreboardManager();
|
final ScoreboardManager scoreboardManager = plugin.getScoreboardManager();
|
||||||
if (scoreboardManager.isEmpty()) {
|
if (!scoreboardManager.handleTeams()) {
|
||||||
super.write(ctx, msg, promise);
|
super.write(ctx, msg, promise);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -56,7 +56,7 @@ public class PlayerChannelHandler extends ChannelDuplexHandler {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scoreboardManager.get().isInternalTeam(updateTeamsPacket.teamName())) {
|
if (scoreboardManager.isInternalTeam(updateTeamsPacket.teamName())) {
|
||||||
super.write(ctx, msg, promise);
|
super.write(ctx, msg, promise);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -74,8 +74,8 @@ public class PlayerChannelHandler extends ChannelDuplexHandler {
|
|||||||
// Cancel packet if the backend is trying to send a team packet with an online player.
|
// Cancel packet if the backend is trying to send a team packet with an online player.
|
||||||
// This is to prevent conflicts with Velocitab teams.
|
// This is to prevent conflicts with Velocitab teams.
|
||||||
plugin.getLogger().warn("Cancelled team \"{}\" packet from backend for player {}. " +
|
plugin.getLogger().warn("Cancelled team \"{}\" packet from backend for player {}. " +
|
||||||
"We suggest disabling \"send_scoreboard_packets\" in Velocitab's config.yml file, " +
|
"We suggest disabling \"send_scoreboard_packets\" in Velocitab's config.yml file, " +
|
||||||
"but note this will disable TAB sorting",
|
"but note this will disable TAB sorting",
|
||||||
updateTeamsPacket.teamName(), player.getUsername());
|
updateTeamsPacket.teamName(), player.getUsername());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -104,6 +104,7 @@ public class Protocol48Adapter extends TeamsPacketAdapter {
|
|||||||
/**
|
/**
|
||||||
* Returns a shortened version of the given string, with a maximum length of 16 characters.
|
* Returns a shortened version of the given string, with a maximum length of 16 characters.
|
||||||
* This is used to ensure that the team name, display name, prefix and suffix are not too long for the client.
|
* This is used to ensure that the team name, display name, prefix and suffix are not too long for the client.
|
||||||
|
*
|
||||||
* @param string the string to be shortened
|
* @param string the string to be shortened
|
||||||
* @return the shortened string
|
* @return the shortened string
|
||||||
*/
|
*/
|
||||||
|
@ -32,7 +32,7 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adapter for handling the UpdateTeamsPacket for Minecraft 1.20.3-1.20.5
|
* Adapter for handling the UpdateTeamsPacket for Minecraft 1.20.3-1.21.2
|
||||||
*/
|
*/
|
||||||
public class Protocol765Adapter extends Protocol404Adapter {
|
public class Protocol765Adapter extends Protocol404Adapter {
|
||||||
|
|
||||||
@ -40,7 +40,8 @@ public class Protocol765Adapter extends Protocol404Adapter {
|
|||||||
super(plugin, Set.of(
|
super(plugin, Set.of(
|
||||||
ProtocolVersion.MINECRAFT_1_20_3,
|
ProtocolVersion.MINECRAFT_1_20_3,
|
||||||
ProtocolVersion.MINECRAFT_1_20_5,
|
ProtocolVersion.MINECRAFT_1_20_5,
|
||||||
ProtocolVersion.MINECRAFT_1_21
|
ProtocolVersion.MINECRAFT_1_21,
|
||||||
|
ProtocolVersion.MINECRAFT_1_21_2
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,7 +52,9 @@ public class Protocol765Adapter extends Protocol404Adapter {
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
protected Component readComponent(@NotNull ByteBuf buf) {
|
protected Component readComponent(@NotNull ByteBuf buf) {
|
||||||
return GsonComponentSerializer.gson().deserializeFromTree(ComponentHolder.deserialize(ProtocolUtils.readBinaryTag(buf, ProtocolVersion.MINECRAFT_1_20_3, null)));
|
return GsonComponentSerializer.gson().deserializeFromTree(ComponentHolder.deserialize(
|
||||||
|
ProtocolUtils.readBinaryTag(buf, ProtocolVersion.MINECRAFT_1_20_3, null)
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -30,18 +30,18 @@ import com.velocitypowered.api.proxy.server.RegisteredServer;
|
|||||||
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
||||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||||
import com.velocitypowered.proxy.protocol.StateRegistry;
|
import com.velocitypowered.proxy.protocol.StateRegistry;
|
||||||
|
import lombok.Getter;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.william278.velocitab.Velocitab;
|
import net.william278.velocitab.Velocitab;
|
||||||
import net.william278.velocitab.config.Group;
|
import net.william278.velocitab.config.Group;
|
||||||
import net.william278.velocitab.player.TabPlayer;
|
import net.william278.velocitab.player.TabPlayer;
|
||||||
|
import net.william278.velocitab.sorting.SortedSet;
|
||||||
import net.william278.velocitab.tab.Nametag;
|
import net.william278.velocitab.tab.Nametag;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.slf4j.event.Level;
|
import org.slf4j.event.Level;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.*;
|
||||||
import java.util.Optional;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import static com.velocitypowered.api.network.ProtocolVersion.*;
|
import static com.velocitypowered.api.network.ProtocolVersion.*;
|
||||||
|
|
||||||
@ -49,29 +49,43 @@ public class ScoreboardManager {
|
|||||||
|
|
||||||
private PacketRegistration<UpdateTeamsPacket> packetRegistration;
|
private PacketRegistration<UpdateTeamsPacket> packetRegistration;
|
||||||
private final Velocitab plugin;
|
private final Velocitab plugin;
|
||||||
private final Set<TeamsPacketAdapter> versions;
|
private final boolean teams;
|
||||||
|
private final Map<ProtocolVersion, TeamsPacketAdapter> versions;
|
||||||
|
@Getter
|
||||||
private final Map<UUID, String> createdTeams;
|
private final Map<UUID, String> createdTeams;
|
||||||
private final Map<String, Nametag> nametags;
|
private final Map<String, Nametag> nametags;
|
||||||
private final Multimap<UUID, String> trackedTeams;
|
private final Multimap<UUID, String> trackedTeams;
|
||||||
|
@Getter
|
||||||
|
private final SortedSet sortedTeams;
|
||||||
|
|
||||||
public ScoreboardManager(@NotNull Velocitab velocitab) {
|
public ScoreboardManager(@NotNull Velocitab velocitab, boolean teams) {
|
||||||
this.plugin = velocitab;
|
this.plugin = velocitab;
|
||||||
|
this.teams = teams;
|
||||||
this.createdTeams = Maps.newConcurrentMap();
|
this.createdTeams = Maps.newConcurrentMap();
|
||||||
this.nametags = Maps.newConcurrentMap();
|
this.nametags = Maps.newConcurrentMap();
|
||||||
this.versions = Sets.newHashSet();
|
this.versions = Maps.newHashMap();
|
||||||
this.trackedTeams = Multimaps.synchronizedMultimap(Multimaps.newSetMultimap(Maps.newConcurrentMap(), Sets::newConcurrentHashSet));
|
this.trackedTeams = Multimaps.synchronizedMultimap(Multimaps.newSetMultimap(Maps.newConcurrentMap(), Sets::newConcurrentHashSet));
|
||||||
|
this.sortedTeams = new SortedSet(Comparator.reverseOrder());
|
||||||
this.registerVersions();
|
this.registerVersions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean handleTeams() {
|
||||||
|
return teams;
|
||||||
|
}
|
||||||
|
|
||||||
private void registerVersions() {
|
private void registerVersions() {
|
||||||
try {
|
try {
|
||||||
versions.add(new Protocol765Adapter(plugin));
|
final Protocol765Adapter protocol765Adapter = new Protocol765Adapter(plugin);
|
||||||
versions.add(new Protocol735Adapter(plugin));
|
protocol765Adapter.getProtocolVersions().forEach(version -> versions.put(version, protocol765Adapter));
|
||||||
versions.add(new Protocol404Adapter(plugin));
|
final Protocol735Adapter protocol735Adapter = new Protocol735Adapter(plugin);
|
||||||
versions.add(new Protocol48Adapter(plugin));
|
protocol735Adapter.getProtocolVersions().forEach(version -> versions.put(version, protocol735Adapter));
|
||||||
|
final Protocol404Adapter protocol404Adapter = new Protocol404Adapter(plugin);
|
||||||
|
protocol404Adapter.getProtocolVersions().forEach(version -> versions.put(version, protocol404Adapter));
|
||||||
|
final Protocol48Adapter protocol48Adapter = new Protocol48Adapter(plugin);
|
||||||
|
protocol48Adapter.getProtocolVersions().forEach(version -> versions.put(version, protocol48Adapter));
|
||||||
} catch (NoSuchFieldError e) {
|
} catch (NoSuchFieldError e) {
|
||||||
throw new IllegalStateException("Failed to register Scoreboard Teams packets." +
|
throw new IllegalStateException("Failed to register Scoreboard Teams packets." +
|
||||||
" Velocitab probably does not (yet) support your Proxy version.", e);
|
" Velocitab probably does not (yet) support your Proxy version.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,11 +93,13 @@ public class ScoreboardManager {
|
|||||||
return nametags.containsKey(teamName);
|
return nametags.containsKey(teamName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getPosition(@NotNull String teamName) {
|
||||||
|
return sortedTeams.getPosition(teamName);
|
||||||
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public TeamsPacketAdapter getPacketAdapter(@NotNull ProtocolVersion version) {
|
public TeamsPacketAdapter getPacketAdapter(@NotNull ProtocolVersion version) {
|
||||||
return versions.stream()
|
return Optional.ofNullable(versions.get(version))
|
||||||
.filter(adapter -> adapter.getProtocolVersions().contains(version))
|
|
||||||
.findFirst()
|
|
||||||
.orElseThrow(() -> new IllegalArgumentException("No adapter found for protocol version " + version));
|
.orElseThrow(() -> new IllegalArgumentException("No adapter found for protocol version " + version));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,6 +110,7 @@ public class ScoreboardManager {
|
|||||||
public void resetCache(@NotNull Player player) {
|
public void resetCache(@NotNull Player player) {
|
||||||
final String team = createdTeams.remove(player.getUniqueId());
|
final String team = createdTeams.remove(player.getUniqueId());
|
||||||
if (team != null) {
|
if (team != null) {
|
||||||
|
removeSortedTeam(team);
|
||||||
plugin.getTabList().getTabPlayer(player).ifPresent(tabPlayer ->
|
plugin.getTabList().getTabPlayer(player).ifPresent(tabPlayer ->
|
||||||
dispatchGroupPacket(UpdateTeamsPacket.removeTeam(plugin, team), tabPlayer)
|
dispatchGroupPacket(UpdateTeamsPacket.removeTeam(plugin, team), tabPlayer)
|
||||||
);
|
);
|
||||||
@ -104,10 +121,18 @@ public class ScoreboardManager {
|
|||||||
public void resetCache(@NotNull Player player, @NotNull Group group) {
|
public void resetCache(@NotNull Player player, @NotNull Group group) {
|
||||||
final String team = createdTeams.remove(player.getUniqueId());
|
final String team = createdTeams.remove(player.getUniqueId());
|
||||||
if (team != null) {
|
if (team != null) {
|
||||||
|
removeSortedTeam(team);
|
||||||
dispatchGroupPacket(UpdateTeamsPacket.removeTeam(plugin, team), group);
|
dispatchGroupPacket(UpdateTeamsPacket.removeTeam(plugin, team), group);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void removeSortedTeam(@NotNull String teamName) {
|
||||||
|
final boolean result = sortedTeams.removeTeam(teamName);
|
||||||
|
if (!result) {
|
||||||
|
plugin.log(Level.ERROR, "Failed to remove team " + teamName + " from sortedTeams");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void vanishPlayer(@NotNull TabPlayer tabPlayer) {
|
public void vanishPlayer(@NotNull TabPlayer tabPlayer) {
|
||||||
this.handleVanish(tabPlayer, true);
|
this.handleVanish(tabPlayer, true);
|
||||||
}
|
}
|
||||||
@ -127,12 +152,13 @@ public class ScoreboardManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final Set<RegisteredServer> siblings = tabPlayer.getGroup().registeredServers(plugin);
|
final Set<RegisteredServer> siblings = tabPlayer.getGroup().registeredServers(plugin);
|
||||||
|
final boolean isNameTagEmpty = tabPlayer.getGroup().nametag().isEmpty();
|
||||||
|
|
||||||
final Optional<Nametag> cachedTag = Optional.ofNullable(nametags.getOrDefault(teamName, null));
|
final Optional<Nametag> cachedTag = Optional.ofNullable(nametags.getOrDefault(teamName, null));
|
||||||
cachedTag.ifPresent(nametag -> siblings.forEach(server -> server.getPlayersConnected().stream().filter(p -> p != player)
|
cachedTag.ifPresent(nametag -> siblings.forEach(server -> server.getPlayersConnected().stream().filter(p -> p != player)
|
||||||
.forEach(connected -> {
|
.forEach(connected -> {
|
||||||
if (vanish && !plugin.getVanishManager().canSee(connected.getUsername(), player.getUsername())) {
|
if (vanish && !plugin.getVanishManager().canSee(connected.getUsername(), player.getUsername())) {
|
||||||
dispatchPacket(UpdateTeamsPacket.removeTeam(plugin, teamName), connected);
|
sendPacket(connected, UpdateTeamsPacket.removeTeam(plugin, teamName), isNameTagEmpty);
|
||||||
trackedTeams.remove(connected.getUniqueId(), teamName);
|
trackedTeams.remove(connected.getUniqueId(), teamName);
|
||||||
} else {
|
} else {
|
||||||
dispatchGroupCreatePacket(plugin, tabPlayer, teamName, nametag, player.getUsername());
|
dispatchGroupCreatePacket(plugin, tabPlayer, teamName, nametag, player.getUsername());
|
||||||
@ -147,14 +173,14 @@ public class ScoreboardManager {
|
|||||||
* @param role The new role of the player. Must not be null.
|
* @param role The new role of the player. Must not be null.
|
||||||
* @param force Whether to force the update even if the player's nametag is the same.
|
* @param force Whether to force the update even if the player's nametag is the same.
|
||||||
*/
|
*/
|
||||||
public void updateRole(@NotNull TabPlayer tabPlayer, @NotNull String role, boolean force) {
|
public CompletableFuture<Void> updateRole(@NotNull TabPlayer tabPlayer, @NotNull String role, boolean force) {
|
||||||
final Player player = tabPlayer.getPlayer();
|
final Player player = tabPlayer.getPlayer();
|
||||||
if (!player.isActive()) {
|
if (!player.isActive()) {
|
||||||
plugin.getTabList().removeOfflinePlayer(player);
|
plugin.getTabList().removeOfflinePlayer(player);
|
||||||
return;
|
return CompletableFuture.completedFuture(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
final String name = player.getUsername();
|
final String name = player.getUsername();
|
||||||
|
final CompletableFuture<Void> future = new CompletableFuture<>();
|
||||||
tabPlayer.getNametag(plugin).thenAccept(newTag -> {
|
tabPlayer.getNametag(plugin).thenAccept(newTag -> {
|
||||||
if (!createdTeams.getOrDefault(player.getUniqueId(), "").equals(role)) {
|
if (!createdTeams.getOrDefault(player.getUniqueId(), "").equals(role)) {
|
||||||
if (createdTeams.containsKey(player.getUniqueId())) {
|
if (createdTeams.containsKey(player.getUniqueId())) {
|
||||||
@ -163,8 +189,15 @@ public class ScoreboardManager {
|
|||||||
tabPlayer
|
tabPlayer
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
final String oldRole = createdTeams.remove(player.getUniqueId());
|
||||||
|
if (oldRole != null) {
|
||||||
|
removeSortedTeam(oldRole);
|
||||||
|
}
|
||||||
createdTeams.put(player.getUniqueId(), role);
|
createdTeams.put(player.getUniqueId(), role);
|
||||||
|
final boolean a = sortedTeams.addTeam(role);
|
||||||
|
if (!a) {
|
||||||
|
plugin.log(Level.ERROR, "Failed to add team " + role + " to sortedTeams");
|
||||||
|
}
|
||||||
this.nametags.put(role, newTag);
|
this.nametags.put(role, newTag);
|
||||||
dispatchGroupCreatePacket(plugin, tabPlayer, role, newTag, name);
|
dispatchGroupCreatePacket(plugin, tabPlayer, role, newTag, name);
|
||||||
} else if (force || (this.nametags.containsKey(role) && !this.nametags.get(role).equals(newTag))) {
|
} else if (force || (this.nametags.containsKey(role) && !this.nametags.get(role).equals(newTag))) {
|
||||||
@ -173,10 +206,13 @@ public class ScoreboardManager {
|
|||||||
} else {
|
} else {
|
||||||
updatePlaceholders(tabPlayer);
|
updatePlaceholders(tabPlayer);
|
||||||
}
|
}
|
||||||
|
future.complete(null);
|
||||||
}).exceptionally(e -> {
|
}).exceptionally(e -> {
|
||||||
plugin.log(Level.ERROR, "Failed to update role for " + player.getUsername(), e);
|
plugin.log(Level.ERROR, "Failed to update role for " + player.getUsername(), e);
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return future;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updatePlaceholders(@NotNull TabPlayer tabPlayer) {
|
public void updatePlaceholders(@NotNull TabPlayer tabPlayer) {
|
||||||
@ -192,6 +228,9 @@ public class ScoreboardManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void resendAllTeams(@NotNull TabPlayer tabPlayer) {
|
public void resendAllTeams(@NotNull TabPlayer tabPlayer) {
|
||||||
|
if (!teams) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!plugin.getSettings().isSendScoreboardPackets()) {
|
if (!plugin.getSettings().isSendScoreboardPackets()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -238,6 +277,9 @@ public class ScoreboardManager {
|
|||||||
private void dispatchGroupCreatePacket(@NotNull Velocitab plugin, @NotNull TabPlayer tabPlayer,
|
private void dispatchGroupCreatePacket(@NotNull Velocitab plugin, @NotNull TabPlayer tabPlayer,
|
||||||
@NotNull String teamName, @NotNull Nametag nametag,
|
@NotNull String teamName, @NotNull Nametag nametag,
|
||||||
@NotNull String... teamMembers) {
|
@NotNull String... teamMembers) {
|
||||||
|
if (!teams) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
tabPlayer.getGroup().getTabPlayers(plugin, tabPlayer).forEach(viewer -> {
|
tabPlayer.getGroup().getTabPlayers(plugin, tabPlayer).forEach(viewer -> {
|
||||||
if (!viewer.getPlayer().isActive()) {
|
if (!viewer.getPlayer().isActive()) {
|
||||||
return;
|
return;
|
||||||
@ -252,6 +294,9 @@ public class ScoreboardManager {
|
|||||||
@NotNull String teamName, @NotNull Nametag nametag,
|
@NotNull String teamName, @NotNull Nametag nametag,
|
||||||
@NotNull TabPlayer viewer,
|
@NotNull TabPlayer viewer,
|
||||||
@NotNull String... teamMembers) {
|
@NotNull String... teamMembers) {
|
||||||
|
if (!teams) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
final boolean canSee = plugin.getVanishManager().canSee(viewer.getPlayer().getUsername(), tabPlayer.getPlayer().getUsername());
|
final boolean canSee = plugin.getVanishManager().canSee(viewer.getPlayer().getUsername(), tabPlayer.getPlayer().getUsername());
|
||||||
if (!canSee) {
|
if (!canSee) {
|
||||||
return;
|
return;
|
||||||
@ -259,12 +304,17 @@ public class ScoreboardManager {
|
|||||||
|
|
||||||
final UpdateTeamsPacket packet = UpdateTeamsPacket.create(plugin, tabPlayer, teamName, nametag, viewer, teamMembers);
|
final UpdateTeamsPacket packet = UpdateTeamsPacket.create(plugin, tabPlayer, teamName, nametag, viewer, teamMembers);
|
||||||
trackedTeams.put(viewer.getPlayer().getUniqueId(), teamName);
|
trackedTeams.put(viewer.getPlayer().getUniqueId(), teamName);
|
||||||
dispatchPacket(packet, viewer.getPlayer());
|
final boolean isNameTagEmpty = tabPlayer.getGroup().nametag().isEmpty();
|
||||||
|
sendPacket(viewer.getPlayer(), packet, isNameTagEmpty);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void dispatchGroupChangePacket(@NotNull Velocitab plugin, @NotNull TabPlayer tabPlayer,
|
private void dispatchGroupChangePacket(@NotNull Velocitab plugin, @NotNull TabPlayer tabPlayer,
|
||||||
@NotNull String teamName,
|
@NotNull String teamName,
|
||||||
@NotNull Nametag nametag) {
|
@NotNull Nametag nametag) {
|
||||||
|
if (!teams) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final boolean isNameTagEmpty = tabPlayer.getGroup().nametag().isEmpty();
|
||||||
tabPlayer.getGroup().getTabPlayers(plugin, tabPlayer).forEach(viewer -> {
|
tabPlayer.getGroup().getTabPlayers(plugin, tabPlayer).forEach(viewer -> {
|
||||||
if (viewer == tabPlayer || !viewer.getPlayer().isActive()) {
|
if (viewer == tabPlayer || !viewer.getPlayer().isActive()) {
|
||||||
return;
|
return;
|
||||||
@ -289,30 +339,20 @@ public class ScoreboardManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
tabPlayer.setRelationalNametag(viewer.getPlayer().getUniqueId(), prefix, suffix);
|
tabPlayer.setRelationalNametag(viewer.getPlayer().getUniqueId(), prefix, suffix);
|
||||||
dispatchPacket(packet, viewer.getPlayer());
|
sendPacket(viewer.getPlayer(), packet, isNameTagEmpty);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void dispatchPacket(@NotNull UpdateTeamsPacket packet, @NotNull Player player) {
|
private void dispatchGroupPacket(@NotNull UpdateTeamsPacket packet, @NotNull Group group) {
|
||||||
if (!player.isActive()) {
|
if (!teams) {
|
||||||
plugin.getTabList().removeOfflinePlayer(player);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
final ConnectedPlayer connectedPlayer = (ConnectedPlayer) player;
|
|
||||||
connectedPlayer.getConnection().write(packet);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
plugin.log(Level.ERROR, "Failed to dispatch packet (unsupported client or server version)", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void dispatchGroupPacket(@NotNull UpdateTeamsPacket packet, @NotNull Group group) {
|
|
||||||
final boolean isRemove = packet.isRemoveTeam();
|
final boolean isRemove = packet.isRemoveTeam();
|
||||||
|
final boolean isNameTagEmpty = group.nametag().isEmpty();
|
||||||
group.registeredServers(plugin).forEach(server -> server.getPlayersConnected().forEach(connected -> {
|
group.registeredServers(plugin).forEach(server -> server.getPlayersConnected().forEach(connected -> {
|
||||||
try {
|
try {
|
||||||
final ConnectedPlayer connectedPlayer = (ConnectedPlayer) connected;
|
sendPacket(connected, packet, isNameTagEmpty);
|
||||||
connectedPlayer.getConnection().write(packet);
|
|
||||||
if (isRemove) {
|
if (isRemove) {
|
||||||
trackedTeams.remove(connected.getUniqueId(), packet.teamName());
|
trackedTeams.remove(connected.getUniqueId(), packet.teamName());
|
||||||
}
|
}
|
||||||
@ -323,6 +363,9 @@ public class ScoreboardManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void dispatchGroupPacket(@NotNull UpdateTeamsPacket packet, @NotNull TabPlayer tabPlayer) {
|
private void dispatchGroupPacket(@NotNull UpdateTeamsPacket packet, @NotNull TabPlayer tabPlayer) {
|
||||||
|
if (!teams) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
final Player player = tabPlayer.getPlayer();
|
final Player player = tabPlayer.getPlayer();
|
||||||
final Optional<ServerConnection> optionalServerConnection = player.getCurrentServer();
|
final Optional<ServerConnection> optionalServerConnection = player.getCurrentServer();
|
||||||
if (optionalServerConnection.isEmpty()) {
|
if (optionalServerConnection.isEmpty()) {
|
||||||
@ -330,6 +373,7 @@ public class ScoreboardManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final Set<Player> players = tabPlayer.getGroup().getPlayers(plugin);
|
final Set<Player> players = tabPlayer.getGroup().getPlayers(plugin);
|
||||||
|
final boolean isNameTagEmpty = tabPlayer.getGroup().nametag().isEmpty();
|
||||||
players.forEach(connected -> {
|
players.forEach(connected -> {
|
||||||
try {
|
try {
|
||||||
final boolean canSee = plugin.getVanishManager().canSee(connected.getUsername(), player.getUsername());
|
final boolean canSee = plugin.getVanishManager().canSee(connected.getUsername(), player.getUsername());
|
||||||
@ -337,15 +381,30 @@ public class ScoreboardManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final ConnectedPlayer connectedPlayer = (ConnectedPlayer) connected;
|
sendPacket(connected, packet, isNameTagEmpty);
|
||||||
connectedPlayer.getConnection().write(packet);
|
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
plugin.log(Level.ERROR, "Failed to dispatch packet (unsupported client or server version)", e);
|
plugin.log(Level.ERROR, "Failed to dispatch packet (unsupported client or server version)", e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void sendPacket(@NotNull Player player, @NotNull UpdateTeamsPacket packet, boolean isNameTagEmpty) {
|
||||||
|
if (!player.isActive()) {
|
||||||
|
plugin.getTabList().removeOfflinePlayer(player);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (player.getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_21_2) && isNameTagEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final ConnectedPlayer connectedPlayer = (ConnectedPlayer) player;
|
||||||
|
connectedPlayer.getConnection().write(packet);
|
||||||
|
}
|
||||||
|
|
||||||
public void registerPacket() {
|
public void registerPacket() {
|
||||||
|
if (!teams) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
packetRegistration = PacketRegistration.of(UpdateTeamsPacket.class)
|
packetRegistration = PacketRegistration.of(UpdateTeamsPacket.class)
|
||||||
.direction(ProtocolUtils.Direction.CLIENTBOUND)
|
.direction(ProtocolUtils.Direction.CLIENTBOUND)
|
||||||
@ -362,7 +421,8 @@ public class ScoreboardManager {
|
|||||||
.mapping(0x5A, MINECRAFT_1_19_4, false)
|
.mapping(0x5A, MINECRAFT_1_19_4, false)
|
||||||
.mapping(0x5C, MINECRAFT_1_20_2, false)
|
.mapping(0x5C, MINECRAFT_1_20_2, false)
|
||||||
.mapping(0x5E, MINECRAFT_1_20_3, false)
|
.mapping(0x5E, MINECRAFT_1_20_3, false)
|
||||||
.mapping(0x60, MINECRAFT_1_20_5, false);
|
.mapping(0x60, MINECRAFT_1_20_5, false)
|
||||||
|
.mapping(0x67, MINECRAFT_1_21_2, false);
|
||||||
packetRegistration.register();
|
packetRegistration.register();
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
plugin.log(Level.ERROR, "Failed to register UpdateTeamsPacket", e);
|
plugin.log(Level.ERROR, "Failed to register UpdateTeamsPacket", e);
|
||||||
@ -389,6 +449,9 @@ public class ScoreboardManager {
|
|||||||
* @param canSee A boolean indicating whether the player can see the target player.
|
* @param canSee A boolean indicating whether the player can see the target player.
|
||||||
*/
|
*/
|
||||||
public void recalculateVanishForPlayer(TabPlayer tabPlayer, TabPlayer target, boolean canSee) {
|
public void recalculateVanishForPlayer(TabPlayer tabPlayer, TabPlayer target, boolean canSee) {
|
||||||
|
if (!teams) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
final Player player = tabPlayer.getPlayer();
|
final Player player = tabPlayer.getPlayer();
|
||||||
final String team = createdTeams.get(target.getPlayer().getUniqueId());
|
final String team = createdTeams.get(target.getPlayer().getUniqueId());
|
||||||
if (team == null) {
|
if (team == null) {
|
||||||
@ -396,7 +459,8 @@ public class ScoreboardManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final UpdateTeamsPacket removeTeam = UpdateTeamsPacket.removeTeam(plugin, team);
|
final UpdateTeamsPacket removeTeam = UpdateTeamsPacket.removeTeam(plugin, team);
|
||||||
dispatchPacket(removeTeam, player);
|
final boolean isNameTagEmpty = tabPlayer.getGroup().nametag().isEmpty();
|
||||||
|
sendPacket(player, removeTeam, isNameTagEmpty);
|
||||||
trackedTeams.remove(player.getUniqueId(), team);
|
trackedTeams.remove(player.getUniqueId(), team);
|
||||||
|
|
||||||
if (canSee) {
|
if (canSee) {
|
||||||
|
@ -36,7 +36,6 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@ -208,22 +207,14 @@ public class UpdateTeamsPacket implements MinecraftPacket {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void decode(ByteBuf byteBuf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
|
public void decode(ByteBuf byteBuf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
|
||||||
final Optional<ScoreboardManager> optionalManager = plugin.getScoreboardManager();
|
final ScoreboardManager scoreboardManager = plugin.getScoreboardManager();
|
||||||
if (optionalManager.isEmpty()) {
|
scoreboardManager.getPacketAdapter(protocolVersion).decode(byteBuf, this, protocolVersion);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
optionalManager.get().getPacketAdapter(protocolVersion).decode(byteBuf, this, protocolVersion);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void encode(ByteBuf byteBuf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
|
public void encode(ByteBuf byteBuf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
|
||||||
final Optional<ScoreboardManager> optionalManager = plugin.getScoreboardManager();
|
final ScoreboardManager scoreboardManager = plugin.getScoreboardManager();
|
||||||
if (optionalManager.isEmpty()) {
|
scoreboardManager.getPacketAdapter(protocolVersion).encode(byteBuf, this, protocolVersion);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
optionalManager.get().getPacketAdapter(protocolVersion).encode(byteBuf, this, protocolVersion);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -36,7 +36,10 @@ import org.apache.commons.lang3.ObjectUtils;
|
|||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
@ -58,10 +61,13 @@ public final class TabPlayer implements Comparable<TabPlayer> {
|
|||||||
private final Map<UUID, Component> relationalDisplayNames;
|
private final Map<UUID, Component> relationalDisplayNames;
|
||||||
private final Map<UUID, Component[]> relationalNametags;
|
private final Map<UUID, Component[]> relationalNametags;
|
||||||
private final Map<String, String> cachedPlaceholders;
|
private final Map<String, String> cachedPlaceholders;
|
||||||
|
private final Map<UUID, Integer> cachedListOrders;
|
||||||
private String lastDisplayName;
|
private String lastDisplayName;
|
||||||
private Component lastHeader;
|
private Component lastHeader;
|
||||||
private Component lastFooter;
|
private Component lastFooter;
|
||||||
private String teamName;
|
private String teamName;
|
||||||
|
@Setter
|
||||||
|
private int listOrder = -1;
|
||||||
@Nullable
|
@Nullable
|
||||||
@Setter
|
@Setter
|
||||||
private UpdateTeamsPacket.TeamColor teamColor;
|
private UpdateTeamsPacket.TeamColor teamColor;
|
||||||
@ -86,6 +92,7 @@ public final class TabPlayer implements Comparable<TabPlayer> {
|
|||||||
this.relationalDisplayNames = Maps.newConcurrentMap();
|
this.relationalDisplayNames = Maps.newConcurrentMap();
|
||||||
this.relationalNametags = Maps.newConcurrentMap();
|
this.relationalNametags = Maps.newConcurrentMap();
|
||||||
this.cachedPlaceholders = Maps.newConcurrentMap();
|
this.cachedPlaceholders = Maps.newConcurrentMap();
|
||||||
|
this.cachedListOrders = Maps.newConcurrentMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@ -237,6 +244,10 @@ public final class TabPlayer implements Comparable<TabPlayer> {
|
|||||||
relationalNametags.remove(target);
|
relationalNametags.remove(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void unsetTabListOrder(@NotNull UUID target) {
|
||||||
|
cachedListOrders.remove(target);
|
||||||
|
}
|
||||||
|
|
||||||
public Optional<Component[]> getRelationalNametag(@NotNull UUID target) {
|
public Optional<Component[]> getRelationalNametag(@NotNull UUID target) {
|
||||||
return Optional.ofNullable(relationalNametags.get(target));
|
return Optional.ofNullable(relationalNametags.get(target));
|
||||||
}
|
}
|
||||||
@ -248,6 +259,8 @@ public final class TabPlayer implements Comparable<TabPlayer> {
|
|||||||
lastHeader = null;
|
lastHeader = null;
|
||||||
lastFooter = null;
|
lastFooter = null;
|
||||||
role = Role.DEFAULT_ROLE;
|
role = Role.DEFAULT_ROLE;
|
||||||
|
teamName = null;
|
||||||
|
cachedListOrders.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -30,6 +30,7 @@ public interface LoggerProvider {
|
|||||||
*
|
*
|
||||||
* @return the logger for the class
|
* @return the logger for the class
|
||||||
*/
|
*/
|
||||||
|
@NotNull
|
||||||
Logger getLogger();
|
Logger getLogger();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -36,7 +36,8 @@ public interface MetricProvider {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the Velocitab plugin instance.
|
* Retrieves the Velocitab plugin instance.
|
||||||
* @return
|
*
|
||||||
|
* @return The Velocitab plugin instance.
|
||||||
*/
|
*/
|
||||||
Velocitab getPlugin();
|
Velocitab getPlugin();
|
||||||
|
|
||||||
|
@ -24,7 +24,6 @@ import net.william278.velocitab.packet.ScoreboardManager;
|
|||||||
import net.william278.velocitab.sorting.SortingManager;
|
import net.william278.velocitab.sorting.SortingManager;
|
||||||
import net.william278.velocitab.tab.PlayerTabList;
|
import net.william278.velocitab.tab.PlayerTabList;
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public interface ScoreboardProvider {
|
public interface ScoreboardProvider {
|
||||||
@ -37,11 +36,10 @@ public interface ScoreboardProvider {
|
|||||||
Velocitab getPlugin();
|
Velocitab getPlugin();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the optional scoreboard manager.
|
* Retrieves the scoreboard manager
|
||||||
*
|
* @return The scoreboard manager
|
||||||
* @return An {@code Optional} object that may contain a {@code ScoreboardManager} instance.
|
|
||||||
*/
|
*/
|
||||||
Optional<ScoreboardManager> getScoreboardManager();
|
ScoreboardManager getScoreboardManager();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the scoreboard manager.
|
* Sets the scoreboard manager.
|
||||||
@ -85,11 +83,9 @@ public interface ScoreboardProvider {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
default void prepareScoreboard() {
|
default void prepareScoreboard() {
|
||||||
if (getPlugin().getSettings().isSendScoreboardPackets()) {
|
final ScoreboardManager scoreboardManager = new ScoreboardManager(getPlugin(), getPlugin().getSettings().isSendScoreboardPackets());
|
||||||
final ScoreboardManager scoreboardManager = new ScoreboardManager(getPlugin());
|
setScoreboardManager(scoreboardManager);
|
||||||
setScoreboardManager(scoreboardManager);
|
scoreboardManager.registerPacket();
|
||||||
scoreboardManager.registerPacket();
|
|
||||||
}
|
|
||||||
|
|
||||||
final PlayerTabList tabList = new PlayerTabList(getPlugin());
|
final PlayerTabList tabList = new PlayerTabList(getPlugin());
|
||||||
setTabList(tabList);
|
setTabList(tabList);
|
||||||
@ -105,10 +101,8 @@ public interface ScoreboardProvider {
|
|||||||
* Disables the ScoreboardManager and closes the tab list for the player.
|
* Disables the ScoreboardManager and closes the tab list for the player.
|
||||||
*/
|
*/
|
||||||
default void disableScoreboardManager() {
|
default void disableScoreboardManager() {
|
||||||
if (getScoreboardManager().isPresent() && getPlugin().getSettings().isSendScoreboardPackets()) {
|
getScoreboardManager().close();
|
||||||
getScoreboardManager().get().close();
|
getScoreboardManager().unregisterPacket();
|
||||||
getScoreboardManager().get().unregisterPacket();
|
|
||||||
}
|
|
||||||
|
|
||||||
getTabList().close();
|
getTabList().close();
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of Velocitab, licensed under the Apache License 2.0.
|
||||||
|
*
|
||||||
|
* Copyright (c) William278 <will27528@gmail.com>
|
||||||
|
* Copyright (c) contributors
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* http://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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.william278.velocitab.sorting;
|
||||||
|
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentSkipListSet;
|
||||||
|
|
||||||
|
public class SortedSet {
|
||||||
|
|
||||||
|
private final ConcurrentSkipListSet<String> sortedTeams;
|
||||||
|
private final Map<String, Integer> positionMap;
|
||||||
|
|
||||||
|
public SortedSet(@NotNull Comparator<String> comparator) {
|
||||||
|
sortedTeams = new ConcurrentSkipListSet<>(comparator);
|
||||||
|
positionMap = Maps.newConcurrentMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized boolean addTeam(@NotNull String teamName) {
|
||||||
|
final boolean result = sortedTeams.add(teamName);
|
||||||
|
if (!result) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
updatePositions();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized boolean removeTeam(@NotNull String teamName) {
|
||||||
|
final boolean result = sortedTeams.remove(teamName);
|
||||||
|
if (!result) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
updatePositions();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void updatePositions() {
|
||||||
|
int index = 0;
|
||||||
|
positionMap.clear();
|
||||||
|
for (final String team : sortedTeams) {
|
||||||
|
positionMap.put(team, index);
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized int getPosition(@NotNull String teamName) {
|
||||||
|
return positionMap.getOrDefault(teamName, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return sortedTeams.toString();
|
||||||
|
}
|
||||||
|
}
|
@ -22,7 +22,8 @@ package net.william278.velocitab.tab;
|
|||||||
import com.velocitypowered.api.scheduler.ScheduledTask;
|
import com.velocitypowered.api.scheduler.ScheduledTask;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
public record GroupTasks(@Nullable ScheduledTask updateTask, @Nullable ScheduledTask headerFooterTask, @Nullable ScheduledTask latencyTask) {
|
public record GroupTasks(@Nullable ScheduledTask updateTask, @Nullable ScheduledTask headerFooterTask,
|
||||||
|
@Nullable ScheduledTask latencyTask) {
|
||||||
|
|
||||||
public void cancel() {
|
public void cancel() {
|
||||||
if (updateTask != null) {
|
if (updateTask != null) {
|
||||||
|
@ -50,4 +50,8 @@ public record Nametag(@NotNull String prefix, @NotNull String suffix) {
|
|||||||
return (prefix.equals(other.prefix)) && (suffix.equals(other.suffix));
|
return (prefix.equals(other.prefix)) && (suffix.equals(other.suffix));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return prefix.isEmpty() && suffix.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,8 @@ import com.velocitypowered.api.proxy.ServerConnection;
|
|||||||
import com.velocitypowered.api.proxy.player.TabList;
|
import com.velocitypowered.api.proxy.player.TabList;
|
||||||
import com.velocitypowered.api.proxy.player.TabListEntry;
|
import com.velocitypowered.api.proxy.player.TabListEntry;
|
||||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||||
|
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
||||||
|
import com.velocitypowered.proxy.protocol.packet.UpsertPlayerInfoPacket;
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
@ -34,6 +36,7 @@ import net.william278.velocitab.api.PlayerAddedToTabEvent;
|
|||||||
import net.william278.velocitab.config.Group;
|
import net.william278.velocitab.config.Group;
|
||||||
import net.william278.velocitab.config.Placeholder;
|
import net.william278.velocitab.config.Placeholder;
|
||||||
import net.william278.velocitab.config.ServerUrl;
|
import net.william278.velocitab.config.ServerUrl;
|
||||||
|
import net.william278.velocitab.packet.ScoreboardManager;
|
||||||
import net.william278.velocitab.player.Role;
|
import net.william278.velocitab.player.Role;
|
||||||
import net.william278.velocitab.player.TabPlayer;
|
import net.william278.velocitab.player.TabPlayer;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@ -46,10 +49,13 @@ import java.util.concurrent.CompletableFuture;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static com.velocitypowered.proxy.protocol.packet.UpsertPlayerInfoPacket.Action.UPDATE_LIST_ORDER;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The main class for tracking the server TAB list
|
* The main class for tracking the server TAB list for a map of {@link TabPlayer}s
|
||||||
*/
|
*/
|
||||||
public class PlayerTabList {
|
public class PlayerTabList {
|
||||||
|
|
||||||
private final Velocitab plugin;
|
private final Velocitab plugin;
|
||||||
@Getter
|
@Getter
|
||||||
private final VanishTabList vanishTabList;
|
private final VanishTabList vanishTabList;
|
||||||
@ -141,6 +147,7 @@ public class PlayerTabList {
|
|||||||
players.values().forEach(p -> {
|
players.values().forEach(p -> {
|
||||||
p.unsetRelationalDisplayName(player.getUniqueId());
|
p.unsetRelationalDisplayName(player.getUniqueId());
|
||||||
p.unsetRelationalNametag(player.getUniqueId());
|
p.unsetRelationalNametag(player.getUniqueId());
|
||||||
|
p.unsetTabListOrder(player.getUniqueId());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,10 +227,9 @@ public class PlayerTabList {
|
|||||||
}
|
}
|
||||||
iteratedPlayer.sendHeaderAndFooter(this);
|
iteratedPlayer.sendHeaderAndFooter(this);
|
||||||
}
|
}
|
||||||
plugin.getScoreboardManager().ifPresent(s -> {
|
final ScoreboardManager scoreboardManager = plugin.getScoreboardManager();
|
||||||
s.resendAllTeams(tabPlayer);
|
scoreboardManager.resendAllTeams(tabPlayer);
|
||||||
tabPlayer.getTeamName(plugin).thenAccept(t -> s.updateRole(tabPlayer, t, false));
|
updateSorting(tabPlayer, false);
|
||||||
});
|
|
||||||
fixDuplicateEntries(joined);
|
fixDuplicateEntries(joined);
|
||||||
// Fire event without listening for result
|
// Fire event without listening for result
|
||||||
plugin.getServer().getEventManager().fireAndForget(new PlayerAddedToTabEvent(tabPlayer, group));
|
plugin.getServer().getEventManager().fireAndForget(new PlayerAddedToTabEvent(tabPlayer, group));
|
||||||
@ -278,7 +284,7 @@ public class PlayerTabList {
|
|||||||
.filter(entry -> !entry.getKey().equals(target.getUniqueId()))
|
.filter(entry -> !entry.getKey().equals(target.getUniqueId()))
|
||||||
.forEach(entry -> target.getTabList().removeEntry(entry.getKey()));
|
.forEach(entry -> target.getTabList().removeEntry(entry.getKey()));
|
||||||
} catch (Throwable error) {
|
} catch (Throwable error) {
|
||||||
plugin.log(Level.ERROR, "Failed to fix duplicate entries for class " + target.getTabList().getClass().getName() , error);
|
plugin.log(Level.ERROR, "Failed to fix duplicate entries for class " + target.getTabList().getClass().getName(), error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,12 +294,13 @@ public class PlayerTabList {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove a player from the tab list
|
* Remove a player from the tab list
|
||||||
* @param uuid
|
*
|
||||||
|
* @param uuid {@link UUID} of the {@link TabPlayer player} to remove
|
||||||
*/
|
*/
|
||||||
protected void removeTablistUUID(@NotNull UUID uuid) {
|
protected void removeTabListUUID(@NotNull UUID uuid) {
|
||||||
getPlayers().forEach((key, value) -> {
|
getPlayers().forEach((key, value) -> value.getPlayer().getTabList().getEntry(uuid).ifPresent(
|
||||||
value.getPlayer().getTabList().getEntry(uuid).ifPresent(entry -> value.getPlayer().getTabList().removeEntry(uuid));
|
entry -> value.getPlayer().getTabList().removeEntry(uuid)
|
||||||
});
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void removePlayer(@NotNull Player target, @Nullable RegisteredServer server) {
|
protected void removePlayer(@NotNull Player target, @Nullable RegisteredServer server) {
|
||||||
@ -318,7 +325,7 @@ public class PlayerTabList {
|
|||||||
.delay(250, TimeUnit.MILLISECONDS)
|
.delay(250, TimeUnit.MILLISECONDS)
|
||||||
.schedule();
|
.schedule();
|
||||||
// Delete player team
|
// Delete player team
|
||||||
plugin.getScoreboardManager().ifPresent(manager -> manager.resetCache(target));
|
plugin.getScoreboardManager().resetCache(target);
|
||||||
//remove player from tab list cache
|
//remove player from tab list cache
|
||||||
getPlayers().remove(uuid);
|
getPlayers().remove(uuid);
|
||||||
}
|
}
|
||||||
@ -389,13 +396,25 @@ public class PlayerTabList {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateSorting(tabPlayer, force);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateSorting(@NotNull TabPlayer tabPlayer, boolean force) {
|
||||||
tabPlayer.getTeamName(plugin).thenAccept(teamName -> {
|
tabPlayer.getTeamName(plugin).thenAccept(teamName -> {
|
||||||
if (teamName.isBlank()) {
|
if (teamName.isBlank()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
plugin.getScoreboardManager().ifPresent(manager -> manager.updateRole(
|
plugin.getScoreboardManager().updateRole(tabPlayer, teamName, force).thenAccept(v -> {
|
||||||
tabPlayer, teamName, force
|
final int order = plugin.getScoreboardManager().getPosition(teamName);
|
||||||
));
|
if (order == -1) {
|
||||||
|
plugin.log(Level.ERROR, "Failed to get position for " + tabPlayer.getPlayer().getUsername());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tabPlayer.setListOrder(order);
|
||||||
|
final Set<TabPlayer> players = tabPlayer.getGroup().getTabPlayers(plugin, tabPlayer);
|
||||||
|
players.forEach(p -> recalculateSortingForPlayer(p, players));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -533,4 +552,44 @@ public class PlayerTabList {
|
|||||||
players.remove(player.getUniqueId());
|
players.remove(player.getUniqueId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the player can use server-side specified TAB list ordering (Minecraft 1.21.2+)
|
||||||
|
*
|
||||||
|
* @param tabPlayer player to check
|
||||||
|
* @return {@code true} if the user is on Minecraft 1.21.2+; {@code false}
|
||||||
|
*/
|
||||||
|
private boolean hasListOrder(@NotNull TabPlayer tabPlayer) {
|
||||||
|
return tabPlayer.getPlayer().getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_21_2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateSorting(@NotNull TabPlayer tabPlayer, @NotNull UUID uuid, int position) {
|
||||||
|
if (!tabPlayer.getPlayer().getTabList().containsEntry(uuid)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (tabPlayer.getCachedListOrders().containsKey(uuid) && tabPlayer.getCachedListOrders().get(uuid) == position) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tabPlayer.getCachedListOrders().put(uuid, position);
|
||||||
|
final UpsertPlayerInfoPacket packet = new UpsertPlayerInfoPacket(UPDATE_LIST_ORDER);
|
||||||
|
final UpsertPlayerInfoPacket.Entry entry = new UpsertPlayerInfoPacket.Entry(uuid);
|
||||||
|
entry.setListOrder(position);
|
||||||
|
packet.addEntry(entry);
|
||||||
|
((ConnectedPlayer) tabPlayer.getPlayer()).getConnection().write(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getPlayerName(UUID uuid) {
|
||||||
|
return plugin.getServer().getPlayer(uuid).map(Player::getUsername).orElse("Unknown");
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void recalculateSortingForPlayer(@NotNull TabPlayer tabPlayer, @NotNull Set<TabPlayer> players) {
|
||||||
|
if (!hasListOrder(tabPlayer)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
players.forEach(p -> {
|
||||||
|
final int order = p.getListOrder();
|
||||||
|
updateSorting(tabPlayer, p.getPlayer().getUniqueId(), order);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,8 @@ public class TabListListener {
|
|||||||
|
|
||||||
private final Velocitab plugin;
|
private final Velocitab plugin;
|
||||||
private final PlayerTabList tabList;
|
private final PlayerTabList tabList;
|
||||||
// In 1.8 there is a packet delay problem
|
|
||||||
|
// Set of UUIDs of users who just left the game - fixes packet delay problem on Minecraft 1.8.x
|
||||||
private final Set<UUID> justQuit;
|
private final Set<UUID> justQuit;
|
||||||
|
|
||||||
public TabListListener(@NotNull Velocitab plugin, @NotNull PlayerTabList tabList) {
|
public TabListListener(@NotNull Velocitab plugin, @NotNull PlayerTabList tabList) {
|
||||||
@ -102,7 +103,7 @@ public class TabListListener {
|
|||||||
plugin.getTabList().clearCachedData(joined);
|
plugin.getTabList().clearCachedData(joined);
|
||||||
|
|
||||||
if (!plugin.getSettings().isShowAllPlayersFromAllGroups() && previousGroup.isPresent()
|
if (!plugin.getSettings().isShowAllPlayersFromAllGroups() && previousGroup.isPresent()
|
||||||
&& (groupOptional.isPresent() && !previousGroup.get().equals(groupOptional.get())
|
&& (groupOptional.isPresent() && !previousGroup.get().equals(groupOptional.get())
|
||||||
|| groupOptional.isEmpty())) {
|
|| groupOptional.isEmpty())) {
|
||||||
tabList.removeOldEntry(previousGroup.get(), joined.getUniqueId());
|
tabList.removeOldEntry(previousGroup.get(), joined.getUniqueId());
|
||||||
}
|
}
|
||||||
@ -126,7 +127,7 @@ public class TabListListener {
|
|||||||
final Component currentHeader = joined.getPlayerListHeader();
|
final Component currentHeader = joined.getPlayerListHeader();
|
||||||
final Component currentFooter = joined.getPlayerListFooter();
|
final Component currentFooter = joined.getPlayerListFooter();
|
||||||
if ((header.equals(currentHeader) && footer.equals(currentFooter)) ||
|
if ((header.equals(currentHeader) && footer.equals(currentFooter)) ||
|
||||||
(currentHeader.equals(Component.empty()) && currentFooter.equals(Component.empty()))
|
(currentHeader.equals(Component.empty()) && currentFooter.equals(Component.empty()))
|
||||||
) {
|
) {
|
||||||
joined.sendPlayerListHeaderAndFooter(Component.empty(), Component.empty());
|
joined.sendPlayerListHeaderAndFooter(Component.empty(), Component.empty());
|
||||||
joined.getCurrentServer().ifPresent(serverConnection -> serverConnection.getServer().getPlayersConnected().forEach(player ->
|
joined.getCurrentServer().ifPresent(serverConnection -> serverConnection.getServer().getPlayersConnected().forEach(player ->
|
||||||
@ -144,7 +145,7 @@ public class TabListListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final Group group = groupOptional.get();
|
final Group group = groupOptional.get();
|
||||||
plugin.getScoreboardManager().ifPresent(manager -> manager.resetCache(joined, group));
|
plugin.getScoreboardManager().resetCache(joined, group);
|
||||||
if (justQuit.contains(joined.getUniqueId())) {
|
if (justQuit.contains(joined.getUniqueId())) {
|
||||||
plugin.getServer().getScheduler().buildTask(plugin,
|
plugin.getServer().getScheduler().buildTask(plugin,
|
||||||
() -> tabList.joinPlayer(joined, group))
|
() -> tabList.joinPlayer(joined, group))
|
||||||
@ -156,7 +157,8 @@ public class TabListListener {
|
|||||||
tabList.joinPlayer(joined, group);
|
tabList.joinPlayer(joined, group);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subscribe(order = PostOrder.LAST)
|
@SuppressWarnings("deprecation")
|
||||||
|
@Subscribe(order = PostOrder.CUSTOM, priority = Short.MIN_VALUE)
|
||||||
public void onPlayerQuit(@NotNull DisconnectEvent event) {
|
public void onPlayerQuit(@NotNull DisconnectEvent event) {
|
||||||
if (event.getLoginStatus() != DisconnectEvent.LoginStatus.SUCCESSFUL_LOGIN) {
|
if (event.getLoginStatus() != DisconnectEvent.LoginStatus.SUCCESSFUL_LOGIN) {
|
||||||
checkDelayedDisconnect(event);
|
checkDelayedDisconnect(event);
|
||||||
@ -178,7 +180,7 @@ public class TabListListener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
tabList.removeTablistUUID(event.getPlayer().getUniqueId());
|
tabList.removeTabListUUID(event.getPlayer().getUniqueId());
|
||||||
}).delay(750, TimeUnit.MILLISECONDS).schedule();
|
}).delay(750, TimeUnit.MILLISECONDS).schedule();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,21 +89,21 @@ public class VanishTabList {
|
|||||||
final String serverName = target.getServerName();
|
final String serverName = target.getServerName();
|
||||||
|
|
||||||
if (tabPlayer.getGroup().onlyListPlayersInSameServer()
|
if (tabPlayer.getGroup().onlyListPlayersInSameServer()
|
||||||
&& !tabPlayer.getServerName().equals(serverName)) {
|
&& !tabPlayer.getServerName().equals(serverName)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final boolean canSee = !plugin.getVanishManager().isVanished(p.getUsername()) ||
|
final boolean canSee = !plugin.getVanishManager().isVanished(p.getUsername()) ||
|
||||||
plugin.getVanishManager().canSee(player.getUsername(), p.getUsername());
|
plugin.getVanishManager().canSee(player.getUsername(), p.getUsername());
|
||||||
|
|
||||||
if (!canSee) {
|
if (!canSee) {
|
||||||
player.getTabList().removeEntry(p.getUniqueId());
|
player.getTabList().removeEntry(p.getUniqueId());
|
||||||
plugin.getScoreboardManager().ifPresent(s -> s.recalculateVanishForPlayer(tabPlayer, target, false));
|
plugin.getScoreboardManager().recalculateVanishForPlayer(tabPlayer, target, false);
|
||||||
} else {
|
} else {
|
||||||
if (!player.getTabList().containsEntry(p.getUniqueId())) {
|
if (!player.getTabList().containsEntry(p.getUniqueId())) {
|
||||||
final TabListEntry tabListEntry = tabList.createEntry(target, player.getTabList(), tabPlayer);
|
final TabListEntry tabListEntry = tabList.createEntry(target, player.getTabList(), tabPlayer);
|
||||||
player.getTabList().addEntry(tabListEntry);
|
player.getTabList().addEntry(tabListEntry);
|
||||||
plugin.getScoreboardManager().ifPresent(s -> s.recalculateVanishForPlayer(tabPlayer, target, true));
|
plugin.getScoreboardManager().recalculateVanishForPlayer(tabPlayer, target, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -32,4 +32,5 @@ public interface QuadFunction<T, U, V, S, R> {
|
|||||||
* @return the function result
|
* @return the function result
|
||||||
*/
|
*/
|
||||||
R apply(T t, U u, V v, S s);
|
R apply(T t, U u, V v, S s);
|
||||||
|
|
||||||
}
|
}
|
@ -21,7 +21,7 @@ package net.william278.velocitab.util;
|
|||||||
|
|
||||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||||
|
|
||||||
public final class SerializerUtil {
|
public final class SerializationUtil {
|
||||||
|
|
||||||
public final static LegacyComponentSerializer LEGACY_SERIALIZER = LegacyComponentSerializer.builder()
|
public final static LegacyComponentSerializer LEGACY_SERIALIZER = LegacyComponentSerializer.builder()
|
||||||
.hexCharacter('#')
|
.hexCharacter('#')
|
@ -61,7 +61,7 @@ public class VanishManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
plugin.getTabList().getVanishTabList().vanishPlayer(tabPlayer.get());
|
plugin.getTabList().getVanishTabList().vanishPlayer(tabPlayer.get());
|
||||||
plugin.getScoreboardManager().ifPresent(scoreboardManager -> scoreboardManager.vanishPlayer(tabPlayer.get()));
|
plugin.getScoreboardManager().vanishPlayer(tabPlayer.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unVanishPlayer(@NotNull Player player) {
|
public void unVanishPlayer(@NotNull Player player) {
|
||||||
@ -72,6 +72,6 @@ public class VanishManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
plugin.getTabList().getVanishTabList().unVanishPlayer(tabPlayer.get());
|
plugin.getTabList().getVanishTabList().unVanishPlayer(tabPlayer.get());
|
||||||
plugin.getScoreboardManager().ifPresent(scoreboardManager -> scoreboardManager.unVanishPlayer(tabPlayer.get()));
|
plugin.getScoreboardManager().unVanishPlayer(tabPlayer.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
velocity_api_version: '${velocity_api_version}'
|
velocity_api_version: '${velocity_api_version}'
|
||||||
velocity_minimum_build: ${velocity_minimum_build}
|
velocity_minimum_build: ${velocity_minimum_build}
|
||||||
|
papi_proxy_bridge_minimum_version: '${papi_proxy_bridge_minimum_version}'
|
Loading…
Reference in New Issue
Block a user